#!/usr/bin/perl
# $Id: control-center 239367 2008-03-14 10:33:56Z tv $

# 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 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::Pango;
use Gtk2::Html2;


#-------------------------------------------------------------
# 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 ($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);
foreach (@ARGV) {
    $i++;
    $theme = $1 if /--theme (\w+)/;
    $program = $1 if /--start-with=(.*)/;
    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("Mandriva Linux 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<span size="x-large" weight="heavy">),
                                                                               N("Mandriva Linux Control Center"),
                                                                               "</span>"),
                                                       ),
                                          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 $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;


# { key => { log_exp, binary, embedded?, description ] }
# embedded meaning: -1 => not embedded, 0 => external x11 app, 1 => proper embedding
my %programs =
  (
   "3D" =>
   {
    binary => "$sbindir/drak3d",
    embedded => 1,
    description => N("Configure 3D Desktop effects"),
    long_description => N("Configure 3D Desktop effects"),
    icon => 'drak3d',
   },


   "Authentication" =>
   {
    binary => "$sbindir/drakauth",
    embedded => 1,
    description =>
    #-PO: this message is already translated in drakx domain from which MCC will searchs it:
    N("Authentication"),
    long_description => N("Select the authentication method (local, NIS, LDAP, Windows Domain, ...)"),
    icon => 'drakauth-mdk',
   },


   "Auto Install" =>
   {
    binary => "$sbindir/drakautoinst",
    embedded => 1,
    description => N("Auto Install floppy"),
    long_description => N("Generate an Auto Install floppy"),
    icon => 'drakautoinst-mdk',
   },


   "Auto login Config" =>
   {
    binary => "$sbindir/drakboot",
    embedded => 1,
    description => N("Set up autologin to automatically log in"),
    long_description => N("Enable autologin and select the user to automatically log in"),
    icon => 'autologin-mdk',
   },


   "Backups" =>
   {
    binary => "$sbindir/drakbackup",
    embedded => 1,
    description => N("Backups"),
    long_description => N("Configure backups of the system and of the users' data"),
    icon => 'backup-mdk',
    application_driven_menu => 1,
   },


   "Snapshots" =>
   {
    binary => "$sbindir/draksnapshot-config",
    embedded => 1,
    description => N("Snapshots"),
    long_description => '',
    icon => 'snapshots',
   },


   "Boot Config" =>
   {
    binary => "$sbindir/drakboot --boot",
    embedded => 1,
    description => N("Set up boot system"),
    long_description => N("Set up how the system boots"),
    icon => 'drakboot-mdk',
   },


   "Boot Theme" =>
   {
    binary => "$sbindir/drakboot --splash",
    embedded => 1,
    description => N("Set up boot graphical theme of system"),
    long_description => N("Select the graphical theme of the system while booting"),
    icon => 'boot-theme-mdk',
   },


   "Boot Disk" =>
   {
    binary => "$sbindir/drakfloppy",
    embedded => 1,
    description => N("Boot floppy"),
    long_description => N("Generate a standalone boot floppy"),
    icon => 'drakfloppy-mdk',
   },


   "Connection Sharing" =>
   {
    binary => "$sbindir/drakgw",
    embedded => 1,
    description => N("Share the Internet connection with other local machines"),
    long_description => N("Share the Internet connection with other local machines"),
    icon => 'drakgw-mdk',
   },


   "Add Connection" =>
   {
    binary => "$sbindir/drakconnect --wizard",
    embedded => 1,
    description => N("Set up a new network interface (LAN, ISDN, ADSL, ...)"),
    long_description => N("Set up a new network interface (LAN, ISDN, ADSL, ...)"),
    icon => 'new-connection-mdk',
   },


   "Configure Internet" =>
   {
    binary => "$sbindir/drakconnect --internet",
    embedded => 1,
    description => N("Internet access"),
    long_description => N("Alter miscellaneous internet settings"),
    icon => 'internet-access-mdk',
   },


   "Console" =>
   {
    binary => "$sbindir/drakconsole",
    embedded => 1,
    description => N("Open a console as administrator"),
    long_description => N("Open a console as administrator"),
    icon => 'console-mdk',
   #The Console will help you to solve issues
   },


   "Date & Time" =>
   {
    binary => "$sbindir/clock.pl",
    embedded => 1,
    description => N("Manage date and time"),
    long_description => N("Manage date and time"),
    icon => 'time-mdk',
   },


   "Display Manager chooser" =>
   {
    binary => "$sbindir/drakedm",
    embedded => 1,
    description => N("Set up display manager"),
    long_description => N("Choose the display manager that enables to select which user to log in"),
    icon => 'drakedm-mdk',
   },


   "Fax" =>
   {
    binary => "$sbindir/drakfax_server",
    embedded => 1,
    description => N("Configure a fax server"),
    long_description => N("Configure a fax server"),
    icon => 'drakfax-mdk',
   },


   "Firewall" =>
   {
    binary => "$sbindir/drakfirewall",
    embedded => 1,
    description => N("Set up your personal firewall"),
    long_description => N("Set up a personal firewall in order to protect the computer and the network"),
    icon => 'firewall-mdk',
   },


   "Fonts" =>
   {
    binary => "$sbindir/drakfont",
    embedded => 1,
    description => N("Manage, add and remove fonts. Import Windows(TM) fonts"),
    long_description => N("Manage, add and remove fonts. Import Windows(TM) fonts"),
    icon => 'drakfont-mdk',
   },


   "Graphical server configuration" =>
   {
    binary => "$sbindir/XFdrake",
    embedded => 1,
    description => N("Set up the graphical server"),
    long_description => N("Set up the graphical server"),
    icon => 'XFdrake-mdk'
   },


   "Hard Drives" =>
   {
    binary => "$sbindir/diskdrake --hd",
    embedded => 1,
    description => N("Manage disk partitions"),
    long_description => N("Create, delete and resize hard disk partitions"),
    icon => 'diskdrake_hd',
   },


   "Hardware List" =>
   {
    binary => "$sbindir/harddrake2",
    embedded => 1,
    description => N("Browse and configure hardware"),
    long_description => N("Browse and configure hardware"),
    icon => 'harddrake-mdk',
    application_driven_menu => 1,
   },


   "Sound" =>
   {
    binary => "$sbindir/draksound",
    embedded => 1,
    description => N("Sound Configuration"),
    long_description => N("Sound Configuration"),
    icon => 'sound',
   },


   "Hosts" =>
   {
    binary => "$sbindir/drakhosts",
    embedded => 1,
    description => N("Hosts definitions"),
    long_description => N("Manage hosts definitions"),
    icon => 'drakhosts',
   },


   "Install Software" =>
   {
    binary => "$sbindir/rpmdrake",
    embedded => 1,
    description => N("Install & Remove Software"),
    long_description => N("Install, uninstall software"),
    icon => 'installremoverpm',
    application_driven_menu => 1,
   },


   "Invictus Firewall" =>
   {
    binary => "$sbindir/drakinvictus",
    embedded => 1,
    description => N("Advanced setup for network interfaces and firewall"),
    long_description => N("Set up network interfaces failover and firewall replication"),
    icon => 'invictus',
   },


   "Keyboard" =>
   {
    binary => "$sbindir/keyboarddrake",
    embedded => 1,
    description => N("Set up the keyboard layout"),
    long_description => N("Set up the keyboard layout"),
    icon => 'keyboard-mdk',
   },


   "Kolab" =>
   {
    binary => "$sbindir/kolab",
    embedded => 1,
    description => N("Kolab"),
    long_description => N("Set up a groupware server"),
   },


   "Localization" =>
   {
    binary => "$bindir/localedrake",
    embedded => 1,
    icon => 'localedrake',
    description => N("Manage localization for your system"),
    long_description => N("Select the language and the country or region of the system"),
   },


   "Logs" =>
   {
    binary => "$sbindir/logdrake",
    embedded => 1,
    description => N("View and search system logs"),
    long_description => N("View and search system logs"),
    icon => 'logdrake-mdk',
   },


   "Manage Connection" =>
   {
    binary => "$sbindir/drakconnect --skip-wizard",
    embedded => 1,
    description => N("Manage connections"),
    long_description => N("Reconfigure a network interface"),
    icon => 'manage-connections-mdk',
   },


   "Mandriva Online" =>
   {
    binary => "$xbindir/mdkonline",
    embedded => 1,
    description => N("Upload your configuration to get information on upgrades"),
    long_description => N("Upload your configuration in order to keep you informed about security and useful upgrades"),
    icon => 'mdkonline-mdk',
   },


   "Manage park" =>
   {
    binary => "$sbindir/park-rpmdrake",
    embedded => 1,
    description => N("Manage computer group"),
    long_description => N("Manage installed software packages on a group of computers"),
    icon => 'drakpark-mdk',
   },


   "Mandriva Update" =>
   {
    binary => "$sbindir/MandrivaUpdate",
    embedded => 1,
    description => N("Update your system"),
    long_description => N("Look at available updates and apply any fixes or upgrades to installed packages"),
    icon => 'MandrivaUpdate',
    application_driven_menu => 1,
   },


   "Menus" =>
   {
    binary => "$bindir/drakmenustyle",
    embedded => 1,
    description => N("Menu Style"),
    long_description => N("Menu Style Configuration"),
    icon => 'drakmenustyle',
   },


   "Migration" =>
   {
    binary => "$sbindir/transfugdrake",
    embedded => 1,
    description => N("Import Windows(TM) documents and settings"),
    long_description => N("Import Windows(TM) documents and settings"),
    icon => 'migrationtools',
   },


   "Monitor Connection" =>
   {
    binary => "$sbindir/net_monitor",
    embedded => 1,
    description => N("Monitor connections"),
    long_description => N("Monitor the network connections"),
    icon => 'net_monitor-mdk',
   },


   "Mouse" =>
   {
    binary => "$sbindir/mousedrake",
    embedded => 1,
    description => N("Set up the pointer device (mouse, touchpad)"),
    long_description => N("Set up the pointer device (mouse, touchpad)"),
    icon => 'mousedrake-mdk',
   },


   "Parental Controls" => {
    binary => "$sbindir/drakguard",
    embedded => 1,
    description => N("Parental Controls"),
    long_description => N("Parental Controls"),
    icon => 'drakguard',
   },


   "Network Center" =>
   {
    binary => "$sbindir/draknetcenter",
    embedded => 1,
    description => N("Network Center"),
    long_description => N("Manage your network devices"),
    icon => 'netcenter',
   },


   "Network Profiles" =>
   {
    binary => "$sbindir/draknetprofile",
    embedded => 1,
    description => N("Manage different network profiles"),
    long_description => N("Activate and manage network profiles"),
    icon => 'draknetprofile',
   },


   "NFS mount points" =>
   {
    binary => "$sbindir/diskdrake --nfs",
    embedded => 1,
    description => N("Access NFS shared drives and directories"),
    long_description => N("Set NFS mount points"),
    icon => 'diskdrake_nfs',
   },


   "NFS exports" =>
   {
    binary => "$sbindir/draknfs",
    embedded => 1,
    description => N("Share drives and directories using NFS"),
    long_description => N("Manage NFS shares"),
    icon => 'draknfs',
    application_driven_menu => 1,
   },


   "Package stats" =>
   {
    binary => "$sbindir/drakstats",
    embedded => 1,
    description => N("Package Stats"),
    long_description => N("Show statistics about usage of installed software packages"),
    icon => 'source-manager',
   },


   "Partition Sharing" =>
   {
    binary => "$sbindir/diskdrake --fileshare",
    embedded => 1,
    description => N("Share your hard disk partitions"),
    long_description => N("Set up sharing of your hard disk partitions"),
    icon => 'diskdrake_fileshare',
   },


   "Printer" =>
   {
    binary => "$sbindir/printerdrake",
    embedded => 1,
    description => N("Set up the printer(s), the print job queues, ..."),
    icon => 'printer-mcc-mdk',
    long_description => N("Set up the printer(s), the print job queues, ..."),
    application_driven_menu => 1,
   },
 

   "Programs scheduling" =>
   {
    binary => "$xbindir/drakcronat",
    embedded => 1,
    description => N("Scheduled tasks"),
    long_description => N("Schedule programs to run periodically or at given times"),
    #DrakCronAt enables to schedule Programs execution through crond and atd daemons
    icon => 'drakcronat-mdk',
   },

   "Proxy Configuration" =>
   {
    binary => "$sbindir/drakproxy",
    embedded => 1,
    description => N("Proxy"),
    long_description => N("Set up a proxy server for files and web browsing"),
    icon => 'drakproxy-mdk',
   },

   "Remote Control" =>
   {
    binary => "$bindir/rfbdrake",
    embedded => 1,
    description => N("Remote Control (Linux/Unix, Windows)"),
    long_description => N("Remote Control of another machine (Linux/Unix, Windows)"),
    icon => 'internet-access-mdk',
   },


   "Remove Interface" =>
   {
    binary => "$sbindir/drakconnect --del",
    embedded => 1,
    description => N("Remove a connection"),
    long_description => N("Delete a network interface"),
    icon => 'remove-connection-mdk',
   },


   #- currently unused
   "Roaming" =>
   {
    binary => "$sbindir/drakroam",
    embedded => 1,
    description => N("Wireless connection"),
    long_description => N("Wireless connection"),
    icon => 'drakroam-mdk',
   },


   "Access Windows shares" =>
   {
    binary => "$sbindir/diskdrake --smb",
    embedded => 1,
    description => N("Access Windows (SMB) shared drives and directories"),
    long_description => N("Configuration of Windows (Samba) shared drives and directories"),
    icon => 'diskdrake_samba',
   },


   "Samba configuration" =>
   {
    binary => "$sbindir/draksambashare",
    embedded => 1,
    description => N("Share drives and directories with Windows (SMB) systems"),
    long_description => N("Manage configuration of Samba"),
    icon => 'draksambashare',
    application_driven_menu => 1,
   },

   "Scanner" =>
   {
    binary => "$sbindir/scannerdrake",
    embedded => 1,
    description => N("Set up scanner"),
    long_description => N("Set up scanner"),
    icon => 'scanner-mdk',
   },


   "Security Level" =>
   {
    binary => "$sbindir/draksec",
    embedded => 1,
    description => N("Set up security level and audit"),
    long_description => N("Set the system security level and the periodic security audit"),
    icon => 'draksec-mdk',
   },


   "Security Permissions" =>
   {
    binary => "$sbindir/drakperm",
    embedded => 1,
    description => N("Tune permissions on system"),
    long_description => N("Fine tune the security permissions of the system"),
    icon => 'drakperm-mdk',
   },


   "Services" =>
   {
    binary => "$sbindir/drakxservices",
    embedded => 1,
    description => N("Manage system services by enabling or disabling them"),
    long_description => N("Manage system services by enabling or disabling them"),
    icon => 'service-mdk',
   },


   "Software Media Manager" =>
   {
    binary => "$sbindir/edit-urpm-sources.pl",
    embedded => 1,
    description => N("Configure media sources for install and update"),
    long_description => N("Select from where software packages are downloaded "),
    icon => 'source-manager',
    application_driven_menu => 1,
   },


   "UPS" =>
   {
    binary => "$sbindir/drakups --wizard",
    embedded => 1,
    description =>
      #-PO: UPS==Uninterruptible power supply
      N("Set up a UPS for power monitoring"),
    long_description =>
    #-PO: here power means electrical power
    N("Set up a UPS for power monitoring"),
    icon => 'drakups-mdk',
   },


   "Users" =>
   {
    # too big
    binary => "$sbindir/userdrake",
    embedded => 1,
    description => N("Manage users on system"),
    long_description => N("Add, remove or change users of the system"),
    icon => 'user-mdk',
    application_driven_menu => 1,
   },


   "Virtualization" =>
   {
    binary => "$sbindir/drakvirt",
    embedded => 1,
    description => N("Virtualization"),
    long_description => N("Virtual machines management"),
    icon => 'drakvirt',
   },


   "VPN" =>
   {
    binary => "$sbindir/drakvpn",
    embedded => 1,
    description => N("Configure VPN connection to secure network access"),
    long_description => N("Configure VPN connection to secure network access"),
    icon => 'drakvpn',
   },


   "WebDAV mount points" =>
   {
    binary => "$sbindir/diskdrake --dav",
    embedded => 1,
    description => N("Access WebDAV shared drives and directories"),
    long_description => N("Set WebDAV mount points"),
    icon => 'webdav-mdk',
   },

  );


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,
        [
            N("Software Management"), 'software',
            [
                if_($isRpmDrake,
                    {
                        title => N("Software Management"),
                        list => [
                            "Install Software",
                            "Mandriva Update",
                            if_(0, "Mandriva Online"),
                            "Software Media Manager",
                        ]
                    },
                ),
                {
                    title => N("Others"),
                    list => [
                        if_($isParkRpmDrake, "Manage park"),
                        if_($isDrakStats, "Package stats"),
                    ]
                },
            ]
        ]
    ),

    if_(0, [ N("Server wizards"), 'wizard-mdk' ]),
    [
        N("Sharing"), 'file-sharing-mdk',
        [
            {
                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 Mandriva Linux"), 'Installsrv.pm', 1,
                      ],
                    ),
                 ),
                ]
            },
        ]
    ],
    [
        N("Network Services"), 'network-services-mdk',
        [
            {
                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',
                      ],
                    ),
                 ),
                ]
            },
        ]
    ],

    [ 
        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',
                      ],
                    ),
                 ),
                ]
            },
        ]
    ],

    [ 
        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,
        [
            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 intalled. 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",
                        )
                    ]
                },
            ]
        ]),

    [
        N("Hardware"), 'drakhard-mdk',
        [
            {
                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",
                ]
            },
        ]
    ],

    [
        N("Network & Internet"), 'net-mdk',
        [
            {
                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",
                ]
            },
        ],
    ],

    [
        N("System"), 'system-mdk',
        [
            {
                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",
                ]
            },
        ]
    ],

    [
        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",
                ]
            },
        ],
    ],

    [
        N("Local disks"), 'partition-mdk',
        [
            {
                title => N("Local disks"),
                list => [
                    "Hard Drives",
                    (map {
                        my ($type, $scan, $text, $long_text) = @$_;
                        map_index {
                            my $full_name = $text . ($::i ? $::i + 1 : '');
                            $programs{$full_name} =
                              {
                                  binary => "$sbindir/diskdrake --removable=$_->{device}",
                                  embedded => 1,
                                  description => sprintf(translate($text), $_->{info}),
                                  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"),
                       ],
                         [ 'floppy', \&detect_devices::floppies, N_("Floppy drive"),
                           N_("Set where your floppy drive is mounted"),
                       ],
                         [ 'zip', \&detect_devices::zips, N_("ZIP drive"),
                           N_("Set where your ZIP drive is mounted"),
                       ],
                     );
                    }),
                    "Partition Sharing",
                ]
            },
        ]
    ],

    [
        N("Security"), 'security-mdk',
        [
            {
                title => N("Security"),
                list => [
                    "Security Level",
                    "Security Permissions",
                    "Firewall",
                    "Invictus Firewall",
                    "Parental Controls",
                ]
            },
        ]
    ],

    [
        N("Boot"), 'boot-mdk',
        [
            {
                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

# 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';
#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 $mdk_rel = common::mandrake_release();

my ($notebook_global);

my $release = mandrake_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,  sub { fork_("drakhelp --id $help_on_context", []) }, 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 }, 'drakbug', '--report', (split(/\s/, $pending_app))[0] || 'drakconf');
                      }, undef, '<Item>' ],
                  [ N("/_Help") . N("/_About..."), undef, \&about_mdk_cc, undef, '<Item>' ]
                 );

sub run_browser {
    my ($url) = @_;
    $url = "http://wiki.mandriva.com/en/Releases/Mandriva/$release->{version}/$url" if $url !~ m!^http://!;
    run_program::raw({ detach => 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"));


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, $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();
                                                          }),
                                       ),
               )
      );

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] = "<none>"
}

widget "*mcc*"              style "mcc"

)));
    $notebook_global->reset_rc_styles;
}

$notebook_global->signal_connect(realize => \&notebook_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("Mandriva Linux 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 Mandriva Linux Control Center")));


my ($_hand_cursor, $wait_cursor) = map { Gtk2::Gdk::Cursor->new($_) } qw(hand2 watch);

my ($index, $left_locked) = (0, 0);

#my $spacing = 25;

my %tool_callbacks;

my ($page_count);


sub request_url {
	my ($_document, $url, $stream) = @_;
     return if !$url;
     $stream->write(join('', cat_($url)));
     $stream->close;
}

my $align = lang::text_direction_rtl() ? "right" : "justify";
my $align2 = lang::text_direction_rtl() ? "right" : "left";

sub clean_list {
    my ($subtree) = @_;
    grep {
        my $stuff = $_;
        my $exec = first(split /\s+/, $programs{$stuff}{binary});
        my $is_present = -x $exec;
        # do not complain about missing entries in move:
        warn qq("$exec" is not executable [$stuff]) if $mdk_rel !~ /Move/ && !$is_present;
        !$programs{$stuff}{hidden} && $is_present;
    } @$subtree;
}

my @labels;
foreach (@tree) {
    my ($text, $icon, $subtrees) = @$_;

    my @subtrees = grep { $_->{list} = [ clean_list($_->{list}) ]; !is_empty_array_ref($_->{list}) } @$subtrees;


    # Skip empty classes:
    next if !@subtrees;

    my $_my_index = $index++;
    my $string = join("\n", qq(<HTML>
<HEAD>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<style type="text/css">
 p {text-align:  $align}
 td {text-align: $align}
 h3 {text-align: $align; color: gray; font-weight: bold;  }
 hr { width: 98%; background-color: #eef2f6; height: 1; border:0; }
 tr {text-align: $align}
 td {
     margin-left: 10px;
     margin-right: 10px;
 }
),
 if_(lang::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:blue }
// a:active  {color:black; text-decoration:none }
</style>

</HEAD>
<BODY>
));
    my $view     = Gtk2::Html2::View->new;
    my $document = Gtk2::Html2::Document->new;

    $document->signal_connect(request_url => \&request_url);
    $document->signal_connect('link-clicked' => 
                               sub {
                                   my ($_document, $url) = @_;
                                   $tool_callbacks{$url}->();
                                   # FIXME: the following code is currently useless: 
                                   #        should we provide a way to kill buggy embedded programs ?
                                   return if $left_locked;
                                   warn_on_startup();
                               });
    $view->set_document($document);


    # Create right notebook pages :

    my $section;
    foreach my $subtree (@subtrees) {
        $string .= "<hr NOSHADE>\n" if $section++;
        $string .= qq(
<h3>$subtree->{title}</h3>
<TABLE BORDER=0 WIDTH="100%" style="margin-bottom: 40px">
);
        if (@{$subtree->{list}} % 2) {
            my @widgets = (pop @{$subtree->{list}}, "");
            @widgets = reverse @widgets if lang::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 {
                        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="' . ugtk2::_find_imgfile($icon) . '">' : '';
                        my @widgets = (
                            qq(<TD><A HREF="$label">$real_icon</A></TD>),
                            qq(<TD style="text-align: $align"><A HREF="$label"><DIV>)
                              . escape_text_for_TextView_markup_format($programs{$label}{description})
                                . qq(</DIV></A></TD>)
                        );
                        @widgets = reverse @widgets if lang::text_direction_rtl();
                         qq(<TD WIDTH="49%">
<TABLE align="$align2"><TR>
@widgets
</TR></TABLE></TD>
);
                   } @{$subtree->{list}})
                   );

        $string .= "</TABLE>\n";
    }
    # b/c we use Pango text markup language, we've to protect '&' caracter:
    $text =~ s/&/&amp;/g;
    $page_count++;

    $string .= qq(</BODY></HTML>);

    $document->clear;
    $document->open_stream("text/html");
    $document->write_stream($string);
    $labels[$index-1]{text} = $text;

    gtkappend_page($notebook_global,
                 my $_w_ret = create_scrolled_window(gtkset_border_width($view, 5),
                                                     [ 'never', 'automatic' ], # 'none',
                                                    ),
                   gtkpack__(Gtk2::HBox->new,
                             Gtk2::Image->new_from_pixbuf(::scale(gtkcreate_pixbuf($icon), -16)),
                             $labels[$index-1]{widget} = Gtk2::WrappedLabel->new($text),
                            ),
                );
    $labels[$index-1]{widget}->set_use_markup(1);
#    $w_ret->vscrollbar->set_size_request(19, undef);

    add2notebook($banner_notebook, "", Gtk2::Banner->new($icon, $text));
}

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} ||= 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);

my $old_index;
$notebook_global->signal_connect(switch_page => sub {
    my ($notebook, $page, $new_index) = @_;
    return if !$labels[$new_index]{widget};
    my $page = $notebook_global->get_current_page;
    $labels[$old_index]{widget}->set_markup($labels[$old_index]{text}) if -1 < $old_index;
    $labels[$new_index]{widget}->set_markup('<span weight="bold">' . $labels[$new_index]{text} . '</span>');
    $old_index = $new_index;
});

$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 $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 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};
    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 = $tool->{binary};
        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 { 
                                              $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);
    }
    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);
}

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;
        }
        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 - Mandriva Linux Control Center"));

    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;
    foreach my $line (grep { $_ ne "\n" && !/^#/ } cat_(top((glob("/usr/share/doc/mandriva-release-*/CREDITS"), glob("/usr/share/doc/mandriva-release-*/CREDITS.$locale->{lang}"))))) {
        $line =~ s/ Limited Edition 2005 \|//;
        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->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, gtkset_size_request(Gtk2::Banner->new('splash_screen_about',
                                                       #-PO: here, %s will be replaced by the version (eg: "Mandriva Linux 2007.1 (Discovery) Control Center")
                                                       N("Mandriva Linux %s (%s) Control Center",
                                                         @$release{qw(version product)}),
                                                       { txt_ypos => 50 }), -1, 120)),
             0, Gtk2::Label->new(""),
             0, Gtk2::Label->new(
                 #-PO: here %s is eg: "1999-2008"
                 N("Copyright (C) %s Mandriva SA", '1999-2008') . "\n"),
             0, Gtk2::HSeparator->new,
             0, Gtk2::Label->new(""),
             1, my $n = Gtk2::Notebook->new,
             );

    add2notebook($n, N("Authors"), $list);
    add2notebook($n, N("Mandriva Linux Contributors"), gtkset_size_request(create_scrolled_window($credits), 650, 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;
}


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 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');
}


1;
