#!/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; # 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 #------------------------------------------------------------- # 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; 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(); #------------------------------------------------------------- # we'll show log only once and don't restart it with new embedded process my $freeze_log = 1; my $still_in_splash_screen = 1; #------------------------------------------------------------- # Data structures $::isWiz = -e "/usr/sbin/drakwizard"; $::isRpmDrake = -e "/usr/sbin/rpmdrake"; # { 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("DrakAutoInst helps you produce an Auto Install floppy") ], "Backups" => [ "drakbackup", "$sbindir/drakbackup", 1, N("DrakBackup helps you configure backups") ], "Boot Config" => [ "drakboot", "$sbindir/drakboot", 1, N("DrakBoot helps you set up how your system boots") ], "Boot Disk" => [ "drakfloppy", "$sbindir/drakfloppy", 1, N("DrakFloppy helps you produce your own boot floppy") ], "Connection Sharing" => [ "drakgw", "$sbindir/drakgw", 1, N("DrakGw helps you share your Internet connection") ], "Connection" => [ "drakconnect", "$sbindir/drakconnect", 1, N("DrakConnect helps you set up your network and Internet connection") ], # little workaround to avoid drakconf freeze "Console" => [ "rxvt", "$xbindir/rxvt", -1, N("Open a console") ], #The Console will help you to solve issues # "Console" => [ "rxvt", [ "$xbindir/rxvt", "rxvt", 1 ], 0, N("Open a console") ], #The Console will help you to solve issues "Date & Time" => [ "clock", "$sbindir/clock.pl", 1, N("Set date and time") ], "Display Manager chooser" => [ "drakedm", "$sbindir/drakedm", 1, N("Choose the display manager") ], "Firewall" => [ "drakfirewall", "$sbindir/drakfirewall", 1, N("DrakFirewall helps you set up a personal firewall") ], "Fonts" => [ "drakfont", "$sbindir/drakfont", 1, N("DrakFont helps you add and remove fonts, including Windows fonts") ], "Graphical server configuration" => [ "XFdrake", "$sbindir/XFdrake", 1, N("XFdrake helps you set up the graphical server") ], "Hard Drives" => [ "diskdrake", "$sbindir/diskdrake --hd", 1, N("DiskDrake helps you define and resize hard disk partitions") ], "Hardware List" => [ "harddrake", "$sbindir/harddrake2", 1, N("HardDrake lists and helps you set up your hardware") ], "Install Software" => [ "rpmdrake", "$sbindir/rpmdrake", -1, N("RpmDrake helps you install software packages") ], "Keyboard" => [ "keyboarddrake", "$sbindir/keyboarddrake", 1, N("KeyboardDrake helps you to set your keyboard layout") ], "Logs" => [ "logdrake", "$sbindir/logdrake", 1, N("LogDrake helps you view and search system logs") ], "Mandrake Update" => [ "rpmdrake", "$sbindir/MandrakeUpdate", -1, N("Mandrake Update helps you apply any fixes or upgrades to installed packages") ], "Menus" => [ "menudrake", "$bindir/menudrake", -1, N("MenuDrake helps you change what programs are shown on the menu"), "$bindir/menudrake" ], "Monitor" => [ "XFdrake", "$sbindir/XFdrake monitor", 1, N("Configure your monitor") ], "Mouse" => [ "mousedrake", "$sbindir/mousedrake", 1, N("MouseDrake helps you set up your mouse") ], "NFS mount points" => [ "diskdrake", "$sbindir/diskdrake --nfs", 1, N("Set NFS mount points") ], "Partition Sharing" => [ "diskdrake", "$sbindir/diskdrake --fileshare", 1, N("Set up sharing of your hard disk partitions") ], "Printer" => [ "printerdrake", "$sbindir/printerdrake", -1, N("PrinterDrake helps you set up your printer, job queues ...."), "$sbindir/printerdrake" ], "Programs scheduling" => [ "drakcronat", "/usr/X11R6/bin/drakcronat", 1, N("DrakCronAt helps you run programs or scripts at certain times") ], #DrakCronAt enables to schedule Programs execution through crond and atd daemons "Proxy Configuration" => [ "drakproxy", "$sbindir/drakproxy", 1, N("DrakProxy helps you set up proxy servers") ], #for files and web browsing "Remove Software" => [ "rpmdrake", "$sbindir/rpmdrake-remove", -1, N("RpmDrake helps you remove software packages") ], "Resolution" => [ "XFdrake", "$sbindir/XFdrake resolution", 1, N("Change your screen resolution") ], "Samba mount points" => [ "diskdrake", "$sbindir/diskdrake --smb", 1, N("Set Samba mount points") ], "Scanner" => [ "scannerdrake", "$sbindir/scannerdrake", 1, N("ScannerDrake helps you set up your scanner") ], "Security Level" => [ "draksec", "$sbindir/draksec", 1, N("DrakSec helps you set the system security level") ], "Security Permissions" => [ "drakperm", "$sbindir/drakperm", 1, N("DrakPerm helps you fine-tune the system security level and permissions") ], "Services" => [ "drakxservices", "$sbindir/drakxservices", 1, N("DrakXServices helps you enable or disable services") ], "Software Media Manager" => [ "rpmdrake", "$sbindir/edit-urpm-sources.pl", -1, N("Software Media Manager helps you define where software packages are downloaded from") ], "TV Cards" => [ "drakxtv", "$sbindir/drakxtv", 1, N("DrakxTV helps you set up your TV card") ], "Users" => [ "userdrake", "$bindir/userdrake", -1, N("UserDrake helps you add, remove or change users of your system") ], # too big "WebDAV mount points" => [ "diskdrake", "$sbindir/diskdrake --dav", 1, N("Set 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("Set where your CD-ROM drive is mounted") ], [ 'dvd', N("DVD"), sub { @{$cdroms_by_type{DVD} || []} }, N("Set where your DVD-ROM drive is mounted") ], [ 'cdwriter', N("CD Burner"), sub { @{$cdroms_by_type{burner} || []} }, N("Set where your CD/DVD burner is mounted") ], [ 'floppy', N("Floppy"), \&detect_devices::floppies, N("Set where your floppy drive is mounted") ], [ 'zip', N("Zip"), \&detect_devices::zips, N("Set where your ZIP drive is mounted") ], ), }), [ "NFS mount points", 'diskdrake_nfs', ], [ "Samba mount points", 'diskdrake_samba', ], [ "WebDAV mount points", 'webdav-mdk', ], [ "Partition Sharing", 'diskdrake_fileshare', ], ] ], [ N("Network & Internet"), 'net-mdk', [ [ "Connection", '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 Configuration"), '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("The DHCP wizard will help you configuring the DHCP services of your server") ], [ "DNS Client wizard", "bind_client", 'dns_client-mdk', N("The DNS Client wizard will help you in adding a new client in your local DNS") ], [ "DNS wizard", "bind", 'dns_server-mdk', N("The DNS wizard will help you configuring the DNS services of your server.") ], [ "FTP wizard", "proftpd", 'ftp-mdk', N("The FTP wizard will help you configuring the FTP Server for your network") ], [ "News wizard", "inn", 'news-mdk', N("The News wizard will help you configuring the Internet News services for your network") ], [ "Postfix wizard", "postfix", 'postfix-mdk', N("The Postfix wizard will help you configuring the Internet Mail services for your network") ], [ "Squid wizard", "squid", 'drakproxy-mdk.png', N("The Proxy wizard will help you configuring a web caching proxy server") ], [ "Samba wizard", "samba", 'samba_server-mdk', N("The Samba wizard will help you configuring your server to behave as a file and print server for workstations running non-Linux systems") ], [ "Time wizard", "ntp", 'ntp_server-mdk', N("The Time wizard will help you to set the time of your server synchronized with an external time server") ], [ "Web wizard", "apache2", 'web_server-mdk', N("The Web wizard will help you configuring the Web Server for your network") ] ) ) ] ]), ); #------------------------------------------------------------- # let build the GUI # main window : my ($global_width, $global_height) = (720, 523); my ($timeout, %check_boxes, $exp_frame, $emb_socket); my $window_global = gtkset_size_request(Gtk2::Window->new('toplevel'), $global_width, $global_height); 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); #-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 @menu_items = ( [ N("/_File"), undef, undef, undef, '' ], [ N("/_File") . N("/_Quit"), N("Q"), \&quit_global, undef, '', 'gtk-quit' ], [ N("/_Options"), undef, undef, undef, '' ], [ join('', @{$options{show_log}}), undef, sub { $option_values{show_log} = $check_boxes{show_log}->get_active; 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_(all($themes_dir) > 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"; }; }, '' ] } grep { -d "$themes_dir/$_" } all($themes_dir)), [ N("/_Themes").N("/_More themes"), undef, \&more_themes, undef, '' ] ), [ N("/_Help"), undef, undef, undef, '' ], [ N("/_Help").N("/_Help"), undef, sub { fork_("drakhelp --id $help_on_context") }, undef, '', 'gtk-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); %check_boxes = map { $_ => $factory->get_widget("
" . join('', map { s/_//; $_ } @{$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, gtksignal_connect(gtkset_size_request(Gtk2::DrawingArea->new, -1, -1), realize => sub { set_back_pixbuf($_[0], rtl_gtkcreate_pixbuf('mcc-left-back-middle')) }), 0, rtl_gtkcreate_img('mcc-left-back-bottom') ), 1, gtkpack_(Gtk2::VBox->new(0, 0), 0, gtksignal_connect(gtkset_size_request(Gtk2::DrawingArea->new, -1, 1), realize => sub { set_back_pixbuf($_[0], gtkcreate_pixbuf('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, gtkadd(gtkset_shadow_type(Gtk2::Frame->new, 'etched_out'), 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('gtk-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, gtksignal_connect(gtkset_size_request(Gtk2::DrawingArea->new, -1, 1), realize => sub { set_back_pixbuf($_[0], gtkcreate_pixbuf('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); # Create left icons foreach (@tree) { my ($text, $icon, $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, @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; # 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]); ($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]; my $offset = -($d_height - string_height($darea_left, $lines->[0]) * (listlength(@$lines) + 0.2)) / 2; # text lines are offsetted 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 => sub { $darea_left->window->set_cursor($cursor_hand) }); $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; $darea_left_list[$index_sav]->window->set_cursor($cursor_hand); $darea_left_list[$index_sav]->queue_draw; } else { $still_in_splash_screen = 0 } $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 $event_box = gtkadd(Gtk2::EventBox->new, gtkcreate_img($tag)); $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 => sub { $event_box->window->set_cursor($cursor_hand) }); $event_box->signal_connect(button_release_event => sub { compute_exec_string($tag, @{$exec_hash->{$label}}) }); # FIXME : resize sig: ->foreach; set_size_request my $hbox_spacing = 10; gtkset_size_request(gtkpack_(Gtk2::HBox->new(0, $hbox_spacing), 0, $event_box, 1, gtktext_insert(Gtk2::TextView->new, [ [ $text, {'background_set' => 0, 'background_stipple_set' => 0 } ] ]) ), 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 # when child properly exited sub child_just_exited() { $pending_app = 0; $left_locked = 0; clean_socket(); gtkset_mousecursor_normal(); $notebook_global->show; Glib::Source->remove($timeout) if $timeout; undef $emb_socket; } sub hide_socket_and_clean() { $emb_frame->hide; $pending_app = 0; update_exp(); } sub clean_socket() { hide_socket_and_clean(); &kill_children(); undef $emb_socket; } sub create_hidden_socket_if_needed() { hide_socket_and_clean(); # clean_socket(); unless ($emb_socket) { gtkpack_($emb_box, 1, gtksignal_connect($emb_socket = Gtk2::Socket->new, 'plug-removed' => \&child_just_exited)); # emitted when embedded apps begin to draw $emb_socket->signal_connect('plug-added' => sub { $left_locked = 0; $emb_wait->hide; $emb_socket->show if $emb_socket; $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_in_splash_screen || $pending_app; if ($option_values{show_log}) { $w_exp->show_all; } else { $w_exp->hide; } } #------------------------------------------------------------- # signals managment # got when child died and gone in zombie state sub sig_child() { # reap zombies my $kid; do { $kid = waitpid(-1, POSIX::WNOHANG) } until $kid > 0; # child unexpectedly died: return unless $left_locked; child_just_exited(); splash_warning(N("This program has exited abnormally")); } #------------------------------------------------------------- # 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}; } 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/; if ($option_values{embedded} && $gtkplug != -1) { # globally embedded and not "explicitely not embedded" $notebook_global->hide; create_hidden_socket_if_needed(); $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); # } # } # } # (re)start logdrake if needed if ($option_values{show_log} && $freeze_log) { #FIXME && !$exp_socket my $exp_socket; gtkshow(gtkadd($exp_frame, $exp_socket = Gtk2::Socket->new)); $w_exp->show; my $exec_log = "logdrake --explain=$log_exp --embedded " . $exp_socket->window->XWINDOW; $freeze_log = 0; fork_($exec_log, \@pid_exp); } $w_exp->hide if $exec =~ /(drakfont|drakconnect|logdrake)/; } 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); } sub quit_global() { &kill_children(); &kill_logdrake(); setVarsInSh($conffile, { EMBEDDED => bool2text($option_values{embedded}), LOGS => bool2text($option_values{show_log}), EXPERT_WIZARD => bool2text($option_values{expert_wizard}), THEME => $theme, }); gtkset_mousecursor_normal(); Gtk2::exit(0); } #------------------------------------------------------------- # 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_from_stock('gtk-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 => $_ } 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) = @_; update_exp(); $notebook_global->set_current_page($index); $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'); }