diff options
Diffstat (limited to 'control-center')
-rwxr-xr-x | control-center | 1555 |
1 files changed, 1555 insertions, 0 deletions
diff --git a/control-center b/control-center new file mode 100755 index 00000000..c5eb5499 --- /dev/null +++ b/control-center @@ -0,0 +1,1555 @@ +#!/usr/bin/perl +# $Id: control-center 269021 2010-05-19 16:37:57Z eugeni $ + +# Copyright (C) 1999-2008 Mandriva +# Daouda Lo +# Damien Krotkine +# Thierry Vignaud <tvignaud@mandriva.com> +# 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; + +# i18n: IMPORTANT: to get correct namespace (drakconf instead of libDrakX) +BEGIN { unshift @::textdomains, 'drakconf' } +use mygtk2 qw(gtknew); +use ugtk2 qw(:create :dialogs :helpers :wrappers); +use Gtk2::WebKit; + +use MDV::Control_Center; + + +my (%tool_pids, %tool_feedback, $gurpmi_pid); + +my ($conffile, $class_install) = ('/etc/mcc.conf', '/etc/sysconfig/system'); + +my ($rootwin_width, $rootwin_height) = gtkroot()->get_size; +my $default_width = $rootwin_width <= 800 ? 720 : 800; +my $default_height = $rootwin_height <= 480 ? 420 : $rootwin_height <= 600 ? 523 : 600; + +my $min_width = $rootwin_width == 640 ? 620 : 680; +my $min_height = $rootwin_height == 480 ? 420 : 500; + +require_root_capability() if !$::testing; # 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_height; +$h{WIDTH} ||= $default_width; + +my %option_values; +$option_values{show_log} = text2bool($h{LOGS}); +my $theme = $h{THEME}; +my $program; +my ($i, $geometry, $save_html); +foreach (@ARGV) { + $i++; + $theme = $1 if /--theme (\w+)/; + $program = $1 if /--start-with=(.*)/; + if (/^--geometry$/) { + $geometry = splice @ARGV, $i, 1; + last; + } + $save_html = 1 if /--save-html-pages/; +} + +-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 + +mygtk2::import_style_ressources(); +my $rc = find { -r $_ } ("$themes_dir/$theme/gtkrc", if_($theme ne 'default', "$themes_dir/default/gtkrc")); +Gtk2::Rc->parse($rc) if -r $rc; + +my $branding = N("Mageia"); +my $product_id = common::parse_LDAP_namespace_structure(cat_('/etc/product.id')); +# allow OEM branding: +$branding = translate($product_id->{distribution}); + + +#------------------------------------------------------------- +# Splash window: please wait ... +my $window_splash = Gtk2::Window->new('popup'); +$window_splash->signal_connect(delete_event => \&quit_global); +$window_splash->set_title(N("%s Control Center", $branding)); +$window_splash->set_position('center_always'); +$window_splash->add(gtknew('Fixed', widget_name => 'Steps', + children => [ + [ gtknew('Image_using_pixbuf', file => 'splash_screen'), 0, 0 ], + [ gtknew('Label', text => N("%s Control Center", $branding)), 21, 60 ], + [ gtknew('Label', text => N("Loading... Please wait")), 21, 175 ], + ]), + ); +$window_splash->show_all; +gtkflush(); + + +#------------------------------------------------------------- +# Data structures +my $more_wizard_dir = "/etc/wizard.d/"; + +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/bin/rfbdrake"; +my $isDrakStats = -e "/usr/sbin/drakstats"; + +my $application_driven_menu; + +sub is_wizard_installed { -f top(glob("/usr/lib/perl5/vendor_perl/*/MDK/Wizard/$_[0]")) } + +my $is_auth_wizard_installed = is_wizard_installed('Nisautofs.pm'); + + +sub wizard_format { + map { + my ($id, $wizard, $icon, $description, $long_description, $file) = @$_; + $programs{$id} = + { + binary => "$sbindir/drakwizard $wizard", + embedded => 1, + description => $description, + long_description => $long_description, + icon => $icon, + }; + if_(is_wizard_installed($file), $id); + } @_; +} + +# [ [ class_label, class icon name, [ program_label, ... ] ] +my @tree = ( + if_($isRpmDrake || $isParkRpmDrake, + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Software Management"), 'software', 'software-management', + [ + if_($isRpmDrake, + { + title => N("Software Management"), + list => [ + "Install Software", + "Mageia Update", + if_(0, "Mageia Online"), + "Updates Configuration", + "Software Media Manager", + ] + }, + ), + { + title => N("Others"), + list => [ + if_($isParkRpmDrake, "Manage park"), + if_($isDrakStats, "Package stats"), + ] + }, + ] + ] + ), + + if_(0, [ N("Server wizards"), 'wizard-mdk' ]), + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Sharing"), 'file-sharing-mdk', 'wiz-client', + [ + { + title => N("Sharing"), + list => [ + (wizard_format( # [ id, wizard file name, icon, description ] + [ "FTP wizard", "proftpd", 'ftp-mdk', N("Configure FTP"), + N("Set up an FTP server"), 'Proftpd.pm', + ], + [ "Samba wizard", "samba", 'samba_server-mdk', N("Configure Samba"), + N("Set up a file and print server for workstations running Linux and non-Linux systems"), 'Samba.pm', + ], + [ "Manage Samba share", "sambashare", 'wizard-mdk', N("Manage Samba share"), + N("Manage, create special share, create public/user share"), 'Sambashare.pm', 1, + ], + [ "Web wizard", "apache2", 'web_server-mdk', N("Configure web server"), + N("Set up a web server"), 'Apache.pm', + ], + [ "Installation server wizard", "installsrv", 'wizard-mdk', N("Configure installation server"), + N("Set up server for network installations of %s", $branding), 'Installsrv.pm', 1, + ], + ), + ), + ] + }, + ] + ], + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Network Services"), 'network-services-mdk', 'mcc-network', + [ + { + title => N("Network Services"), + list => [ + (wizard_format( # [ id, wizard file name, icon, description ] + [ "DHCP wizard", "dhcp", 'dhcp_server-mdk', N("Configure DHCP"), + N("Set up a DHCP server"), 'Dhcp.pm', + ], + [ "DNS wizard", "bind", 'dns_server-mdk', N("Configure DNS"), + N("Set up a DNS server (network name resolution)"), 'Bind.pm', + ], + [ "Squid wizard", "squid", 'drakproxy-mdk', N("Configure proxy"), + N("Configure a web caching proxy server"), 'Squid.pm', + ], + [ "Time wizard", "ntp", 'ntp_server-mdk', N("Configure time"), + N("Set the time of the server to be synchronized with an external time server"), 'Ntp.pm', + ], + [ "SSHD wizard", "sshd", 'wizard-sshd', N("OpenSSH daemon configuration"), + N("OpenSSH daemon configuration"), 'Sshd.pm', + ], + ), + ), + ] + }, + ] + ], + + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Authentication"), 'drakauth-mdk', '', + [ + { + title => N("Authentication"), + list => [ + if_($is_auth_wizard_installed, "Authentication"), + (wizard_format( # [ id, wizard file name, icon, description ] + [ 'Nis+autofs wizard', "nisautofs", 'nisautofs', N("Configure NIS and Autofs"), + N("Configure the NIS and Autofs services"), 'Nisautofs.pm', + ], + [ "LDAP wizard", "ldap", "ldap-mdk", N("Configure LDAP"), + N("Configure the LDAP directory services"), 'Ldap.pm', + ], + ), + ), + ] + }, + ] + ], + + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Groupware"), 'groupware-mdk', '', + [ + { + title => N("Groupware"), + list => [ + (wizard_format( # [ id, wizard file name, icon, description ] + if_(0, [ "News wizard", "inn", 'news-mdk', N("Configure news"), + N("Configure a newsgroup server"), 'Inn.pm', + ], + [ "Kolab wizard", "kolab", "kolab-mdk", N("Configure groupware"), + N("Configure a groupware server"), 'Kolab.pm', + ]), + [ "Postfix wizard", "postfix", 'postfix-mdk', N("Configure mail"), + N("Configure the Internet Mail services"), 'Postfix.pm' + ], + ), + ), + ] + }, + ] + ], + + if_($isWebAdmin || $isRfbDrake, + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Online Administration"), 'online-administration-mdk', '', + [ + { + title => N("Online Administration"), + list => [ + if_($isWebAdmin, + (map { + my ($id, $icon, $op, $description, $long_description) = @$_; + $programs{$id} = + { + binary => "$bindir/mdkwebadmin.pl $op", + embedded => -1, # too big + description => $description, + long_description => $long_description, + icon => $icon, + }; + + $id; + } ( # [ id, wizard file name, icon, description ] + [ "Local Admin", 'local-administration-mdk', '--direct', N("Local administration"), + (-e "/usr/bin/webmin" ? N("Configure the local machine via web interface") : N("You don't seem to have webmin installed. Local config is disabled")) + ], + [ "Remote Admin", 'remote-administration-mdk', '--link', N("Remote administration"), + N("Click here if you want to configure a remote box via Web interface"), + ]) + ) + ), + if_($isRfbDrake, + "Remote Control", + ) + ] + }, + ] + ]), + + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Hardware"), 'drakhard-mdk', 'mcc-hardware', + [ + { + title => N("Manage your hardware"), + list => [ + "Hardware List", + "Sound", + ] + }, + { + title => N("Configure graphics"), + list => [ + "3D", + "Graphical server configuration", + ] + }, + { + title => N("Configure mouse and keyboard"), + list => [ + "Keyboard", + "Mouse", + ] + }, + { + title => N("Configure printing and scanning"), + list => [ + "Printer", + "Scanner", + if_(-x "$bindir/drakfax", "Fax"), + ] + }, + { + title => N("Others"), + list => [ + "UPS", + ] + }, + ] + ], + + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Network & Internet"), 'net-mdk', 'mcc-network', + [ + { + title => N("Manage your network devices"), + list => [ + "Network Center", + "Add Connection", + if_(!-x "$sbindir/draknetcenter", + "Configure Internet", + "Manage Connection", + "Monitor Connection", + ), + "Remove Interface", + ] + }, + { + title => N("Personalize and Secure your network"), + list => [ + "Proxy Configuration", + "Connection Sharing", + "Network Profiles", + "VPN", + ] + }, + { + title => N("Others"), + list => [ + "Hosts", + ] + }, + ], + ], + + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("System"), 'system-mdk', 'mcc-system', + [ + { + title => N("Manage system services"), + list => [ + if_(!$is_auth_wizard_installed, "Authentication"), + "Menus", + "Services", + "Fonts", + ] + }, + { + title => N("Localization"), + list => [ + "Date & Time", + "Localization", + ] + }, + { + title => N("Administration tools"), + list => [ + "Logs", + if_($ENV{LANGUAGE} !~ /^zh/, "Console"), + "Users", + "Migration", + if_(-x "$xbindir/drakcronat", "Programs scheduling"), + "Backups", + 'Snapshots', + "Virtualization", + ] + }, + ] + ], + + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Network Sharing"), 'network-sharing-mdk', '', + [ + { + title => N("Configure Windows(R) shares"), + list => [ + "Access Windows shares", + "Samba configuration", + ] + }, + { + title => N("Configure NFS shares"), + list => [ + "NFS mount points", + "NFS exports", + ] + }, + { + title => N("Configure WebDAV shares"), + list => [ + "WebDAV mount points", + ] + }, + ], + ], + + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Local disks"), 'partition-mdk', 'mcc-localdisks', + [ + { + title => N("Local disks"), + list => [ + "Hard Drives", + (map { + my ($type, $scan, $text_orig, $long_text) = @$_; + map_index { + my $text = sprintf(translate($text_orig), $_->{info}); + my $full_name = $text . ($::i ? $::i + 1 : ''); + $programs{$full_name} = + { + binary => "$sbindir/diskdrake --removable=$_->{device}", + embedded => 1, + description => $text, + long_description => sprintf(translate($long_text), $_->{info}), + 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 (%s)",), + N_("Set where your \"%s\" CD-ROM drive is mounted"), + ], + [ 'dvd', sub { @{$cdroms_by_type{DVD} || []} }, N_("DVD-ROM (%s)"), + N_("Set where your \"%s\" DVD-ROM drive is mounted"), + ], + [ 'cdwriter', sub { @{$cdroms_by_type{burner} || []} }, N_("CD/DVD burner (%s)"), + N_("Set where your \"%s\" CD/DVD burner is mounted"), + ], + [ 'zip', \&detect_devices::zips, N_("ZIP drive"), + N_("Set where your ZIP drive is mounted"), + ], + ); + }), + "Partition Sharing", + ] + }, + ] + ], + + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Security"), 'security-mdk', 'mcc-security', + [ + { + title => N("Security"), + list => [ + "Security Level", + "Tomoyo Policy", + "Firewall", + "Mageia Tools Authentication", + "Invictus Firewall", + "Parental Controls", + ] + }, + ] + ], + + [ + #-PO: please keep the following message very short: it must fit in the left list of MCC!!! + N("Boot"), 'drakboot-mdk', 'mcc-boot', + [ + { + title => N("Configure boot steps"), + list => [ + #if_(detect_devices::floppies, "Boot Disk"), # kernel is too big + "Auto login Config", + "Boot Config", + "Display Manager chooser", + ] + }, + { + title => N("Boot look'n feel"), + list => [ + #"Boot Theme", + ] + }, + { + title => N("Others"), + list => [ + if_(0, "Auto Install"), + ] + }, + ] + ], +); + +if (scalar glob_("$more_wizard_dir/*.conf")) { + my @leaf = ( + N("Additional wizards"), + 'wizard-mdk', + [] + ); + + foreach my $file (glob_("$more_wizard_dir/*.conf")) { + next unless -f $file; + my %tmp = getVarsFromSh($file); + $programs{$tmp{NAME}} = { + binary => "$sbindir/drakwizard " . lc($file), + embedded => 1, + description => $tmp{DESCRIPTION}, + icon => $tmp{ICON} || 'wizard-mdk', + long_description => $tmp{LONG_DESCRIPTION}, + }; + push(@{$leaf[2]}, $tmp{NAME}); + } + push(@tree, \@leaf); +} + +#------------------------------------------------------------- +# let build the GUI + + +my $align = mygtk2::text_direction_rtl() ? "right" : "left"; +my $align2 = mygtk2::text_direction_rtl() ? "right" : "left"; + + +# main window : + +my ($timeout, %check_boxes, $emb_socket); + +# set default size: + +my $window_global = gtkset_size_request(Gtk2::Window->new('toplevel'), $min_width, $min_height); +$::main_window = $window_global; + +if ($geometry) { + @h{qw(HEIGTH WIDTH)} = $geometry =~ /(\d+)x(\d+)/; + my ($x, $y) = $geometry =~ /([+-]\d+)([+-]\d+)/; + $window_global->set_uposition($x, $y) if $x || $y; +} else { +} +$window_global->resize(max($default_width, $h{WIDTH}, $min_width), max($h{HEIGTH}, $default_height, $min_height)); + +$window_global->set_icon(gtkcreate_pixbuf("/usr/share/icons/drakconf.png")); + +my $pending_app = 0; + +my $help_on_context = 'drakconf-intro'; + +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 $mga_rel = common::mageia_release(); + +my ($steps, $view); + +my $release = mageia_release_info(); + +my @menu_items = ( + [ N("/_File"), undef, undef, undef, '<Branch>' ], + [ N("/_File") . N("/_Quit"), N("<control>Q"), \&quit_global, undef, '<Item>', N("Quit") ], + [ N("/_Options"), undef, undef, undef, '<Branch>' ], + [ join('', @{$options{show_log}}), undef, + sub { + $option_values{show_log} = $check_boxes{show_log}->get_active; + if ($option_values{show_log}) { + start_logdrake(); + } else { + kill_logdrake(); + } + }, + undef, '<CheckItem>' + ], + + if_(0 && $isWiz, + [ join('', @{$options{wiz_expert}}), undef, + sub { $option_values{expert_wizard} = $check_boxes{wiz_expert}->get_active }, + undef, '<CheckItem>', + ], + ), + if_(@themes > 1, + [ N("/_Themes"), undef, undef, undef, '<Branch>' ], + (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, '<CheckItem>' + ]; + } @themes), + [ N("/_Themes") . N("/_More themes"), undef, \&more_themes, undef, '<Item>' ] + ), + [ N("/_Help"), undef, undef, undef, '<Branch>' ], + [ N("/_Help") . N("/_Help"), undef, \&run_help, undef, '<Item>', N("Help") ], + [ N("/_Help") . N("/_Release notes"), undef, sub { run_browser('Notes') }, undef, '<Item>', N("Help") ], + [ N("/_Help") . N("/What's _New?"), undef, sub { run_browser("What's New?") }, undef, '<Item>', N("Help") ], + [ N("/_Help") . N("/_Errata"), undef, sub { run_browser('Errata') }, undef, '<Item>', N("Help") ], + [ N("/_Help") . N("/_Report Bug"), undef, sub { + run_program::raw({ detach => 1, as_user => 1 }, 'drakbug', '--report', (split(/\s/, $pending_app))[0] || 'drakconf'); + }, undef, '<Item>' ], + [ N("/_Help") . N("/_About..."), undef, \&about_mga_cc, undef, '<Item>' ] + ); + + +sub run_help() { + run_program::raw({ detach => 1, as_user => 1 }, 'drakhelp', '--id', $help_on_context); +} + +sub run_browser { + my ($url) = @_; + # Enable back $url when we will have our first release and our first release wiki page + #$url = "http://wiki.mageia.org/en/$release->{version}_$url" if $url !~ m!^http://!; + $url = "http://www.mageia.org/" if $url !~ m!^http://!; + run_program::raw({ detach => 1, as_user => 1 }, '/usr/bin/www-browser', $url); +} + +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/_//g; $i } @_) } + +%check_boxes = map { + $_ => $factory->get_widget("<main>" . get_path(@{$options{$_}})); +} ('show_log', if_(0 && $isWiz, "wiz_expert")); + +my @buttons; +my $offset = 15; + +$view = gtknew('WebKit_View', no_popup_menu => 1); +# so that it exists when building steps: +my $banner_notebook = Gtk2::Notebook->new; +build_list(); + +gtkadd($window_global, + gtkpack_(Gtk2::VBox->new(0, 0), + 0, $menu, + #0, $banner_notebook, + 0, Gtk2::HSeparator->new, + # 0, gtkset_size_request(Gtk2::VBox->new(10, 10), -1, 2), + 1, gtkpack_(Gtk2::HBox->new(0, 0), + 1, $steps = gtknew('MDV_Notebook', parent_window => $window_global, children => [ #Layout Fixed + # 145 is the vertical offset in order to be below the actual logo: + [ gtknew('VBox', widget_name => 'Steps', spacing => 0, width => (192 - $offset), + children_tight => [ map { + gtknew('HBox', spacing => 0, children => [ + 1, $_, + 0, gtknew('Alignment', width => 12), + ]); + } @buttons ]), 0, 100 ], + ], right_child => gtknew('ScrolledWindow', child => gtkset_border_width($view, 5), + no_shadow => 1, h_policy => 'never')), + 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(); + }), + ), + ) + ); + +$view->set_size_request(-1, -1); + +$window_global->signal_connect(delete_event => \&quit_global); + +my $accel = Gtk2::AccelGroup->new; +$accel->connect(Gtk2::Gdk->keyval_from_name('F1'), [], ['visible'], \&run_help); +$window_global->add_accel_group($accel); + +use POSIX qw(:sys_utsname_h :math_h :sys_wait_h :unistd_h); +my (undef, $nodename) = POSIX::uname(); +$window_global->set_title(N("%s Control Center %s [on %s]", $branding, $version, $nodename)); +$window_global->set_position('center'); + +foreach my $notebook ($banner_notebook) { + $notebook->set_property('show-border', 0); + $notebook->set_property('show-tabs', 0); +} + + +# banner : + +#add2notebook($banner_notebook, "", Gtk2::Banner->new("/usr/share/icons/large/drakconf.png", +# N("Welcome to the %s Control Center", $branding))); + + +my ($_hand_cursor, $wait_cursor) = map { Gtk2::Gdk::Cursor->new($_) } qw(hand2 watch); + +my $left_locked = 0; + +#my $spacing = 25; + +my %tool_callbacks; + +my ($page_count); + + +my $conf_file = '/etc/sysconfig/mcc.conf'; +foreach (cat_($conf_file)) { + #s/^ENABLE_//; + #my ($key, $val) = /^(.*)=(.*)/; + if (my ($key, $val) = /^ENABLE_(.*)=(.*)/) { + $key =~ s/_/ /g; + #warn "--> ($key, $val)\n"; + $programs{$key}{disabled} = 1 if $val ne 'yes'; + warn ">> disabling $key\n" if $val ne 'yes'; + } else { + warn "bogus line in $conf_file: $_\n"; + next; + } +} + +sub load_packages2install() { + my $progs_conf_file = '/usr/share/mcc/progs.conf'; + foreach (cat_($progs_conf_file)) { + #if (my ($key, $val) = /^USE_WRAPPER_FOR_(.*)=(.*)/) { + if (my ($key, $val) = /^INSTALL_FOR_(.*)=(.*)/) { + $key =~ s/_/ /g; + # we'll use gurpmi in order to install missing packages if needed; + $programs{$key}{packages2install} = $val if $val; + } else { + warn "bogus line in $conf_file: $_\n"; + next; + } + } +} + +sub clean_list { + my ($subtree) = @_; + grep { + my $stuff = $_; + my $exec = real_bin_path($programs{$stuff}{binary}); + my $is_present = -x $exec; + if (!$is_present && $programs{$stuff}{packages2install}) { + $is_present = 1; + # override missing icon: + $programs{$stuff}{real_icon} ||= $programs{$stuff}{icon}; + $programs{$stuff}{icon} = 'installremoverpm'; + } elsif ($programs{$stuff}{real_icon}) { + $programs{$stuff}{icon} = $programs{$stuff}{real_icon}; + } + # do not complain about missing entries in move: + warn qq("$exec" is not executable [$stuff]) if $mga_rel !~ /Move/ && !$is_present && !$programs{$stuff}{disabled}; + !$programs{$stuff}{hidden} && $is_present && !$programs{$stuff}{disabled}; + } @$subtree; +} + +sub build_widget_element { + my ($label) = @_; + my $icon = $programs{$label}{icon}; + + die "$label 's icon is missing" if !$programs{$label} && $::testing; + $tool_callbacks{$label} = sub { + run_tool($label, undef, #$event_box, + $icon, $programs{$label}{description}, $programs{$label}); + }; + # FIX ME: DO THIS AGAIN: + $tool_feedback{$label} = sub {}; #sub { $event_box->window && $event_box->window->set_cursor($hand_cursor) }; + my $real_icon = $icon ? '<IMG SRC="' . mygtk2::_find_imgfile($icon) . '">' : ''; + my @widgets = ( + qq(<TD class="img">$real_icon</TD>), + qq(<TD style="text-align: $align"><DIV>) + . escape_text_for_TextView_markup_format($programs{$label}{description}) + . qq(</DIV></TD>) + ); + qq(<TD WIDTH="49%"> +<A HREF="$label"><TABLE align="$align2" ID="subtable"><TR> +@widgets +</TR></TABLE></A></TD> +); +} + +my (@strings, $current_string_idx); +sub load_view() { + $view->load_html_string($strings[$current_string_idx], 'file:///'); +} + +sub build_list() { + my $index = 0; + load_packages2install(); + my $i; +foreach (@tree) { + my ($text, $icon, $help, $subtrees) = @$_; + + my @subtrees = grep { $_->{list} = [ clean_list($_->{list}) ]; !is_empty_array_ref($_->{list}) } @$subtrees; + + + # Skip empty classes: + next if !@subtrees; + $i++; + + + my $my_index = $index++; + my $square_icon_uri = mygtk2::_find_imgfile('cadre-ic'); + my $back_img = mygtk2::_find_imgfile('right-white-background_right_part_768.png'); + my $string = join("\n", qq(<HTML> +<HEAD> +<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> +<style type="text/css"> + body { +// background-color: #ecf0f2; +// background-image: url($back_img); + background-repeat: no-repeat; /*pas de répition de l'image*/ + background-attachment: fixed; + background-position: top left + } + p {text-align: $align} + td {text-align: $align} + #title { + text-align: $align; + color: #grey; + font-weight: bold; + margin-left: 13px; + margin-top: 16px; + margin-bottom: 8px; + } + #top_margin { + margin-top: 30px; + } + #block { + margin-left: 4px; + margin-bottom: 24px; + border-spacing: 8px 0px; + } + #subtable { + border-spacing: 8px 8px; + } + td.img { + background-image: url($square_icon_uri); + background-repeat: no-repeat; + background-position: center center; + width: 60; height: 60; + text-align: center; + } + hr { + background-color: #eef2f6; + margin-left: 21px; + margin-right: 22px; + height: 1; + border: 0; + } + tr {text-align: $align} + td { + margin-left: 10px; + margin-right: 10px; + } +), + if_(mygtk2::text_direction_rtl(), " a { direction: rtl; unicode-bidi: embed }"), qq( + a:link {color:black; text-decoration:none } + a:visited {color:black; text-decoration:none } + a:hover {color:grey } +// a:active {color:black; text-decoration:none } +</style> + +</HEAD> +<BODY> +)); + + + + # Create right notebook pages : + + my $section; + foreach my $subtree (@subtrees) { + $string .= $section++ ? "<hr NOSHADE>\n" : '<div ID="top_margin"></div>'; + my $title = mygtk2::asteriskize($subtree->{title}); + $string .= qq( +<div ID="title">$title</div> +<TABLE BORDER=0 WIDTH="100%" ID="block"> +); + if (@{$subtree->{list}} % 2) { + my @widgets = (pop @{$subtree->{list}}, ""); + @widgets = reverse @widgets if mygtk2::text_direction_rtl(); + push @{$subtree->{list}}, @widgets; + } + $string .= join("\n", + map { + "<TR>" . join("\n", grep { defined $_ } @$_) . "</TR>" } +# "<TR>" . join("\n<TD> </TD>\n", grep { defined $_ } @$_) . "</TR>" } + group_by(2, map { $_ ? build_widget_element($_) : '<TD></TD>' } @{$subtree->{list}}) + ); + + $string .= "</TABLE>\n"; + } + my $text_orig = $text; + # b/c we use Pango text markup language, we've to protect '&' caracter: + $text =~ s/&/&/g; + $page_count++; + + $string .= qq(</BODY></HTML>); + if ($save_html) { + mkdir_p('/tmp/mcc'); + MDK::Common::File::output_utf8("/tmp/mcc/$i.html", $string); + } + + $strings[$my_index] = $string; + push @buttons, gtknew('Button', relief => 'none', child => + gtknew('Label_Right', text_markup => qq(<small>$text</small>), ellipsize => 'end'), + clicked => sub { + $current_string_idx = $my_index; + load_view(); + $steps->move_selection($_[0]->child); + $banner_notebook->set_current_page($my_index); + $help_on_context = $help; + }, + ); + + #add2notebook($banner_notebook, "", Gtk2::Banner->new($icon, $text_orig)); +} +} + +$view->signal_connect('navigation-requested' => + sub { + my (undef, undef, $request) = @_; + my $res = 'ignore'; + + # get the actual program ID: + my $url = Glib::filename_from_uri($request->get_uri); + $url =~ s!^/*!!; # not a path + + # workaround wekbit calling navigation-requested with "/": + if (!$url) { + return 'accept'; + } + + # url can be a translated UTF-8 string (eg: for CD/DVD entries): + c::set_tagged_utf8($url); + + # do not crash on eg: "reload": + if (!$tool_callbacks{$url}) { + warn "Warning: invalid tool name: $url\n"; + return $res; + } + $tool_callbacks{$url}->(); + # FIXME: the following code is currently useless: + # should we provide a way to kill buggy embedded programs ? + return $res if $left_locked; + warn_on_startup(); + return $res; + }); + +my %icons = ( + ); + +# 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 { run_tool($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); + err_dialog(N("Error"), N("There's a bug in translations of your language (%s) + +Please report that bug.", $ENV{LANGUAGE})); + } +} + +# "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} ||= mygtk2::_pixbuf_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(); + +# display first page (we use a timeout so that Y position of widgets is know): +Glib::Timeout->add(100, sub { $buttons[0]->signal_emit('clicked'); 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('CHLD', 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_by { + my $nb = shift @_; + my @l; + for (my $i = 0; $i < @_; $i += $nb) { + push @l, [ map { $_[$_] } $i..$i+$nb-1 ]; # $_[$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(); + } +} + +#------------------------------------------------------------- +# 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 $w ($steps, $banner_notebook) { + $w->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 real_bin_path { + my ($prog) = @_; + first(split /\s+/, $prog); +} + +sub run_tool { + 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}; + my $will_run_gurpmi; + if (! -x real_bin_path($exec)) { + if ($tool->{packages2install}) { + # gurpmi doesn't support being embedded yet: + $gtkplug = -1; + $exec = join(' ', "$bindir/gurpmi", split(/\s/, $tool->{packages2install})); + $will_run_gurpmi = 1; + } else { + 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) { + $steps->hide; + create_hidden_socket($icon, $text); + $emb_box->show; + $emb_socket->realize; + $pending_app = $tool->{binary}; + if ($gtkplug > 0) { + $buttons->show; + $exec .= " --embedded " . $emb_socket->get_id; + $wait_darea->show; + $cancel->show; + $run_pixbuf = eval { 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 { + $wait_darea->{size} ||= $wait_darea->allocation; + 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 + local $option_values{embedded} = 0; + $tool_pids{$label} = fork_($exec); + $gurpmi_pid = $tool_pids{$label} if $will_run_gurpmi; + } + 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 { + foreach my $pid (@_) { + kill('TERM', $pid) if $pid; + } +} + +sub kill_children() { + kill_them_all(@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); + standalone::exit(0); +} + + +#------------------------------------------------------------- +# signals managment + +# got when child died and gone in zombie state +sub sig_child { + my ($_sig, $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; + if ($child_pid == $gurpmi_pid) { + undef $gurpmi_pid; + + my @services; + if ($programs{$tool}{services_to_start_after_install}) { + @services = @{$programs{$tool}{services_to_start_after_install}}; + run_program::raw({ detach => 1 }, 'service', $_, 'restart') foreach @services; + } + + # refresh the icon list if some package got installed (eg: system-config-printer) + build_list(); + load_view(); + } + } + 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_mga_cc() { + my $window_about = new_dialog(N("About - %s Control Center", $branding)); + my $tree_model = Gtk2::TreeStore->new("Glib::String", "Glib::String", "Glib::String"); + my $credits = Gtk2::TextView->new; + my $list = Gtk2::TreeView->new_with_model($tree_model); + $list->can_focus(0); + each_index { $list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => $::i)) } 0..1; + $list->set_headers_visible(0); + + foreach my $row ([ N("Authors: "), '' ], + [ '', 'Chmouel Boudjnah' ], + [ '', 'Damien "dam\'s" Krotkine', + #-PO "perl" here is the programming language + N("(perl version)") ], + [ '', 'Daouda Lo' ], + [ '', 'Thierry Vignaud' ], + [ '', 'Yves Duret' ], + [ '', '' ], + [ N("Artwork: "), '', '' ], + [ '', 'Anh-Van Nguyen' ], + + [ '', + #-PO If your language allows it, use eacute for first "e" and egrave for 2nd one. + N("Helene Durosini"), '<ln@mandriva.com>' ], + ) { + $tree_model->append_set(undef, [ map_index { $::i => $_ } @$row ]); + } + + my ($previous_type, $not_first_title, $not_first_block); + my $locale = lang::read; + require Gtk2::Pango; + foreach my $line (grep { $_ ne "\n" && !/^#/ } cat_(top(glob("/usr/share/doc/mageia-release-*/CREDITS.$locale->{lang}")))) { + if (my ($type, $comment, $contributor) = split(/\|/, $line, 3)) { + last if !$type; + $comment =~ s/^ //; # fix initial space of first section (CREDITS format should be enhanced to specify lines that really are sections) + chomp($contributor); + if ($previous_type ne $type) { + gtktext_append($credits, [ [ join('', if_($not_first_title, "\n"), translate(common::to_utf8($type)), "\n"), + { 'weight' => Gtk2::Pango->PANGO_WEIGHT_BOLD, scale => Gtk2::Pango->scale_large } ] ]); + $previous_type = $type; + $not_first_title = 1; + } + if ($contributor) { + gtktext_append($credits, + #-PO: this is used as "language: translator" in credits part of the about dialog: + N("- %s: %s\n", map { translate($_) } $comment, $contributor) + ); + } else { + $comment = $comment . "\n" if !$not_first_block; # fix spacing before second title; + gtktext_append($credits, join('', if_($not_first_block, "- "), translate($comment))); + $not_first_block = 1; + } + } + } + + + # 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: ") ]; + $tree_model->append_set(undef, [ 0 => $_->[0], 1 => $_->[1] ]) foreach [ '', $translator_name, $translator_email ]; + } + $list->get_selection->set_mode('none'); + + gtkpack_($window_about->vbox, + (0, Gtk2::Banner->new('/usr/share/icons/large/drakconf.png', + #-PO: Here, first %s will be replaced by 'Mageia' + #-PO: second %s will be replaced by the version (eg: "Mageia 1 (Free) Control Center") + N("%s %s (%s) Control Center", + $branding, @$release{qw(version product)}))), + 0, Gtk2::Label->new(""), + 0, Gtk2::Label->new( + #-PO: here %s is eg: "1999-2008" + N("Copyright (C) %s Mandriva SA", '1999-2008') . "\n" . N("Copyright (C) %s Mageia", '2011') . "\n" ), + 0, Gtk2::HSeparator->new, + 0, Gtk2::Label->new(""), + 1, my $n = Gtk2::Notebook->new, + ); + + add2notebook($n, N("Authors"), $list); + add2notebook($n, N("Mageia Contributors"), gtkset_size_request(create_scrolled_window($credits), 650, 50)); + $n->set_current_page(0); + + $window_about->show_all; +} + + +#------------------------------------------------------------- +# mcc specific graphic functions: + + +sub set_page { + my ($index) = @_; + start_logdrake(); + set_page_raw($index); + hide_buttons(); +} + +sub hide_buttons() { + $buttons->hide; + $cancel->hide; +} + + +sub create_packtable2 { + my ($options, @l) = @_; + my $w = Gtk2::Table->new(0, 0, 1); + add2hash_($options, { xpadding => 5, ypadding => 0 }); + each_index { + my ($i, $l) = ($::i, $_); + each_index { + my $j = $::i; + if ($_) { + ref $_ or $_ = gtknew('WrappedLabel', text => $_); + $w->attach($_, $j, $j + 1, $i, $i + 1, ['expand', 'fill'], ['expand', 'fill'], $options->{xpadding}, $options->{ypadding}); + $_->show; + } + } @$l; + } @l; + $w->set_col_spacings($options->{col_spacings} || 0); + $w->set_row_spacings($options->{row_spacings} || 0); + gtkset_border_width($w, $::isInstall ? 3 : 10); +} + +#------------------------------------------------------------- +# mcc specific graphic functions: + +sub render_shiner { + my ($pixbuf, $shine_value) = @_; + my $new_pixbuf = (mygtk2::_new_alpha_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'); +} + + +1; |