#!/usr/bin/perl # $Id$ # Copyright (C) 1999-2004 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 c; 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 (%tool_pids, %tool_feedback); my ($version, $conffile, $class_install) = ( `cat /etc/mandrakelinux-release`=~/\b(\d+\.{1}.+\))/, "/etc/mcc.conf", "/etc/sysconfig/system" ); my ($default_heigth, $default_width) = (523, 720); require_root_capability(); # just to get root capabilities #------------------------------------------------------------- # read configuration, set themes, ... my %h = getVarsFromSh($conffile); my %class = getVarsFromSh($class_install); $h{THEME} ||= 'default'; $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{show_log} = text2bool($h{LOGS}); my $theme = $h{THEME}; $theme = $1 if "@ARGV" =~ /--theme (\w+)/; my $program; foreach (@ARGV) { $program = $1 if /--start-with=(.*)/; } my ($i, $geometry); foreach (@ARGV) { $i++; if (/^--geometry$/) { $geometry = splice @ARGV, $i, 1; last; } } -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("Mandrakelinux Control Center")); $window_splash->set_position('center_always'); $window_splash->add(gtkadd(gtkset_shadow_type(Gtk2::Frame->new, 'etched_out'), gtkput(my $fixed = Gtk2::Fixed->new, gtkpack(Gtk2::VBox->new(0, 0), if_(-r "$themes_dir/$theme/splash_screen.png", gtkcreate_img("splash_screen")), gtkset_markup(Gtk2::Label->new, join('', qq(\n), N("Mandrakelinux Control Center"), ""), ), Gtk2::Label->new(N("Loading... Please wait") . "\n"), ), 0, 0), ) ); $fixed->set_has_window(1); $fixed->signal_connect(realize => sub { ugtk2::set_back_pixmap($fixed) }); $window_splash->show_all; gtkflush(); #------------------------------------------------------------- # Data structures my $isWiz = -e "/usr/sbin/drakwizard"; my $isRpmDrake = -e "/usr/sbin/rpmdrake"; my $isParkRpmDrake = -e "/usr/sbin/park-rpmdrake"; my $isWebAdmin = -e "/usr/bin/mdkwebadmin"; my $isRfbDrake = -e "/usr/X11R6/bin/rfbdrake"; my $isDrakStats = -e "/usr/bin/drakstats"; my $application_driven_menu; # { key => { log_exp, binary, embedded?, description ] } # embedded meaning: -1 => not embedded, 0 => external x11 app, 1 => proper embedding my %programs = ( "Authentication" => { binary => "$sbindir/drakauth", embedded => 1, description => #-PO: this message is already translated in drakx domain from which MCC will searchs it: N("Authentication"), icon => 'drakauth-mdk', }, "Auto Install" => { binary => "$sbindir/drakautoinst", embedded => 1, description => N("Auto Install floppy"), icon => 'drakautoinst-mdk', }, "Auto login Config" => { binary => "$sbindir/drakboot", embedded => 1, description => N("Autologin"), icon => 'autologin-mdk', }, "Backups" => { binary => "$sbindir/drakbackup", embedded => 1, description => N("Backups"), icon => 'backup-mdk', }, "Boot Config" => { binary => "$sbindir/drakboot --boot", embedded => 1, description => N("Boot loader"), icon => 'drakboot-mdk', }, "Boot Theme" => { binary => "$sbindir/drakboot --splash", embedded => 1, description => N("Boot theme"), icon => 'boot-theme-mdk', }, "Boot Disk" => { binary => "$sbindir/drakfloppy", embedded => 1, description => N("Boot floppy"), icon => 'drakfloppy-mdk', }, "Connection Sharing" => { binary => "$sbindir/drakgw", embedded => 1, description => N("Internet connection sharing"), icon => 'drakgw-mdk', }, "Add Connection" => { binary => "$sbindir/drakconnect --wizard", embedded => 1, description => N("New connection"), icon => 'new-connection-mdk', }, "Configure Internet" => { binary => "$sbindir/drakconnect --internet", embedded => 1, description => N("Internet access"), icon => 'internet-access-mdk', }, "Console" => { binary => "$sbindir/drakconsole", embedded => 1, description => N("Console"), icon => 'console-mdk', }, "Date & Time" => { binary => "$sbindir/clock.pl", embedded => 1, description => N("Date and time"), #The Console will help you to solve issues icon => 'time-mdk', }, "Display Manager chooser" => { binary => "$sbindir/drakedm", embedded => 1, description => N("Display manager"), icon => 'drakedm-mdk', }, "Fax" => { binary => "$sbindir/drakfax_server", embedded => 1, description => N("Fax"), icon => 'drakfax-mdk', }, "Firewall" => { binary => "$sbindir/drakfirewall", embedded => 1, description => N("Firewall"), icon => 'firewall-mdk', }, "Fonts" => { binary => "$sbindir/drakfont", embedded => 1, description => N("Fonts"), icon => 'drakfont-mdk', }, "Graphical server configuration" => { binary => "$sbindir/XFdrake", embedded => 1, description => N("Graphical server"), icon => 'XFdrake-mdk' }, "Hard Drives" => { binary => "$sbindir/diskdrake --hd", embedded => 1, description => N("Partitions"), icon => 'diskdrake_hd', }, "Hardware List" => { binary => "$sbindir/harddrake2", embedded => 1, description => N("Hardware"), icon => 'harddrake-mdk', application_driven_menu => 1, }, "Install Software" => { binary => "$sbindir/rpmdrake", embedded => 1, description => N("Install"), icon => 'rpmdrake', application_driven_menu => 1, }, "Installed Software" => { binary => "$sbindir/rpmdrake-remove", embedded => 1, description => N("Installed Software"), icon => 'rpmdrake-remove', application_driven_menu => 1, }, "Keyboard" => { binary => "$sbindir/keyboarddrake", embedded => 1, description => N("Keyboard"), icon => 'keyboard-mdk', }, "Kolab" => { binary => "$sbindir/kolab", embedded => 1, description => N("Kolab"), }, "Localization" => { binary => "$bindir/localedrake", embedded => 1, description => #-PO: this message is already translated in drakx domain from which MCC will searchs it: N("Language") . " - " . #-PO: this message is already translated in drakx domain from which MCC will searchs it: N("Country / Region"), }, "Logs" => { binary => "$sbindir/logdrake", embedded => 1, description => N("Logs"), icon => 'logdrake-mdk', }, "Manage Connection" => { binary => "$sbindir/drakconnect --skip-wizard", embedded => 1, description => N("Manage connections"), icon => 'manage-connections-mdk', }, "Manage park" => { binary => "$sbindir/park-rpmdrake", embedded => 1, description => N("Manage computer group"), icon => 'drakpark-mdk', }, "Mandrake Update" => { binary => "$sbindir/MandrakeUpdate", embedded => 1, description => N("Updates"), icon => 'MandrakeUpdate', }, "Menus" => { binary => "$bindir/menudrake", embedded => -1, description => N("Menus"), icon => 'menudrake-mdk', }, "Monitor" => { binary => "$sbindir/XFdrake monitor", embedded => 1, description => N("Monitor"), icon => 'configure-monitor-mdk', }, "Monitor Connection" => { binary => "$sbindir/net_monitor", embedded => 1, description => N("Monitor connections"), icon => 'net_monitor-mdk', }, "Mouse" => { binary => "$sbindir/mousedrake", embedded => 1, description => N("Mouse"), icon => 'mousedrake-mdk', }, "NFS mount points" => { binary => "$sbindir/diskdrake --nfs", embedded => 1, description => N("NFS mount points"), icon => 'diskdrake_nfs', }, "Package stats" => { binary => "$bindir/drakstats", embedded => 1, description => N("Package Stats"), icon => 'source-manager', }, "Partition Sharing" => { binary => "$sbindir/diskdrake --fileshare", embedded => 1, description => N("Local disk sharing"), icon => 'diskdrake_fileshare', }, "Printer" => { binary => "$sbindir/printerdrake", embedded => 1, description => N("Printers"), icon => 'printer-mcc-mdk', application_driven_menu => 1, }, "Programs scheduling" => { binary => "/usr/X11R6/bin/drakcronat", embedded => 1, description => N("Scheduled tasks"), #DrakCronAt enables to schedule Programs execution through crond and atd daemons icon => 'drakcronat-mdk', }, "Proxy Configuration" => { binary => "$sbindir/drakproxy", embedded => 1, description => N("Proxy"), #for files and web browsing icon => 'drakproxy-mdk', }, "Remote Control" => { binary => "$xbindir/rfbdrake", embedded => 1, description => N("Remote Control (Linux/Unix, Windows)"), icon => 'internet-access-mdk', }, "Remove Interface" => { binary => "$sbindir/drakconnect --del", embedded => 1, description => N("Remove a connection"), icon => 'remove-connection-mdk', }, "Remove Software" => { binary => "$sbindir/rpmdrake-remove", embedded => 1, description => N("Remove"), icon => 'rpmdrake-remove', application_driven_menu => 1, }, "Resolution" => { binary => "$sbindir/XFdrake resolution", embedded => 1, description => N("Screen resolution"), icon => 'resolution-mdk', }, "Samba mount points" => { binary => "$sbindir/diskdrake --smb", embedded => 1, description => N("Samba mount points"), icon => 'diskdrake_samba', }, "Scanner" => { binary => "$sbindir/scannerdrake", embedded => 1, description => N("Scanners"), icon => 'scanner-mdk', }, "Security Level" => { binary => "$sbindir/draksec", embedded => 1, description => N("Level and checks"), icon => 'draksec-mdk', }, "Security Permissions" => { binary => "$sbindir/drakperm", embedded => 1, description => N("Permissions"), icon => 'drakperm-mdk', }, "Services" => { binary => "$sbindir/drakxservices", embedded => 1, description => N("Services"), icon => 'service-mdk', }, "Software Media Manager" => { binary => "$sbindir/edit-urpm-sources.pl", embedded => 1, description => N("Media Manager"), icon => 'source-manager', }, "TV Cards" => { binary => "$sbindir/drakxtv", embedded => 1, description => N("TV card"), icon =>, 'tv-mdk', }, "UPS" => { binary => "$sbindir/drakups --wizard", embedded => 1, description => N("UPS"), icon => 'drakups-mdk', }, "Users" => { # too big binary => "$bindir/userdrake", embedded => 1, description => N("Users and groups"), icon => 'user-mdk', application_driven_menu => 1, }, "WebDAV mount points" => { binary => "$sbindir/diskdrake --dav", embedded => 1, description => N("WebDAV mount points"), icon => 'webdav-mdk', }, ); sub wizard_format { map { my ($id, $wizard, $icon, $description) = @$_; $programs{$id} = { binary => "$sbindir/drakwizard $wizard", embedded => 1, description => $description, icon => $icon, }; $id; } @_; } # [ [ class_label, class icon name, [ program_label, ... ] ] my @tree = (if_($isRpmDrake || $isParkRpmDrake, [ N("Software Management"), 'software', [ if_($isRpmDrake, "Install Software", "Installed Software", "Remove Software", "Mandrake Update", "Software Media Manager", ), if_($isParkRpmDrake, "Manage park"), if_($isDrakStats, "Package stats"), ] ]), if_($isWiz, if_(0, [ N("Server wizards"), 'wizard-mdk' ]), [ N("Sharing"), 'wizard-mdk', [ (wizard_format(# [ id, wizard file name, icon, description ] [ "FTP wizard", "proftpd", 'ftp-mdk', N("Configure FTP") ], [ "Samba wizard", "samba", 'samba_server-mdk', N("Configure Samba") ], [ "Web wizard", "apache2", 'web_server-mdk', N("Configure web server") ], [ "Installation server wizard", "installsrv", 'wizard-mdk', N("Configure installation server") ], ), ), ] ], [ N("Network Services"), 'wizard-mdk', [ (wizard_format(# [ id, wizard file name, icon, description ] [ "DHCP wizard", "dhcp", 'dhcp_server-mdk', N("Configure DHCP") ], [ "DNS wizard", "bind", 'dns_server-mdk', N("Configure DNS") ], [ "Squid wizard", "squid", 'drakproxy-mdk', N("Configure proxy") ], [ "Time wizard", "ntp", 'ntp_server-mdk', N("Configure time") ], ), ), ] ], [ N("Authentication"), 'drakauth-mdk', [ "Authentication", (wizard_format(# [ id, wizard file name, icon, description ] [ 'Nis+autofs wizard', "nisautofs", 'nisautofs', N("Configure NIS and Autofs") ], [ "LDAP wizard", "ldap", "ldap-mdk", N("Configure LDAP") ], ), ), ] ], [ N("Groupware"), 'wizard-mdk', [ (wizard_format(# [ id, wizard file name, icon, description ] [ "News wizard", "inn", 'news-mdk', N("Configure news") ], [ "Kolab wizard", "kolab", "kolab-mdk", N("Configure groupware") ], [ "Postfix wizard", "postfix", 'postfix-mdk', N("Configure mail") ], ), ), ] ], ), if_($isWebAdmin || $isRfbDrake, [ N("Online Administration"), 'online-administration-mdk', [ if_($isWebAdmin, (map { my ($id, $icon, $op, $description) = @$_; $programs{$id} = { binary => "$bindir/mdkwebadmin.pl $op", embedded => -1, # too big description => $description, icon => $icon, }; $id; } (# [ id, wizard file name, icon, description ] [ "Local Admin", 'local-administration-mdk', '--direct', N("Local administration") ], [ "Remote Admin", 'remote-administration-mdk', '--link', N("Remote administration") ]) ) ), if_($isRfbDrake, "Remote Control", ) ] ]), [ N("Hardware"), 'drakhard-mdk', [ "Hardware List",, "Monitor", "Resolution", if_(0, "Graphical server configuration"), if_(-x "$bindir/drakfax", "Fax"), "TV Cards", "Keyboard", "Mouse", "Printer", "Scanner", "UPS", ] ], [ N("Network & Internet"), 'net-mdk', [ "Add Connection", "Configure Internet", "Manage Connection", "Monitor Connection", "Remove Interface", "Proxy Configuration", "Connection Sharing", ], ], [ N("System"), 'system-mdk', [ if_($isWiz, "Authentication"), "Menus", "Display Manager chooser", "Services", "Fonts", "Date & Time", "Logs", if_($ENV{LANGUAGE} !~ /^zh/, "Console"), "Users", if_(-x "/usr/X11R6/bin/drakcronat", "Programs scheduling"), "Backups", ] ], [ N("Mount Points"), 'partition-mdk', [ "Hard Drives", (map { my ($type, $scan, $text) = @$_; map_index { my $full_name = $text . ($::i ? $::i + 1 : ''); $programs{$full_name} = { binary => "$sbindir/diskdrake --removable=$_->{device}", embedded => 1, description => $text, icon => "diskdrake_$type", }; $full_name, } $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', sub { @{$cdroms_by_type{cdrom} || []} }, N("CD-ROM") ], [ 'dvd', sub { @{$cdroms_by_type{DVD} || []} }, N("DVD-ROM") ], [ 'cdwriter', sub { @{$cdroms_by_type{burner} || []} }, N("CD/DVD burner") ], [ 'floppy', \&detect_devices::floppies, N("Floppy drive") ], [ 'zip', \&detect_devices::zips, N("ZIP drive") ], ), }), "NFS mount points",, "Samba mount points", "WebDAV mount points", "Partition Sharing", ] ], [ N("Security"), 'security-mdk', [ "Security Level", "Security Permissions", "Firewall", ] ], [ N("Boot"), 'boot-mdk', [ if_(0 && detect_devices::floppies, "Boot Disk"), "Auto login Config", "Boot Config", "Boot Theme", if_(0, "Auto Install"), ] ], ); #------------------------------------------------------------- # let build the GUI # main window : my ($timeout, %check_boxes, $emb_socket, $page_id); my $window_global = gtkset_size_request(Gtk2::Window->new('toplevel'), 620, 460); if ($geometry) { my ($w, $h) = $geometry =~ /(\d+)x(\d+)/; my ($x, $y) = $geometry =~ /([+-]\d+)([+-]\d+)/; $window_global->resize(max($w, $default_width), max($default_heigth, $h)); $window_global->set_uposition($x, $y) if $x || $y; } else { $window_global->resize(max($default_width, $h{WIDTH}), max($h{HEIGTH}, $default_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; start_logdrake(); }, undef, '' ], if_(0 && $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); 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); # 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::read_net_conf($netcnx, $netc, $intf); @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::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{$_}})); } ("show_log", if_(0 && $isWiz, "wiz_expert")); gtkadd($window_global, gtkpack_(Gtk2::VBox->new(0, 0), 0, $menu, 0, my $banner_notebook = Gtk2::Notebook->new, 0, Gtk2::HSeparator->new, # 0, gtkset_size_request(Gtk2::VBox->new(10, 10), -1, 2), 1, gtkpack_(Gtk2::HBox->new(0, 0), 1, my $notebook_global = gtkset_name(Gtk2::Notebook->new, 'mcc'), 1, gtkpack(my $emb_box = Gtk2::VBox->new(0, 0), my $wait_darea = gtkset_size_request(Gtk2::DrawingArea->new, -1, -1), ), ), 0, Gtk2::HSeparator->new, 0, my $buttons = gtkadd(gtkset_layout(Gtk2::HButtonBox->new, 'start'), map { gtkset_border_width($_, 3) } gtksignal_connect(my $cancel = Gtk2::Button->new(N("Cancel")), clicked => sub { stop_wait_area(); kill_children(); child_just_exited(); }), gtkset_sensitive(gtkset_relief(Gtk2::Button->new(""), 'none'), 0), ), ) ); sub notebook_set_background { my ($w) = @_; return if !$w->realized; my $color = $window_global->get_style->base('normal'); Gtk2::Rc->parse_string(join('', qq(style "mcc" { bg[NORMAL] = { ), join(' ,', $color->red, $color->green, $color->blue), qq( } bg_pixmap[NORMAL] = "" } widget "*mcc*" style "mcc" ))); $notebook_global->reset_rc_styles; } $notebook_global->signal_connect(realize => \¬ebook_set_background); $window_global->signal_connect("style-set" => sub { notebook_set_background($notebook_global) }); $window_global->signal_connect(delete_event => \&quit_global); use POSIX qw(:sys_utsname_h :math_h :sys_wait_h :unistd_h); my (undef, $nodename) = POSIX::uname(); $window_global->set_title(N("Mandrakelinux Control Center %s [on %s]", $version, $nodename)); $window_global->set_position('center'); foreach my $notebook ($banner_notebook) { $notebook->set_property('show-border', 0); $notebook->set_property('show-tabs', 0); } $notebook_global->set_property('tab-pos', 'left'); $notebook_global->set_scrollable(1); # banner : add2notebook($banner_notebook, "", Gtk2::Banner->new("/usr/share/icons/large/drakconf.png", N("Welcome to the Mandrakelinux Control Center"))); my ($hand_cursor, $normal_cursor, $wait_cursor) = map { Gtk2::Gdk::Cursor->new($_) } qw(hand2 left-ptr watch); my ($index, $left_locked) = (0, 0); my $spacing = 25; my %tool_callbacks; foreach (@tree) { my ($text, $icon, $subtree) = @$_; my @subtree; foreach my $stuff (@$subtree) { my $exec = first(split /\s+/, $programs{$stuff}{binary}); # do not complain about missing entries in move: if (-x $exec) { push @subtree, $stuff; } else { warn qq("$exec" is not executable [$stuff]) if $mdk_rel !~ /Move/; } } # Skip empty classes: next if !@subtree; my $my_index = $index++; my $box; $box = Gtk2::WebIcon->new($text, $icon, { button_release_event => sub { # FIXME: the following code is currently useless: # should we provide a way to kill buggy embedded programs ? return if $left_locked; $page_id = $my_index + 1; warn_on_startup(); }, } ); # Create right notebook pages : my $tbl = create_packtable({ col_spacings => $spacing, row_spacings => $spacing, homogeneous => 1, mcc => 1 }, group_by3(map { my $label = $_; my ($icon, $text) = @{$programs{$label}}{qw(icon description)}; die "$label 's icon is missing" if !$programs{$label} && $::testing; my $event_box; $tool_callbacks{$label} = sub { # needed for --start-with parameter: $event_box->realize; compute_exec_string($label, $event_box, $icon, $text, $programs{$label}); }; $event_box = Gtk2::WebIcon->new($text, $icon, { button_release_event => $tool_callbacks{$label}, } ); $event_box->set_events([ 'enter_notify_mask', 'leave_notify_mask', 'button_press_mask', 'button_release_mask' ]); $tool_feedback{$label} = sub { $event_box->window && $event_box->window->set_cursor($hand_cursor) }; $event_box; } @subtree)); # b/c we use Pango text markup language, we've to protect '&' caracter: $text =~ s/&/&/g; gtkappend_page($notebook_global, my $_w_ret = create_scrolled_window(gtkset_border_width($tbl, 5), [ 'never', 'automatic' ], 'none', ), gtkpack__(Gtk2::HBox->new, Gtk2::Image->new_from_pixbuf(::scale(gtkcreate_pixbuf($icon), -16)), gtkset_markup(Gtk2::WrappedLabel->new, '' . $text . ''), ), ); # $w_ret->vscrollbar->set_size_request(19, undef); add2notebook($banner_notebook, "", Gtk2::Banner->new($icon, $text)); } my %icons = ( "Localization" => 'localedrake' ); # manage tools not present in MCC (localedrake, drakauth, ...): foreach my $label (difference2([ keys %programs ], [ keys %tool_callbacks ])) { my $text = $programs{$label}{description}; $tool_callbacks{$label} = sub { compute_exec_string($label, undef, $icons{$label} || 'wizard-mdk', $text, $programs{$label}) }; $tool_feedback{$label} = sub {}; } foreach (keys %check_boxes) { my $widget = $check_boxes{$_}; if (defined $widget) { $widget->set_active($option_values{$_}); } else { print STDERR qq(BUG with LANGUAGE "$ENV{LANGUAGE}" for "$_"\n); } } # "wait while launching a program" area : my ($run_pixbuf, $run_counter, $run_counter_add, %run_pixbufs); sub stop_wait_area() { $wait_darea->hide; Glib::Source->remove($timeout) if $timeout; undef %run_pixbufs; } $wait_darea->signal_connect(expose_event => sub { my ($w) = @_; return if !$wait_darea->realized; return unless $run_pixbuf; # some people got an expose event before we start an embedded tool return unless $wait_darea->{size}; $wait_darea->{layout} ||= $wait_darea->create_pango_layout(N("Loading... Please wait")); my $pixbuf = $run_pixbufs{$run_counter} ||= render_alpha($run_pixbuf, $run_counter); if (!$wait_darea->{first_expose}) { $wait_darea->{first_expose} = 1; my $size = $wait_darea->{size}; my ($d_width, $d_height) = ($size->width, $size->height); ($w->{pix_width}, $w->{pix_height}) = ($pixbuf->get_width, $pixbuf->get_height); my ($txt_width, $txt_height) = $wait_darea->{layout}->get_pixel_size; $w->{pix_yy} = ($d_width - $w->{pix_width})/2; $w->{pix_xx} = ($d_height - $w->{pix_height} - $txt_height)/2; $w->{text_yy} = $w->{pix_yy} + ($w->{pix_width} - $txt_width)/2; $w->{text_xx} = $w->{pix_xx} + $w->{pix_height} + 5; } $pixbuf->render_to_drawable($w->window, $w->style->fg_gc('normal'), 0, 0, $w->{pix_yy}, $w->{pix_xx}, $w->{pix_width}, $w->{pix_height}, 'normal', 0, 0); my $style = $wait_darea->{style} ||= $wait_darea->get_style; $wait_darea->window->draw_layout($style->text_gc('normal'), $w->{text_yy}, $w->{text_xx}, $w->{layout}); $run_counter += $run_counter_add; $run_counter_add = -$run_counter_add if $run_counter < 100 || 255-$run_counter_add < $run_counter; }); gtkflush(); set_page_raw(0); $notebook_global->signal_connect(switch_page => sub { my $tab_number = $_[2]; return unless $tab_number > 0; }); $window_global->show_all; hide_buttons(); $emb_box->hide; $wait_darea->realize; $SIG{USR1} = 'IGNORE'; $SIG{USR2} = 'IGNORE'; $SIG{TERM} = \&quit_global; $SIG{CHLD} = \&sig_child; $window_splash->destroy; undef $window_splash; Glib::Timeout->add(200, sub { sig_child(1); 1 }); if ($program) { if (my $sub = $tool_callbacks{$program}) { Glib::Timeout->add(500, sub { $sub->(); 0 }); } else { err_dialog(N("Error"), N("Impossible to run unknown '%s' program", $program)); } } Gtk2->main; sub group_by3 { my @l; for (my $i = 0; $i < @_; $i += 3) { push @l, [ $_[$i], $_[$i+1], $_[$i+2] ]; } @l; } sub warn_on_startup { 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(); } set_page($page_id); } #------------------------------------------------------------- # socket/plug managment # called once embedded tool has exited sub child_just_exited() { $pending_app = 0; $left_locked = 0; if ($emb_socket) { $emb_socket->destroy; undef $emb_socket; } $emb_box->hide; hide_buttons(); $cancel->hide; gtkset_mousecursor_normal(); foreach my $notebook ($notebook_global, $banner_notebook) { $notebook->show; } stop_wait_area(); } sub hide_socket_and_clean() { $emb_box->hide; $pending_app = 0; } sub create_hidden_socket { my ($icon, $label) = @_; my $banner; gtkpack_($emb_box, 0, $banner = gtkpack__(Gtk2::VBox->new(0, 0), Gtk2::Banner->new($icon, $label), Gtk2::HSeparator->new, ), 1, gtksignal_connect($emb_socket = Gtk2::Socket->new, 'plug-removed' => sub { $menu->show if $application_driven_menu; $banner->destroy; child_just_exited(); })); $banner->hide; # signal emitted when embedded apps begin to draw: $emb_socket->signal_connect('plug-added' => sub { $banner_notebook->hide; if ($application_driven_menu) { $menu->hide; } else { $banner->show; } stop_wait_area(); $left_locked = 0; hide_buttons(); $buttons->hide; return if !$emb_socket; $emb_socket->show; $emb_socket->can_focus(1); $emb_socket->grab_focus; #$emb_socket->window->XSetInputFocus; #only need by console (no more embedded until we've vte/zvt binding) }); $emb_box->set_focus_child($emb_socket); $emb_socket->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; return $pid; } else { splash_warning(N("cannot fork: %s", "$!")); child_just_exited(); } } sub compute_exec_string { my ($label, $box, $icon, $text, $tool) = @_; my ($exec, $gtkplug) = @$tool{qw(binary embedded)}; $application_driven_menu = $tool->{application_driven_menu}; return if $tool_pids{$label}; 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 = $gtkplug != -1; # not "explicitely not embedded" if ($embedded) { foreach my $notebook ($notebook_global) { $notebook->hide; } create_hidden_socket($icon, $text); $emb_box->show; $emb_socket->realize; $pending_app = 1; if ($gtkplug > 0) { $buttons->show; $exec .= " --embedded " . $emb_socket->get_id; $wait_darea->show; $cancel->show; $run_pixbuf = gtkcreate_pixbuf($icon . "_128"); $run_counter = 255; $run_counter_add = -5; undef $wait_darea->{color}; undef $wait_darea->{size}; undef $wait_darea->{first_expose}; $wait_darea->{not_yet_complete} = 1; $timeout = Glib::Timeout->add(35, sub { my $size = $wait_darea->{size} ||= $wait_darea->allocation; my ($width, $height) = ($size->width, $size->height); my $style = $wait_darea->{style} ||= $wait_darea->get_style; my $bg = $wait_darea->{bg} ||= $style->bg('normal'); my $base = $wait_darea->{base} ||= $style->base('normal'); my $color = $wait_darea->{color} ||= $base; # $base -> $bg my @colors = ([ $color->red, $bg->red ], [ $color->green, $bg->green ], [ $color->blue, $bg->blue ]); if ($wait_darea->{not_yet_complete} && any { $_->[0] != $_->[1] } @colors) { $wait_darea->window->set_background($color); $wait_darea->{color} = gtkcolor(map { $_->[0] - min(300, $_->[0] - $_->[1]) } @colors); $wait_darea->queue_draw; } elsif ($wait_darea->{not_yet_complete}) { # set exact color $wait_darea->window->set_background($bg); $wait_darea->queue_draw; $wait_darea->{not_yet_complete} = 0; } else { my $w = $wait_darea; $wait_darea->queue_draw_area($w->{pix_yy}, $w->{pix_xx}, $w->{pix_width}, $w->{pix_height}); } 1; }); $left_locked = 1; $buttons->show; $tool_pids{$label} = fork_($exec); } } else { # not embedded # fix #3415 when $gtkplug eq -1 my $old = $option_values{embedded}; $option_values{embedded} = 0; $tool_pids{$label} = fork_($exec); $option_values{embedded} = $old; } start_logdrake(); $box->window->set_cursor($wait_cursor) if $box; } sub start_logdrake { # (re)start logdrake if needed if ($option_values{show_log} && !$pid_exp) { my $exec_log = "logdrake --explain=drakxtools"; $pid_exp = fork_($exec_log, []); } } 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() { if (@pid_launched) { &kill_children(); return 1; # tell gtk+ not to quit } &kill_children(); &kill_logdrake(); my ($x, $y) = $window_global->get_size; setVarsInSh($conffile, { 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 ($o_is_cleaner) = @_; my $child_pid; do { $child_pid = waitpid(-1, POSIX::WNOHANG); if (my $tool = find { $tool_pids{$_} eq $child_pid } keys %tool_pids) { $tool_feedback{$tool}->(); delete $tool_pids{$tool}; @pid_launched = grep { $_ ne $child_pid } @pid_launched; } 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 if $o_is_cleaner || !$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 - Mandrakelinux 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)") ], [ '', 'Damien "dam\'s" Krotkine', #-PO "perl" here is the programming language 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 (grep { $_ ne "\n" } cat_(top(glob("/usr/share/doc/mandrakelinux-release-*/CREDITS")))) { if ($line =~ /^\s*\* (.*)$/) { my ($contributor, $descr) = split(/:\s*/, $1, 2); chomp($descr); $descr =~ s/(["\@\$])/\\$1/g; $credits_model->append_set(undef, map_index { $::i => $_ } map { join("\n", warp_text(translate(c::to_utf8($_)), 30)) } $contributor, $descr); } else { chomp($line); $credits_model->append_set(undef, 0 => translate(common::sprintf_fixutf8($line))); } } # 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("_: NAME OF TRANSLATORS\nYour names"); #-PO Add your E-Mail address here if you want to show it in the about doialog. my $translator_email = N("_: EMAIL OF TRANSLATORS\nYour emails"); if ($translator_name ne "Your names" && 0) { $tree_model->append_set(undef, [ 0 => $_->[0], 1 => $_->[1] ]) foreach [ '', '' ], [ N("Translator: "), $translator_name, $translator_email ]; } $list->get_selection->set_mode('none'); gtkpack_($window_about->vbox, (0, gtkset_size_request(Gtk2::Banner->new('splash_screen_about', N("Mandrakelinux Control Center"), { txt_ypos => 50 }), -1, 120)), 0, Gtk2::Label->new(""), 0, Gtk2::Label->new(N("Copyright (C) 1999-2004 Mandrakesoft SA") . "\n"), 0, Gtk2::HSeparator->new, 0, Gtk2::Label->new(""), 1, my $n = Gtk2::Notebook->new, ); add2notebook($n, N("Authors"), $list); add2notebook($n, N("Mandrakelinux Contributors"), create_scrolled_window(gtkset_size_request($clist, 50, 50))); $window_about->show_all; } #------------------------------------------------------------- # mcc specific graphic functions: sub set_page_raw { my ($bool) = @_; foreach my $notebook ($notebook_global, $banner_notebook) { $notebook->set_current_page($bool); } } sub set_page { my ($index) = @_; start_logdrake(); set_page_raw($index); $help_on_context = $ctx[$index]; hide_buttons(); } sub hide_buttons { $buttons->hide; $cancel->hide; } #------------------------------------------------------------- # 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::WebIcon; use ugtk2 qw(:helpers :wrappers); use common; sub new { my ($_class, $text, $icon, $callbacks) = @_; my $hbox_spacing = 10; # FIXME: do ->set_pixbuf() on {enter,leave}_events my $box = gtkadd(gtksignal_connect(Gtk2::EventBox->new, realize => sub { $_[0]->window->set_cursor($hand_cursor) }), gtkpack_( # better VBox of StockButtons there (with IconFactory) Gtk2::VBox->new(0, $hbox_spacing), 0, gtkcreate_img($icon), 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, justification => "center", 'background_stipple_set' => 0 } ] ]) ), ); $tv->set_events([]); # FIXME : resize sig: ->foreach; set_size_request $callbacks->{key_press_event} = sub { my (undef, $event) = @_; return if !$callbacks->{button_release_event}; $callbacks->{button_release_event}->() if $Gtk2::Gdk::Keysyms{Return} eq $event->keyval; }; while (my ($signal, $handler) = each %$callbacks) { $box->signal_connect($signal => $handler); } gtkset_size_request($box, 50, -1); $box->set_events([ 'enter_notify_mask', 'leave_notify_mask', 'button_press_mask', 'button_release_mask', 'key-press-mask' ]); return gtkset_border_width($box, 10); } 1;