#!/usr/bin/perl # $Id$ # Copyright (C) 1999-2003 MandrakeSoft # Daouda Lo # Damien Krotkine # Thierry Vignaud # Yves Duret # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2, or (at your option) any later version. # # This program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. use strict; use diagnostics; use lib qw(/usr/lib/libDrakX); use standalone; use common; use detect_devices; use lang; use network::netconnect; # for profiles # i18n: IMPORTANT: to get correct namespace (drakconf instead of libDrakX) BEGIN { unshift @::textdomains, 'drakconf' } use ugtk2 qw(:create :dialogs :helpers :wrappers); #------------------------------------------------------------- # paths my ($bindir, $sbindir, $xbindir) = ("/usr/bin", "/usr/sbin", "/usr/X11R6/bin"); my $mcc_dir = "/usr/share/mcc"; my $themes_dir = "$mcc_dir/themes/"; my ($version, $conffile, $class_install) = ("9.2", "/etc/mcc.conf", "/etc/sysconfig/system"); require_root_capability(); # just to get root capabilities my ($default_heigth, $default_width) = (523, 720); #------------------------------------------------------------- # read configuration, set themes, ... my %h = getVarsFromSh($conffile); my %class = getVarsFromSh($class_install); $h{THEME} ||= 'default'; $h{EMBEDDED} ||= bool2text(1); $h{LOGS} ||= bool2text($class{CLASS} eq 'expert' ? 1 : 0); $h{EXPERT_WIZARD} ||= 0; $h{HEIGTH} ||= $default_heigth; $h{WIDTH} ||= $default_width; my %option_values; @option_values{qw(embedded_mode show_log wiz_expert)} = (text2bool($h{EMBEDDED}), text2bool($h{LOGS}), text2bool($h{EXPERT_WIZARD})); my $theme = $h{THEME}; $theme = $1 if "@ARGV" =~ /--theme (\w+)/; -d "$themes_dir/$theme" or $theme = 'default'; add_icon_path("$themes_dir/$theme/"); add_icon_path("$themes_dir/default") if $theme ne 'default'; # fall back if theme miss some icons my $rc = find { -r $_ } ("$themes_dir/$theme/gtkrc", if_($theme ne 'default', "$themes_dir/default/gtkrc")); Gtk2::Rc->parse($rc) if -r $rc; #------------------------------------------------------------- # Splash window: please wait ... my $window_splash = Gtk2::Window->new('popup'); $window_splash->signal_connect(delete_event => \&quit_global); $window_splash->set_title(N("Mandrake Control Center") . $version); $window_splash->set_position('center_always'); $window_splash->add(gtkadd(gtkset_shadow_type(Gtk2::Frame->new, 'etched_out'), gtkpack(Gtk2::VBox->new(0, 0), if_(-r "$themes_dir/$theme/splash_screen.png", gtkcreate_img("splash_screen")), Gtk2::Label->new(N("Loading... Please wait")) ) ) ); $window_splash->show_all; gtkflush(); #------------------------------------------------------------- my $still_not_in_a_section = 1; #------------------------------------------------------------- # Data structures $::isWiz = -e "/usr/sbin/drakwizard"; $::isRpmDrake = -e "/usr/sbin/rpmdrake"; $::isWebAdmin = -e "/usr/bin/mdkwebadmin"; # { key => [ log_exp, binary, gtkplug?, description ] } # { key => [ log_exp, [ binary, win_nb ], gtkplug?, description ] } # gtkplug meaning: -1 => not embedded, 0 => external x11 app, 1 => proper embedding my $exec_hash = { "Auto Install" => [ "drakautoinst", "$sbindir/drakautoinst", 1, N("Auto Install floppy") ], "Backups" => [ "drakbackup", "$sbindir/drakbackup", 1, N("Backups") ], "Boot Config" => [ "drakboot", "$sbindir/drakboot", 1, N("Bootstrapping") ], "Boot Disk" => [ "drakfloppy", "$sbindir/drakfloppy", 1, N("Boot floppy") ], "Connection Sharing" => [ "drakgw", "$sbindir/drakgw", 1, N("Internet connection sharing") ], "Add Connection" => [ "drakconnect", "$sbindir/drakconnect --wizard", 1, N("New connection") ], "Manage Connection" => [ "drakconnect", "$sbindir/drakconnect --skip-wizard", 1, N("Manage connections") ], "Monitor Connection" => [ "net_monitor", "$sbindir/net_monitor", 1, N("Monitor connections") ], "Configure Internet" => [ "drakconnect", "$sbindir/drakconnect --internet", 1, N("Internet access") ], # little workaround to avoid drakconf freeze "Console" => [ "rxvt", "$xbindir/rxvt", -1, N("Console") ], #The Console will help you to solve issues # "Console" => [ "rxvt", [ "$xbindir/rxvt", "rxvt", 1 ], 0, N("Console") ], #The Console will help you to solve issues "Date & Time" => [ "clock", "$sbindir/clock.pl", 1, N("Date and time") ], "Display Manager chooser" => [ "drakedm", "$sbindir/drakedm", 1, N("Display manager") ], "Firewall" => [ "drakfirewall", "$sbindir/drakfirewall", 1, N("Firewall") ], "Fonts" => [ "drakfont", "$sbindir/drakfont", 1, N("Fonts") ], "Graphical server configuration" => [ "XFdrake", "$sbindir/XFdrake", 1, N("Graphical server") ], "Hard Drives" => [ "diskdrake", "$sbindir/diskdrake --hd", 1, N("Partitions") ], "Hardware List" => [ "harddrake", "$sbindir/harddrake2", 1, N("Hardware") ], "Install Software" => [ "rpmdrake", "$sbindir/rpmdrake", -1, N("Install") ], "Keyboard" => [ "keyboarddrake", "$sbindir/keyboarddrake", 1, N("Keyboard") ], "Logs" => [ "logdrake", "$sbindir/logdrake", 1, N("Logs") ], "Mandrake Update" => [ "rpmdrake", "$sbindir/MandrakeUpdate", -1, N("Updates") ], "Menus" => [ "menudrake", "$bindir/menudrake", -1, N("Menus"), "$bindir/menudrake" ], "Monitor" => [ "XFdrake", "$sbindir/XFdrake monitor", 1, N("Monitor") ], "Mouse" => [ "mousedrake", "$sbindir/mousedrake", 1, N("Mouse") ], "NFS mount points" => [ "diskdrake", "$sbindir/diskdrake --nfs", 1, N("NFS mount points") ], "Partition Sharing" => [ "diskdrake", "$sbindir/diskdrake --fileshare", 1, N("Local disk sharing") ], "Printer" => [ "printerdrake", "$sbindir/printerdrake", -1, N("Printers"), "$sbindir/printerdrake" ], "Programs scheduling" => [ "drakcronat", "/usr/X11R6/bin/drakcronat", 1, N("Scheduled tasks") ], #DrakCronAt enables to schedule Programs execution through crond and atd daemons "Proxy Configuration" => [ "drakproxy", "$sbindir/drakproxy", 1, N("Proxy") ], #for files and web browsing "Remove Interface" => [ "drakconnect", "$sbindir/drakconnect --del", 1, N("Remove a connection") ], "Remove Software" => [ "rpmdrake", "$sbindir/rpmdrake-remove", -1, N("Remove") ], "Resolution" => [ "XFdrake", "$sbindir/XFdrake resolution", 1, N("Screen esolution") ], "Samba mount points" => [ "diskdrake", "$sbindir/diskdrake --smb", 1, N("Samba mount points") ], "Scanner" => [ "scannerdrake", "$sbindir/scannerdrake", 1, N("Scanners") ], "Security Level" => [ "draksec", "$sbindir/draksec", 1, N("Level and checks") ], "Security Permissions" => [ "drakperm", "$sbindir/drakperm", 1, N("Permissions") ], "Services" => [ "drakxservices", "$sbindir/drakxservices", 1, N("Services") ], "Software Media Manager" => [ "rpmdrake", "$sbindir/edit-urpm-sources.pl", -1, N("Media Manager") ], "TV Cards" => [ "drakxtv", "$sbindir/drakxtv", 1, N("TV card") ], "Users" => [ "userdrake", "$bindir/userdrake", -1, N("Users and groups") ], # too big "WebDAV mount points" => [ "diskdrake", "$sbindir/diskdrake --dav", 1, N("WebDAV mount points") ], }; # [ [ class_label, class icon name, [ [ program_label, program icon name ] ... ] ] ] my @tree = ([ N("Boot"), 'boot-mdk', [ if_(detect_devices::floppies, [ "Boot Disk", 'drakfloppy-mdk', ]), [ "Boot Config", 'drakboot-mdk', ], [ "Auto Install", 'drakautoinst-mdk', ], ] ], [ N("Hardware"), 'drakhard-mdk', [ [ "Hardware List", 'harddrake-mdk', ], [ "Monitor", 'configure-monitor-mdk', ], [ "Resolution", 'resolution-mdk', ], [ "Graphical server configuration", 'XFdrake-mdk', ], [ "TV Cards", 'tv-mdk', ], [ "Keyboard", 'keyboard-mdk', ], [ "Mouse", 'mousedrake-mdk', ], [ "Printer", 'printer-mcc-mdk', ], [ "Scanner", 'scanner-mdk', ], ] ], [ N("Mount Points"), 'partition-mdk', [ [ "Hard Drives", 'diskdrake_hd', ], (map { my ($type, $name, $scan, $text) = @$_; map_index { my $full_name = $name . ($::i ? $::i + 1 : ''); $exec_hash->{$full_name} = [ "diskdrake", "$sbindir/diskdrake --removable=$_->{device}", 1, $text ]; [ $full_name, "diskdrake_$type" ]; } $scan->(); } do { my %cdroms_by_type; foreach (detect_devices::cdroms()) { my $type = detect_devices::isBurner($_) ? 'burner' : detect_devices::isDvdDrive($_) ? 'DVD' : 'cdrom'; push @{$cdroms_by_type{$type}}, $_; } ([ 'cdrom', N("CD-ROM"), sub { @{$cdroms_by_type{cdrom} || []} }, N("CD-ROM") ], [ 'dvd', N("DVD"), sub { @{$cdroms_by_type{DVD} || []} }, N("DVD-ROM") ], [ 'cdwriter', N("CD Burner"), sub { @{$cdroms_by_type{burner} || []} }, N("CD/DVD") ], [ 'floppy', N("Floppy"), \&detect_devices::floppies, N("Floppy drive") ], [ 'zip', N("Zip"), \&detect_devices::zips, N("ZIP drive") ], ), }), [ "NFS mount points", 'diskdrake_nfs', ], [ "Samba mount points", 'diskdrake_samba', ], [ "WebDAV mount points", 'webdav-mdk', ], [ "Partition Sharing", 'diskdrake_fileshare', ], ] ], [ N("Network & Internet"), 'net-mdk', [ [ "Add Connection", 'drakconnect-mdk', ], [ "Configure Internet", 'drakconnect-mdk', ], [ "Manage Connection", 'drakconnect-mdk', ], [ "Monitor Connection", 'drakconnect-mdk', ], [ "Remove Interface", 'drakconnect-mdk', ], [ "Proxy Configuration", 'drakproxy-mdk', ], [ "Connection Sharing", 'drakgw-mdk', ], ], ], [ N("Security"), 'security-mdk', [ [ "Security Level", 'draksec-mdk', ], [ "Security Permissions", 'drakperm-mdk', ], [ "Firewall", 'firewall-mdk', ], ] ], [ N("System"), 'system-mdk', [ [ "Menus" , 'menudrake-mdk', ], [ "Display Manager chooser", 'drakedm-mdk', ], [ "Services" , 'service-mdk', ], [ "Fonts", 'drakfont-mdk', ], [ "Date & Time" , 'time-mdk', ], [ "Logs", 'logdrake-mdk', ], if_($ENV{LANGUAGE} !~ /^zh/, [ "Console", 'console-mdk', ]), [ "Users", 'user-mdk', ], # [ "Programs scheduling", 'drakcronat-mdk', ], [ "Backups", 'backup-mdk', ], # [ "RFBDrake", 'unknown-mdk' ], ] ], if_($::isRpmDrake, [ N("Software Management"), 'software', [ [ "Install Software", 'rpmdrake' ], [ "Remove Software", 'rpmdrake-remove' ], [ "Mandrake Update", 'MandrakeUpdate' ], [ "Software Media Manager", 'source-manager' ], ] ]), if_($::isWiz, [ N("Server wizards"), 'wizard-mdk', [ (map { my ($id, $wizard, $icon, $description) = @$_; $exec_hash->{$id} = [ "drakwizard", "$sbindir/drakwizard $wizard", 1, $description ]; [ $id, $icon ]; } (# [ id, wizard file name, icon, description ] [ "DHCP wizard", "dhcp", 'dhcp_server-mdk', N("Configure DHCP") ], [ "DNS Client wizard", "bind_client", 'dns_client-mdk', N("Add a DNS client") ], [ "DNS wizard", "bind", 'dns_server-mdk', N("Configure DNS") ], [ "FTP wizard", "proftpd", 'ftp-mdk', N("Configure FTP") ], [ "News wizard", "inn", 'news-mdk', N("Configure news") ], [ "Postfix wizard", "postfix", 'postfix-mdk', N("Configure mail") ], [ "Squid wizard", "squid", 'drakproxy-mdk.png', N("Configure proxy") ], [ "Samba wizard", "samba", 'samba_server-mdk', N("Configure Samba") ], [ "Time wizard", "ntp", 'ntp_server-mdk', N("Configure time") ], [ "Web wizard", "apache2", 'web_server-mdk', N("Configure web") ]) ) ] ]), if_($::isWebAdmin, [ N("Online Administration"), 'net-mdk', [ (map { my ($id, $icon, $op, $description) = @$_; $exec_hash->{$id} = [ "mdkwebadmin", "$bindir/mdkwebadmin.pl $op", -1, $description ]; [ $id, $icon ]; } (# [ id, wizard file name, icon, description ] [ "Local Admin", 'XFdrake-mdk', '--direct', N("Local administration") ], [ "Remote Admin", 'drakconnect-mdk', '--link', N("Remote administration") ]) ) ] ]), ); #------------------------------------------------------------- # let build the GUI # main window : my ($timeout, %check_boxes, $exp_frame, $emb_socket); my $window_global = gtkset_size_request(Gtk2::Window->new('toplevel'), $default_width, $default_heigth); $window_global->resize($h{WIDTH}, $h{HEIGTH}); $window_global->set_icon(gtkcreate_pixbuf("/usr/share/icons/drakconf.png")); my $pending_app = 0; my $help_on_context = 'drakconf-intro'; #Please replace with correct contextual help page my @ctx = qw(drakconf-intro mcc-boot mcc-hardware mcc-mountpoints mcc-network mcc-security mcc-system software-management wiz-client); my @themes = grep { -d "$themes_dir/$_" } all($themes_dir); #-PO Translators, please keep all "/" charaters !!! my %options = ( 'show_log' => [ N("/_Options"), N("/Display _Logs") ], 'embedded_mode' => [ N("/_Options"), N("/_Embedded Mode") ], 'wiz_expert' => [ N("/_Options"), N("/Expert mode in _wizards") ], ); my %shared_translations = ( "profiles" => N("/_Profiles"), "delete" => N("/_Delete"), "new" => N("/_New"), ); # for profiles: my ($netcnx, $netc, $intf) = ({}, {}, {}); my @profiles; my $mdk_rel = common::mandrake_release(); my @menu_items = ( [ N("/_File"), undef, undef, undef, '' ], [ N("/_File") . N("/_Quit"), N("Q"), \&quit_global, undef, '', N("Quit") ], [ N("/_Options"), undef, undef, undef, '' ], [ join('', @{$options{show_log}}), undef, sub { $option_values{show_log} = $check_boxes{show_log}->get_active; return if $still_not_in_a_section; start_logdrake(); update_exp(); }, undef, '' ], [ join('', @{$options{embedded_mode}}), undef, sub { $option_values{embedded} = $check_boxes{embedded_mode}->get_active }, undef, '', ], if_($::isWiz, [ join('', @{$options{wiz_expert}}), undef, sub { $option_values{expert_wizard} = $check_boxes{wiz_expert}->get_active }, undef, '', ], ), if_(@themes > 1, [ N("/_Themes"), undef, undef, undef, '' ], (map { my $name = $_; [ N("/_Themes") . "/" . ($name eq $theme ? " O " : " ") . "_$_", undef, sub { return if $theme eq $name; !$pending_app || splash_warning(N("This action will restart the control center.\nAny change not applied will be lost."), 1) and do { # embedded app must be killed kill_children(); kill_logdrake(); child_just_exited(); exec "$0 --theme $name"; }; }, undef, '' ] } @themes), [ N("/_Themes").N("/_More themes"), undef, \&more_themes, undef, '' ] ), [ $shared_translations{profiles}, undef, undef, undef, '' ], [ $shared_translations{profiles} . $shared_translations{new}, undef, sub { my $dialog = _create_dialog(N("New profile..."), { small => 1, transient => $window_global }); my $entry_dialog = Gtk2::Entry->new; gtkpack($dialog->vbox, Gtk2::WrappedLabel->new(N("Name of the profile to create (the new profile is created as a copy of the current one):")), $entry_dialog, ); gtkpack($dialog->action_area, gtksignal_connect(Gtk2::Button->new(N("Cancel")), clicked => sub { $dialog->destroy }), gtksignal_connect(my $bok = Gtk2::Button->new(N("Ok")), clicked => sub { my $prof = $entry_dialog->get_text; # netprofile does not like spaces in profile names... $prof =~ s/ /_/g; # warn if already existing: if (member($prof, @profiles)) { err_dialog(N("Error"), N("The \"%s\" profile already exists!", $prof), { transient => $dialog }); return 1; } network::netconnect::add_profile($netcnx, $prof); update_profiles(); $dialog->destroy; }), ); $bok->can_default(1); $bok->grab_default; $dialog->show_all; $dialog->run; update_profiles(); }, undef, '' ], [ $shared_translations{profiles} . $shared_translations{delete}, undef, sub { return if !$window_global->realized && $netcnx->{PROFILE} ne "default"; my $dialog = _create_dialog(N("Delete profile"), { stock => 'gtk-dialog-warning' }); gtkpack($dialog->vbox, Gtk2::Label->new(N("Profile to delete:")), my $combo_dialog = Gtk2::OptionMenu->new, ); $combo_dialog->set_popdown_strings(grep { ! /default/ } network::netconnect::get_profiles()); gtkpack($dialog->action_area, gtksignal_connect(Gtk2::Button->new(N("Cancel")), clicked => sub { $dialog->destroy }), gtksignal_connect(Gtk2::Button->new(N("Ok")), clicked => sub { my $profile2delete = $combo_dialog->entry->get_text; if ($profile2delete eq $netcnx->{PROFILE}) { err_dialog(N("Warning"), N("You can not delete the current profile")); return 1; } $dialog->destroy; Gtk2->main_quit; network::netconnect::del_profile($profile2delete); $profile2delete eq $netcnx->{PROFILE} ? set_profiles("default") : update_profiles(); update_profiles(1); }), ); $dialog->show_all; $dialog->run; return; }, undef, '' ], [ join('/', $shared_translations{profiles}, ""), undef, undef, undef, '' ], [ N("/_Help"), undef, undef, undef, '' ], [ N("/_Help").N("/_Help"), undef, sub { fork_("drakhelp --id $help_on_context") }, undef, '', N("Help") ], [ N("/_Help").N("/_Report Bug"), undef, sub { fork_("drakbug --report drakconf &") }, undef, '' ], [ N("/_Help").N("/_About..."), undef, \&about_mdk_cc, undef, '' ] ); my ($menu, $factory) = create_factory_menu($window_global, @menu_items); network::netconnect::read_net_conf('', $netcnx, $netc); # to retrieve a path, one must prevent "accelerators completion": sub get_path { join('', map { my $i = $_; $i =~ s/_//; $i } @_) } # menus do not like "_" from profiles (whereas netprofile does not like spaces in profile names...) sub profile2menu { my ($profile) = @_; $profile =~ s/_/ /g; "/$profile"; } sub enable_profile_entry { my ($profile, $value) = @_; eval { $factory->get_widget(get_path("
", $shared_translations{profiles} . profile2menu($profile)))->set_active($value); }; print "error: $@\n" if $@; } # update profiles list sub update_profiles { my ($removing) = @_; my $done; $factory->delete_item(get_path("
", $shared_translations{profiles} . profile2menu($_))) foreach @profiles; network::netconnect::load_conf($netcnx, $netc, $intf); #reread_net_conf(); @profiles = network::netconnect::get_profiles(); foreach my $prof (@profiles) { $factory->create_item([ $shared_translations{profiles} . profile2menu($prof), undef, sub { return unless $done; if ($netcnx->{PROFILE} eq $prof) { $done = 0; enable_profile_entry($prof, 1); $done = 1; return 0; } if (!warn_dialog(N("Warning"), N("We are about to switch from the \"%s\" profile to the \"%s\" profile. Are you sure you want to do the switch.", $netcnx->{PROFILE}, $prof), { transient => $window_global })) { $done = 0; enable_profile_entry($prof, 0); $done = 1; return; } # wait message is needed $done = 0; enable_profile_entry($netcnx->{PROFILE}, 0); $netcnx->{PROFILE} = $prof; network::netconnect::save_profile($netcnx); network::netconnect::set_profile($netcnx); enable_profile_entry($prof, 1) if !$removing; $done = 1; }, undef, '' ]); # Radio } $factory->get_widget(get_path("
", $shared_translations{profiles}, $shared_translations{delete}))->set_sensitive(@profiles > 1); eval { $factory->get_widget(get_path("
", $shared_translations{profiles} . profile2menu($netcnx->{PROFILE})))->set_active(1) }; print "error is «$@»\n" if $@; $done = 1; } update_profiles(); %check_boxes = map { $_ => $factory->get_widget("
" . get_path(@{$options{$_}})) } ("embedded_mode", "show_log", if_($::isWiz, "wiz_expert")); gtkadd($window_global, gtkpack_(Gtk2::VBox->new(0, 0), 0, $menu, 0, gtkset_size_request(Gtk2::VBox->new(10, 10), -1, 2), 1, gtkpack_(Gtk2::HBox->new(0, 0), 0, gtkpack_(Gtk2::VBox->new(0, 0), 0, my $fixed_left = gtkset_name(Gtk2::Fixed->new, 'mcc'), # default left background 1, Gtk2::SizeableImage->new('mcc-left-back-middle'), 0, rtl_gtkcreate_img('mcc-left-back-bottom') ), 1, gtkpack_(Gtk2::VBox->new(0, 0), 0, Gtk2::SizeableImage->new('mcc-right-bottom'), 1, gtkset_name( gtkadd(my $emb_frame = Gtk2::EventBox->new, gtkadd(gtkset_border_width(Gtk2::Frame->new, 5), gtkpack_(my $emb_box = Gtk2::VBox->new(0, 0), 1, gtkpack_(my $emb_wait = Gtk2::VBox->new(0, 0), 1, Gtk2::HBox->new(0, 0), 0, gtkpack_(Gtk2::HBox->new(0, 0), 1, Gtk2::VBox->new(0, 0), 0, my $run_darea = gtkset_size_request(Gtk2::DrawingArea->new, 128, 128), 1, Gtk2::VBox->new(0, 0), ), 0, Gtk2::Label->new(N("Please wait...")), 1, Gtk2::HBox->new(0, 0), 0, gtkadd(gtkset_layout(gtkset_border_width(Gtk2::HButtonBox->new, 10), 'end'), gtksignal_connect(Gtk2::Button->new_from_stock(N("Cancel")), clicked => sub { Glib::Source->remove($timeout) if $timeout; &child_just_exited() }), ), ), ), ), ), 'mcc'), 1, my $notebook_global = gtkset_name(Gtk2::Notebook->new, 'mcc'), 0, gtkset_name(my $w_exp = create_scrolled_window(gtkset_shadow_type(gtkset_size_request(gtkset_border_width($exp_frame = Gtk2::Frame->new(N("Logs")), 5), -1, 120), 'etched_in'), [ 'never', 'never' ], 'none'), 'mcc'), 0, Gtk2::SizeableImage->new('mcc-right-bottom') ) ), 0, gtkset_size_request(Gtk2::VBox->new(10, 10), -1, 2) ) ); $window_global->signal_connect(delete_event => \&quit_global); $window_global->set_title(N("Mandrake Control Center %s", $version) . " [" . chomp_(`hostname`) . "]"); $window_global->set_position('center'); $notebook_global->set_property('show-border', 0); $notebook_global->set_property('show-tabs', 0); #$notebook_global->set_show_tabs(0); # banner : my $pixbuf_icon = gtkcreate_pixbuf('mcc-title-icon'); # main page (summary) : my $margin = 40; my $summary = gtktext_insert(Gtk2::TextView->new, [ [ N("Welcome to the Mandrake Control Center")."\n\n", {'size-points' => 15, justification => 'center', pixels_above_lines => $margin-10, 'weight-set' => 1, weight => 1000 } ], [ formatAlaTeX(N("Mandrake Control Center is Mandrake Linux's main configuration tool. It enables the system administrator to configure the hardware and services used for all users. The tools accessed through the Mandrake Control Center greatly simplify the use of the system, notably by avoiding the use of the evil command line.")) ], [ "\n ", { justification => 'GTK_JUSTIFY_RIGHT' } ], [ gtkcreate_pixbuf('mcc-welcome-logo'), { justification => 'GTK_JUSTIFY_RIGHT' } ] ]); $summary->set_left_margin($margin); $summary->set_right_margin($margin); add2notebook($notebook_global, "", create_scrolled_window(gtkset_size_request($summary, 50, 50), [ 'never', 'automatic' ] ), ); use POSIX qw(:sys_utsname_h :math_h :sys_wait_h :unistd_h); my (undef, $nodename, $release, undef, $machine) = POSIX::uname(); # left icons : my @darea_left_list; my ($cursor_hand, $cursor_normal) = (Gtk2::Gdk::Cursor->new('hand2'), Gtk2::Gdk::Cursor->new('left-ptr')); my $left_back_pixbuf = rtl_gtkcreate_pixbuf('mcc-left-back'); my ($back_width, $back_height) = ($left_back_pixbuf->get_width, $left_back_pixbuf->get_height); # 0 => unselected, 1 => highlited, 2 => selected my ($index, $left_locked) = (0, 0); my $index_sav = -1; my (@curr_state, @old_state); my ($d_width, $d_height) = (173, 46); my $right_text_offset = $d_width - 155; my @colors = (gtkcolor(0, 0, 0), gtkcolor(0xAA, 0xAA, 0xFF), gtkcolor(0, 0, 0)); my $spacing = 25; my ($lspacing, $left_txt_offset, $icon_offset); my $hand_cursor = sub { $_[0]->window->set_cursor($cursor_hand) }; # Create left icons foreach (@tree) { my ($text, $icon, $subtree) = @$_; # Skip empty classes my @subtree; foreach my $stuff (@$subtree) { my $exec = first(split /\s+/, $exec_hash->{$stuff->[0]}[1]); # do not complain about missing entries in move: if (-x $exec) { push @subtree, $stuff; } else { warn qq("$exec" is not executable) if $mdk_rel !~ /Move/; } } next if !@subtree; my $my_index = $index++; # die "gtkput(fixed_left, VBox of StockButtons there or\n (while waiting for gc to implement IconFactory) VBox of Hbox[icon|text]"; my $darea_left = gtkset_size_request(Gtk2::DrawingArea->new, $d_width, $d_height); $darea_left->set_events([ 'exposure_mask', 'enter_notify_mask', 'leave_notify_mask', 'button_press_mask', 'button_release_mask' ]); my $icon_pixbuf = gtkcreate_pixbuf($icon); my ($icon_width, $icon_height) = ($icon_pixbuf->get_width, $icon_pixbuf->get_height); unless ($lspacing) { # $lspacing = round_up(($d_height-$icon_height)/2, 1); $lspacing = ($d_height-$icon_height)/2; $left_txt_offset = $lspacing * 2 + $icon_width; $icon_offset = 15 if lang::text_direction_rtl(); $left_txt_offset += $icon_offset; $lspacing = round_up($lspacing, 1); } my $left_back_pixbuf = $my_index ? $left_back_pixbuf : rtl_gtkcreate_pixbuf('mcc-left-back-top'); my @icon_pixbufs = ($icon_pixbuf, render_shiner($icon_pixbuf, 1.89), $icon_pixbuf); my @fonts = map { Gtk2::Pango::FontDescription->from_string($_) } '', 'Bold', 'Bold'; my ($lines, $widths, $heights, $offset, @dbl_area_left); push @curr_state, 0; push @old_state, -1; $darea_left->signal_connect(expose_event => sub { my (undef, $event) = @_; my $curr_state = $curr_state[$my_index]; my $full_redraw = $curr_state != $old_state[$my_index]; my ($x, $y, $width, $height) = $full_redraw ? (0, 0, $d_width, $d_height) : $event->area->values; unless ($lines) { $darea_left->modify_font($fonts[1]); ($lines, $widths, $heights) = (get_text_coord($text, $darea_left, $d_width-$left_txt_offset-$right_text_offset, $d_height, 0, 0, 0, 0))[2..4]; $offset = -($d_height - string_height($darea_left, $lines->[0]) * (listlength(@$lines) + 0.2)) / 2; # text lines are offsetted } # Redraw double buffer on first expose in that particular state (selected <=> not selected): render background, then icon, then text unless ($dbl_area_left[$curr_state]) { my $window = $darea_left->window; $dbl_area_left[$curr_state] = Gtk2::Gdk::Pixmap->new($window, $d_width, $d_height, $window->get_depth); my $gc = $darea_left->style->fg_gc('normal'); $left_back_pixbuf->render_to_drawable($dbl_area_left[$curr_state], $gc, 0, 0, 0, 0, $back_width, $back_height, 'normal', 0, 0); $icon_pixbufs[$curr_state]->render_to_drawable($dbl_area_left[$curr_state], $gc, 0, 0, $lspacing+$icon_offset, $lspacing, $icon_width, $icon_height, 'normal', 0, 0); $darea_left->style->black_gc->set_rgb_fg_color($colors[$curr_state]); $darea_left->modify_font($fonts[$curr_state]); mapn { $dbl_area_left[$curr_state]->draw_layout($darea_left->style->black_gc, $_[1]+$left_txt_offset, $_[2]-$offset, $darea_left->create_pango_layout($_[0])); } $lines, $widths, $heights; } $old_state[$my_index] = $curr_state; $darea_left->window->draw_drawable($darea_left->style->bg_gc('normal'), $dbl_area_left[$curr_state], $x, $y, $x, $y, $width, $height); }); $darea_left->signal_connect(realize => $hand_cursor); $darea_left->signal_connect(enter_notify_event => sub { return if $old_state[$my_index] == -1 || $curr_state[$my_index] != 0; $curr_state[$my_index] = 1; $darea_left->queue_draw; }); $darea_left->signal_connect(leave_notify_event => sub { return if $curr_state[$my_index] != 1; $curr_state[$my_index] = 0; $darea_left->queue_draw; }); $darea_left->signal_connect(button_release_event => sub { return if $left_locked; if ($pending_app) { return if !splash_warning(N("The modifications done in the current module won't be saved."), 1); kill_children(); child_just_exited(); } # deselect previously selected darea if ($index_sav != -1) { $curr_state[$index_sav] = 0; $hand_cursor->($darea_left_list[$index_sav]); $darea_left_list[$index_sav]->queue_draw; } $index_sav = $my_index; $curr_state[$my_index] = 2; $darea_left->window->set_cursor($cursor_normal); $darea_left->queue_draw; set_page($my_index+1); }); # push left icons $fixed_left->put($darea_left, 0, $my_index*$d_height); push @darea_left_list, $darea_left; # Create right notebook pages : my $tbl = create_packtable({ col_spacings => $spacing, row_spacings => $spacing, homogeneous => 1, mcc => 1 }, group_by2(map { my ($label, $tag) = @$_; my $text = $exec_hash->{$label}[3]; die "$label 's icon is missing" if !$exec_hash->{$label} && $::testing; my $hbox_spacing = 10; my $event_box = gtkadd(Gtk2::EventBox->new, gtkpack_( Gtk2::VBox->new(0, $hbox_spacing), 0, gtkcreate_img($tag), 1, Gtk2::WrappedLabel->new($text), if_(0,1, gtktext_insert( #- disable selecting text and popping the contextual menu # (GUI team says it's *horrible* to be able to do select text!) gtksignal_connect(my $tv = Gtk2::TextView->new, button_press_event => sub { 1 }), [ [ $text, {'background_set' => 0, 'background_stipple_set' => 0 } ] ]) ), ) ); $tv->set_events([]); $event_box->set_events([ 'enter_notify_mask', 'leave_notify_mask', 'button_press_mask', 'button_release_mask' ]); # FIXME: do ->set_pixbuf() on {enter,leave}_events $event_box->signal_connect(realize => $hand_cursor); $event_box->signal_connect(button_release_event => sub { compute_exec_string($tag, @{$exec_hash->{$label}}) }); # FIXME : resize sig: ->foreach; set_size_request gtkset_size_request($event_box, 50, -1); } @subtree)); add2notebook($notebook_global, "", my $_w_ret = create_scrolled_window(gtkset_border_width($tbl, 5), [ 'never', 'automatic' ], 'none' ), ); # $w_ret->vscrollbar->set_size_request(19, undef); } $notebook_global->set_size_request(-1, $index * 60); $emb_frame->set_size_request(-1, $index * 50); foreach (keys %check_boxes) { my $widget = $check_boxes{$_}; if (defined $widget) { $widget->set_active($option_values{$_}); } else { print STDERR "BUG with LANGUAGE $ENV{LANGUAGE}\n"; } }; hide_socket_and_clean(); # "wait while launching a program" area : my ($run_pixbuf, $run_counter, $run_counter_add); $run_darea->signal_connect(expose_event => sub { return unless $run_pixbuf; # some people got an expose event before we start an embedded tool my $pixbuf = render_alpha($run_pixbuf, $run_counter); my ($window, $gc, $width, $height) = ($run_darea->window, $run_darea->style->fg_gc('normal'), $pixbuf->get_width, $pixbuf->get_height); $pixbuf->render_to_drawable($window, $gc, 0, 0, 0, 0, $width, $height, 'normal', 0, 0); $run_counter += $run_counter_add; $run_counter_add = -$run_counter_add if $run_counter < 100 || 245 < $run_counter; }); gtkflush(); $notebook_global->set_current_page(0); $notebook_global->signal_connect(switch_page => sub { my $tab_number = $_[2]; return unless $tab_number > 0; }); $window_global->show_all; $exp_frame->hide; $emb_frame->hide; $SIG{USR1} = 'IGNORE'; $SIG{USR2} = 'IGNORE'; $SIG{TERM} = \&quit_global; $SIG{CHLD} = \&sig_child; #$SIG{CONT} = sub { Gtk2->main }; $window_splash->destroy; undef $window_splash; Gtk2->main; #------------------------------------------------------------- # socket/plug managment # called once embedded tool has exited sub child_just_exited() { $pending_app = 0; $left_locked = 0; hide_socket_and_clean(); if ($emb_socket) { $emb_socket->destroy; undef $emb_socket; } gtkset_mousecursor_normal(); $notebook_global->show; Glib::Source->remove($timeout) if $timeout; } sub hide_socket_and_clean() { $emb_frame->hide; $pending_app = 0; update_exp(); } sub create_hidden_socket() { gtkpack_($emb_box, 1, gtksignal_connect($emb_socket = Gtk2::Socket->new, 'plug-removed' => \&child_just_exited)); # signal emitted when embedded apps begin to draw: $emb_socket->signal_connect('plug-added' => sub { $left_locked = 0; $emb_wait->hide; return if !$emb_socket; $emb_socket->show; $emb_socket->can_focus(1); $emb_socket->grab_focus; $emb_socket->window->XSetInputFocus; }); $emb_box->set_focus_child($emb_socket); $emb_socket->hide; $emb_wait->hide; } sub update_exp() { return if $still_not_in_a_section; if ($option_values{show_log}) { $w_exp->show_all; } else { $w_exp->hide; } } #------------------------------------------------------------- # processes managment # embedded processes pid will be stocked there my @pid_launched; # logdrake pid are stocked here my $pid_exp; sub fork_ { my ($prog, $o_pid_table) = @_; $o_pid_table ||= \@pid_launched; my $pid = fork(); if (defined $pid) { !$pid and do { exec($prog) or POSIX::_exit(1) }; # immediate exit, else forked gtk+ object destructors will badly catch up parent mcc push @$o_pid_table, $pid if $option_values{embedded}; return $pid; } else { splash_warning(N("cannot fork: %s", "$!")); child_just_exited(); } } sub compute_exec_string { my ($icon, $log_exp, $exec_, $gtkplug, undef, $alternate) = @_; #($_[0], @{$_[1]}); my $exec = ref($exec_) ? $exec_->[0] : $exec_; if (! -x first(split /\s+/, $exec)) { splash_warning(N("cannot fork and exec \"%s\" since it is not executable", $exec)); return; } $exec .= " --summary" if $option_values{expert_wizard} && $exec_ =~ /drakwizard/; my $embedded = $option_values{embedded} && $gtkplug != -1; # globally embedded and not "explicitely not embedded" if ($embedded) { $notebook_global->hide; create_hidden_socket(); $emb_frame->show; $emb_socket->realize; $pending_app = 1; if ($gtkplug > 0) { $exec .= " --embedded " . $emb_socket->get_id; $emb_wait->show; $run_pixbuf = gtkcreate_pixbuf($icon . "_128"); $run_counter = 255; $run_counter_add = -10; $timeout = Glib::Timeout->add(70, sub { $run_darea->queue_draw; 1 }); $left_locked = 1; fork_($exec); } else { # gtkplug == 0 $emb_box->grab_focus; $emb_socket->grab_focus; $emb_socket->show; $exec_->[0] = $exec; $SIG{CHLD} = undef; $emb_socket->add_id(launch_xapp(@$exec_)); $SIG{CHLD} = \&sig_child; } } else { # not embedded # fix #3415 when $gtkplug eq -1 my $old = $option_values{embedded}; $option_values{embedded} = 0; fork_($gtkplug == 0 ? $exec_->[0] : $alternate || $exec); $option_values{embedded} = $old; } #FIXME # if ($option_values{embedded} && $gtkplug != -1) { # foreach (@darea_left_list) { # $_->->window->set_cursor($cursor_hand); # if ($_->{state} != 0) { # $_->{state} = 0; # $_->->draw(undef); # } # } # } start_logdrake(); $w_exp->hide if $embedded && $exec =~ /^drakfont|logdrake|mousedrake/; } sub start_logdrake { # (re)start logdrake if needed if ($option_values{show_log} && !$pid_exp) { #FIXME && !$exp_socket gtkshow(gtkadd($exp_frame, my $exp_socket = Gtk2::Socket->new)); $w_exp->show; my $exec_log = "logdrake --explain=drakxtools --embedded " . $exp_socket->window->XWINDOW; $pid_exp = fork_($exec_log, []); } } sub launch_xapp { my ($exec, $name, $xx) = @_; my $find_windows = sub { local *X; open(X, "-|", "xwininfo -root -tree -int"); grep { /$name/ } ; }; my @before = &$find_windows(); fork_($exec); my @after = &$find_windows(); require Time::HiRes; while (@after != $xx + @before) { Time::HiRes::usleep(50); @after = &$find_windows() } my $c = top(difference2(\@after, \@before)); return $1 if $c =~ /\s*([0-9]*)\s*/; } sub kill_them_all { map { if__($_, kill 'TERM', $_) } @_; } sub kill_children() { kill_them_all(@pid_launched); @pid_launched = (); } sub kill_logdrake() { kill_them_all($pid_exp) if $pid_exp; } sub quit_global() { &kill_children(); &kill_logdrake(); my ($x, $y) = $window_global->get_size; setVarsInSh($conffile, { EMBEDDED => bool2text($option_values{embedded}), LOGS => bool2text($option_values{show_log}), EXPERT_WIZARD => bool2text($option_values{expert_wizard}), HEIGTH => $y, WIDTH => $x, THEME => $theme, }); gtkset_mousecursor_normal(); Gtk2::exit(0); } #------------------------------------------------------------- # signals managment # got when child died and gone in zombie state sub sig_child() { my $child_pid; do { $child_pid = waitpid(-1, POSIX::WNOHANG); undef $pid_exp if $pid_exp eq $child_pid; } while $child_pid > 0; # child unexpectedly died (cleanup since child_just_exited won't be called by plug-removed since plug never was added) return unless $left_locked; child_just_exited(); splash_warning(N("This program has exited abnormally")); } #------------------------------------------------------------- # mcc dialog specific functions sub splash_warning { my ($label, $o_cancel_button) = @_; warn_dialog(N("Warning"), $label, { cancel => $o_cancel_button }); } sub new_dialog { my ($title, $o_no_button) = @_; my $dialog = gtkset_border_width(Gtk2::Dialog->new, 10); $dialog->set_transient_for($window_global); $dialog->set_position('center-on-parent'); $dialog->set_title($title); $dialog->action_area->pack_start(gtkadd(Gtk2::HButtonBox->new, gtksignal_connect(Gtk2::Button->new(N("Close")), clicked => sub { $dialog->destroy }) ), 0,0,0) unless $o_no_button; gtkset_modal($dialog, 1); } sub more_themes() { my $window_about = new_dialog(N("More themes")); gtkpack_($window_about->vbox, 0, Gtk2::Label->new(N("Getting new themes")), 0, gtkadd(gtkset_shadow_type(gtkset_border_width(Gtk2::Frame->new(N("Additional themes")), 10), 'etched_out'), gtkpack(Gtk2::HBox->new(0, 5), N("Get additional themes on www.damz.net"), ) ) ); $window_about->show_all; } sub about_mdk_cc() { my $window_about = new_dialog(N("About - Mandrake Control Center")); my $tree_model = Gtk2::TreeStore->new("Glib::String", "Glib::String", "Glib::String"); my $credits_model = Gtk2::TreeStore->new("Glib::String", "Glib::String"); my ($list, $clist) = (Gtk2::TreeView->new_with_model($tree_model), Gtk2::TreeView->new_with_model($credits_model)); $_->can_focus(0) foreach $list, $clist; each_index { $list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => $::i)) } 0..2; each_index { $clist->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => $::i)) } 0..1; $_->set_headers_visible(0) foreach $list, $clist; foreach my $row ([ N("Authors: "), '', '' ], [ '', 'Chmouel Boudjnah', N("(original C version)") ], #-PO "perl" here is the programming language [ '', 'Damien "dam\'s" Krotkine', N("(perl version)") ], [ '', 'Daouda Lo', '' ], [ '', 'Thierry Vignaud', '' ], [ '', 'Yves Duret', N("(perl version)") ], [ '', '' ], [ N("Artwork: "), '', '' ], [ '', 'Anh-Van Nguyen', N("(design)") ], #-PO If your language allows it, use eacute for first "e" and egrave for 2nd one. [ '', N("Helene Durosini"), '' ], ) { $tree_model->append_set(undef, [ map_index { $::i => $_ } @$row ]); } foreach my $line (sort(cat_(top(glob("/usr/share/doc/mandrake-release-*/CREDITS"))))) { $credits_model->append_set(undef, map_index { $::i => $_ } map { translate(common::sprintf_fixutf8($_)) } split(', ', chomp_($line), 2)); } # Give our translators the ability to show their family and # friends that thez participated ... #-PO Add your Name here to find it in the About section in your language. my $translator_name = N("~ * ~"); #-PO Add your E-Mail address here if you want to show it in the about doialog. my $translator_email = N("~ @ ~"); if ($translator_name ne "~ * ~ " && 0) { $list->append_set(undef, [ 0 => $_->[0], 1 => $_->[1] ]) foreach [ '', '' ], [ N("Translator: "), $translator_name, $translator_email ]; } $list->get_selection->set_mode('none'); gtkpack_($window_about->vbox, -r "$themes_dir/$theme/splash_screen_about.png" ? (0, gtkcreate_img("splash_screen_about")) : (1, gtkmodify_font(Gtk2::Label->new(N("Mandrake Control Center %s\n", $version)), 'Bold 24'),), 0, Gtk2::Label->new("\n" . N("Copyright (C) 1999-2003 Mandrakesoft SA") . "\n"), 1, my $n = Gtk2::Notebook->new, ); add2notebook($n, N("Authors"), $list); add2notebook($n, N("Mandrake Linux Contributors"), create_scrolled_window(gtkset_size_request($clist, 50, 50))); $window_about->show_all; } #------------------------------------------------------------- # mcc specific graphic functions: sub set_page { my ($index) = @_; start_logdrake(); update_exp(); $notebook_global->set_current_page($index); $still_not_in_a_section = 0; $help_on_context = $ctx[$index]; } #------------------------------------------------------------- # mcc specific graphic functions: sub rtl_gtkcreate_pixbuf { my ($icon) = @_; my $pixbuf; eval { $pixbuf = ugtk2::gtkcreate_pixbuf($icon . "_rtl") } if lang::text_direction_rtl(); $pixbuf ||= ugtk2::gtkcreate_pixbuf($icon); } sub rtl_gtkcreate_img { my ($icon) = @_; lang::text_direction_rtl() ? ugtk2::gtkcreate_img($icon . "_rtl") : ugtk2::gtkcreate_img($icon); } sub new_pixbuf { my ($pixbuf) = @_; my ($height, $width) = ($pixbuf->get_height, $pixbuf->get_width); my $new_pixbuf = Gtk2::Gdk::Pixbuf->new('rgb', 1, 8, $height, $width); $new_pixbuf->fill(0x00000000); # transparent white $width, $height, $new_pixbuf; } sub render_alpha { my ($pixbuf, $alpha_threshold) = @_; my ($width, $height, $new_pixbuf) = new_pixbuf($pixbuf); $pixbuf->composite($new_pixbuf, 0, 0, $width, $height, 0, 0, 1, 1, 'nearest', $alpha_threshold); $new_pixbuf; } sub render_shiner { my ($pixbuf, $shine_value) = @_; my $new_pixbuf = (new_pixbuf($pixbuf))[2]; $pixbuf->saturate_and_pixelate($new_pixbuf, $shine_value, 0); $new_pixbuf; } sub scale { my ($pixbuf, $gain) = @_; my ($width, $height) = ($pixbuf->get_height, $pixbuf->get_width); $pixbuf->scale_simple($height+$gain, $width+$gain, 'hyper'); } package Gtk2::SizeableImage; use ugtk2 qw(:helpers :wrappers); sub set_bg_img { my ($w) = @_; $w->{pixbuf} ||= gtkcreate_pixbuf($w->{image}); set_back_pixbuf($w, $w->{pixbuf}); } sub new { my ($_class, $image) = @_; my $w = gtksignal_connect( gtksignal_connect( gtkset_size_request(Gtk2::DrawingArea->new, -1, 1), "configure-event" => \&set_bg_img ), realize => \&set_bg_img ); $w->{image} = $image; $w; } 1;