1 |
#!/usr/bin/perl |
2 |
# $Id: control-center 269021 2010-05-19 16:37:57Z eugeni $ |
3 |
|
4 |
# Copyright (C) 1999-2008 Mandriva |
5 |
# Daouda Lo |
6 |
# Damien Krotkine |
7 |
# Thierry Vignaud <tvignaud@mandriva.com> |
8 |
# Yves Duret |
9 |
# |
10 |
# This program is free software; you can redistribute it and/or modify it under |
11 |
# the terms of the GNU General Public License as published by the Free Software |
12 |
# Foundation; either version 2, or (at your option) any later version. |
13 |
# |
14 |
# This program is distributed in the hope that it will be useful, |
15 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 |
# GNU General Public License for more details. |
18 |
# |
19 |
# You should have received a copy of the GNU General Public License |
20 |
# along with this program; if not, write to the Free Software |
21 |
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. |
22 |
|
23 |
|
24 |
use strict; |
25 |
use diagnostics; |
26 |
use lib qw(/usr/lib/libDrakX); |
27 |
use c; |
28 |
use standalone; |
29 |
use common; |
30 |
use detect_devices; |
31 |
use lang; |
32 |
|
33 |
# i18n: IMPORTANT: to get correct namespace (drakconf instead of libDrakX) |
34 |
BEGIN { unshift @::textdomains, 'drakconf' } |
35 |
use mygtk2 qw(gtknew); |
36 |
use ugtk2 qw(:create :dialogs :helpers :wrappers); |
37 |
use Gtk2::WebKit; |
38 |
|
39 |
use MDV::Control_Center; |
40 |
|
41 |
|
42 |
my (%tool_pids, %tool_feedback, $gurpmi_pid); |
43 |
|
44 |
my ($conffile, $class_install) = ('/etc/mcc.conf', '/etc/sysconfig/system'); |
45 |
|
46 |
my ($rootwin_width, $rootwin_height) = gtkroot()->get_size; |
47 |
my $default_width = $rootwin_width <= 800 ? 720 : 800; |
48 |
my $default_height = $rootwin_height <= 480 ? 420 : $rootwin_height <= 600 ? 523 : 600; |
49 |
|
50 |
my $min_width = $rootwin_width == 640 ? 620 : 680; |
51 |
my $min_height = $rootwin_height == 480 ? 420 : 500; |
52 |
|
53 |
require_root_capability() if !$::testing; # just to get root capabilities |
54 |
|
55 |
|
56 |
#------------------------------------------------------------- |
57 |
# read configuration, set themes, ... |
58 |
my %h = getVarsFromSh($conffile); |
59 |
my %class = getVarsFromSh($class_install); |
60 |
$h{THEME} ||= 'default'; |
61 |
$h{LOGS} ||= bool2text($class{CLASS} eq 'expert' ? 1 : 0); |
62 |
$h{EXPERT_WIZARD} ||= 0; |
63 |
$h{HEIGTH} ||= $default_height; |
64 |
$h{WIDTH} ||= $default_width; |
65 |
|
66 |
my %option_values; |
67 |
$option_values{show_log} = text2bool($h{LOGS}); |
68 |
my $theme = $h{THEME}; |
69 |
my $program; |
70 |
my ($i, $geometry, $save_html); |
71 |
foreach (@ARGV) { |
72 |
$i++; |
73 |
$theme = $1 if /--theme (\w+)/; |
74 |
$program = $1 if /--start-with=(.*)/; |
75 |
if (/^--geometry$/) { |
76 |
$geometry = splice @ARGV, $i, 1; |
77 |
last; |
78 |
} |
79 |
$save_html = 1 if /--save-html-pages/; |
80 |
} |
81 |
|
82 |
-d "$themes_dir/$theme" or $theme = 'default'; |
83 |
add_icon_path("$themes_dir/$theme/"); |
84 |
add_icon_path("$themes_dir/default") if $theme ne 'default'; # fall back if theme miss some icons |
85 |
|
86 |
mygtk2::import_style_ressources(); |
87 |
my $rc = find { -r $_ } ("$themes_dir/$theme/gtkrc", if_($theme ne 'default', "$themes_dir/default/gtkrc")); |
88 |
Gtk2::Rc->parse($rc) if -r $rc; |
89 |
|
90 |
my $branding = N("Mageia"); |
91 |
my $product_id = common::parse_LDAP_namespace_structure(cat_('/etc/product.id')); |
92 |
# allow OEM branding: |
93 |
$branding = translate($product_id->{distribution}); |
94 |
|
95 |
|
96 |
#------------------------------------------------------------- |
97 |
# Splash window: please wait ... |
98 |
my $window_splash = Gtk2::Window->new('popup'); |
99 |
$window_splash->signal_connect(delete_event => \&quit_global); |
100 |
$window_splash->set_title(N("%s Control Center", $branding)); |
101 |
$window_splash->set_position('center_always'); |
102 |
$window_splash->add(gtknew('Fixed', widget_name => 'Steps', |
103 |
children => [ |
104 |
[ gtknew('Image_using_pixbuf', file => 'splash_screen'), 0, 0 ], |
105 |
[ gtknew('Label', text => N("%s Control Center", $branding)), 21, 60 ], |
106 |
[ gtknew('Label', text => N("Loading... Please wait")), 21, 85 ], |
107 |
]), |
108 |
); |
109 |
$window_splash->show_all; |
110 |
gtkflush(); |
111 |
|
112 |
|
113 |
#------------------------------------------------------------- |
114 |
# Data structures |
115 |
my $more_wizard_dir = "/etc/wizard.d/"; |
116 |
|
117 |
my $isWiz = -e "/usr/sbin/drakwizard"; |
118 |
my $isRpmDrake = -e "/usr/sbin/rpmdrake"; |
119 |
my $isParkRpmDrake = -e "/usr/sbin/park-rpmdrake"; |
120 |
my $isWebAdmin = -e "/usr/bin/mdkwebadmin"; |
121 |
my $isRfbDrake = -e "/usr/bin/rfbdrake"; |
122 |
my $isDrakStats = -e "/usr/sbin/drakstats"; |
123 |
|
124 |
my $application_driven_menu; |
125 |
|
126 |
sub is_wizard_installed { -f top(glob("/usr/lib/perl5/vendor_perl/*/MDK/Wizard/$_[0]")) } |
127 |
|
128 |
my $is_auth_wizard_installed = is_wizard_installed('Nisautofs.pm'); |
129 |
|
130 |
|
131 |
sub wizard_format { |
132 |
map { |
133 |
my ($id, $wizard, $icon, $description, $long_description, $file) = @$_; |
134 |
$programs{$id} = |
135 |
{ |
136 |
binary => "$sbindir/drakwizard $wizard", |
137 |
embedded => 1, |
138 |
description => $description, |
139 |
long_description => $long_description, |
140 |
icon => $icon, |
141 |
}; |
142 |
if_(is_wizard_installed($file), $id); |
143 |
} @_; |
144 |
} |
145 |
|
146 |
# [ [ class_label, class icon name, [ program_label, ... ] ] |
147 |
my @tree = ( |
148 |
if_($isRpmDrake || $isParkRpmDrake, |
149 |
[ |
150 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
151 |
N("Software Management"), 'software', 'software-management', |
152 |
[ |
153 |
if_($isRpmDrake, |
154 |
{ |
155 |
title => N("Software Management"), |
156 |
list => [ |
157 |
"Install Software", |
158 |
"Mageia Update", |
159 |
if_(0, "Mageia Online"), |
160 |
"Updates Configuration", |
161 |
"Software Media Manager", |
162 |
] |
163 |
}, |
164 |
), |
165 |
{ |
166 |
title => N("Others"), |
167 |
list => [ |
168 |
if_($isParkRpmDrake, "Manage park"), |
169 |
if_($isDrakStats, "Package stats"), |
170 |
] |
171 |
}, |
172 |
] |
173 |
] |
174 |
), |
175 |
|
176 |
if_(0, [ N("Server wizards"), 'wizard-mdk' ]), |
177 |
[ |
178 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
179 |
N("Sharing"), 'file-sharing-mdk', 'wiz-client', |
180 |
[ |
181 |
{ |
182 |
title => N("Sharing"), |
183 |
list => [ |
184 |
(wizard_format( # [ id, wizard file name, icon, description ] |
185 |
[ "FTP wizard", "proftpd", 'ftp-mdk', N("Configure FTP"), |
186 |
N("Set up an FTP server"), 'Proftpd.pm', |
187 |
], |
188 |
[ "Samba wizard", "samba", 'samba_server-mdk', N("Configure Samba"), |
189 |
N("Set up a file and print server for workstations running Linux and non-Linux systems"), 'Samba.pm', |
190 |
], |
191 |
[ "Manage Samba share", "sambashare", 'wizard-mdk', N("Manage Samba share"), |
192 |
N("Manage, create special share, create public/user share"), 'Sambashare.pm', 1, |
193 |
], |
194 |
[ "Web wizard", "apache2", 'web_server-mdk', N("Configure web server"), |
195 |
N("Set up a web server"), 'Apache.pm', |
196 |
], |
197 |
[ "Installation server wizard", "installsrv", 'wizard-mdk', N("Configure installation server"), |
198 |
N("Set up server for network installations of %s", $branding), 'Installsrv.pm', 1, |
199 |
], |
200 |
), |
201 |
), |
202 |
] |
203 |
}, |
204 |
] |
205 |
], |
206 |
[ |
207 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
208 |
N("Network Services"), 'network-services-mdk', 'mcc-network', |
209 |
[ |
210 |
{ |
211 |
title => N("Network Services"), |
212 |
list => [ |
213 |
(wizard_format( # [ id, wizard file name, icon, description ] |
214 |
[ "DHCP wizard", "dhcp", 'dhcp_server-mdk', N("Configure DHCP"), |
215 |
N("Set up a DHCP server"), 'Dhcp.pm', |
216 |
], |
217 |
[ "DNS wizard", "bind", 'dns_server-mdk', N("Configure DNS"), |
218 |
N("Set up a DNS server (network name resolution)"), 'Bind.pm', |
219 |
], |
220 |
[ "Squid wizard", "squid", 'drakproxy-mdk', N("Configure proxy"), |
221 |
N("Configure a web caching proxy server"), 'Squid.pm', |
222 |
], |
223 |
[ "Time wizard", "ntp", 'ntp_server-mdk', N("Configure time"), |
224 |
N("Set the time of the server to be synchronized with an external time server"), 'Ntp.pm', |
225 |
], |
226 |
[ "SSHD wizard", "sshd", 'wizard-sshd', N("OpenSSH daemon configuration"), |
227 |
N("OpenSSH daemon configuration"), 'Sshd.pm', |
228 |
], |
229 |
), |
230 |
), |
231 |
] |
232 |
}, |
233 |
] |
234 |
], |
235 |
|
236 |
[ |
237 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
238 |
N("Authentication"), 'drakauth-mdk', '', |
239 |
[ |
240 |
{ |
241 |
title => N("Authentication"), |
242 |
list => [ |
243 |
if_($is_auth_wizard_installed, "Authentication"), |
244 |
(wizard_format( # [ id, wizard file name, icon, description ] |
245 |
[ 'Nis+autofs wizard', "nisautofs", 'nisautofs', N("Configure NIS and Autofs"), |
246 |
N("Configure the NIS and Autofs services"), 'Nisautofs.pm', |
247 |
], |
248 |
[ "LDAP wizard", "ldap", "ldap-mdk", N("Configure LDAP"), |
249 |
N("Configure the LDAP directory services"), 'Ldap.pm', |
250 |
], |
251 |
), |
252 |
), |
253 |
] |
254 |
}, |
255 |
] |
256 |
], |
257 |
|
258 |
[ |
259 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
260 |
N("Groupware"), 'groupware-mdk', '', |
261 |
[ |
262 |
{ |
263 |
title => N("Groupware"), |
264 |
list => [ |
265 |
(wizard_format( # [ id, wizard file name, icon, description ] |
266 |
if_(0, [ "News wizard", "inn", 'news-mdk', N("Configure news"), |
267 |
N("Configure a newsgroup server"), 'Inn.pm', |
268 |
], |
269 |
[ "Kolab wizard", "kolab", "kolab-mdk", N("Configure groupware"), |
270 |
N("Configure a groupware server"), 'Kolab.pm', |
271 |
]), |
272 |
[ "Postfix wizard", "postfix", 'postfix-mdk', N("Configure mail"), |
273 |
N("Configure the Internet Mail services"), 'Postfix.pm' |
274 |
], |
275 |
), |
276 |
), |
277 |
] |
278 |
}, |
279 |
] |
280 |
], |
281 |
|
282 |
if_($isWebAdmin || $isRfbDrake, |
283 |
[ |
284 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
285 |
N("Online Administration"), 'online-administration-mdk', '', |
286 |
[ |
287 |
{ |
288 |
title => N("Online Administration"), |
289 |
list => [ |
290 |
if_($isWebAdmin, |
291 |
(map { |
292 |
my ($id, $icon, $op, $description, $long_description) = @$_; |
293 |
$programs{$id} = |
294 |
{ |
295 |
binary => "$bindir/mdkwebadmin.pl $op", |
296 |
embedded => -1, # too big |
297 |
description => $description, |
298 |
long_description => $long_description, |
299 |
icon => $icon, |
300 |
}; |
301 |
|
302 |
$id; |
303 |
} ( # [ id, wizard file name, icon, description ] |
304 |
[ "Local Admin", 'local-administration-mdk', '--direct', N("Local administration"), |
305 |
(-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")) |
306 |
], |
307 |
[ "Remote Admin", 'remote-administration-mdk', '--link', N("Remote administration"), |
308 |
N("Click here if you want to configure a remote box via Web interface"), |
309 |
]) |
310 |
) |
311 |
), |
312 |
if_($isRfbDrake, |
313 |
"Remote Control", |
314 |
) |
315 |
] |
316 |
}, |
317 |
] |
318 |
]), |
319 |
|
320 |
[ |
321 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
322 |
N("Hardware"), 'drakhard-mdk', 'mcc-hardware', |
323 |
[ |
324 |
{ |
325 |
title => N("Manage your hardware"), |
326 |
list => [ |
327 |
"Hardware List", |
328 |
"Sound", |
329 |
] |
330 |
}, |
331 |
{ |
332 |
title => N("Configure graphics"), |
333 |
list => [ |
334 |
"3D", |
335 |
"Graphical server configuration", |
336 |
] |
337 |
}, |
338 |
{ |
339 |
title => N("Configure mouse and keyboard"), |
340 |
list => [ |
341 |
"Keyboard", |
342 |
"Mouse", |
343 |
] |
344 |
}, |
345 |
{ |
346 |
title => N("Configure printing and scanning"), |
347 |
list => [ |
348 |
"Printer", |
349 |
"Scanner", |
350 |
if_(-x "$bindir/drakfax", "Fax"), |
351 |
] |
352 |
}, |
353 |
{ |
354 |
title => N("Others"), |
355 |
list => [ |
356 |
"UPS", |
357 |
] |
358 |
}, |
359 |
] |
360 |
], |
361 |
|
362 |
[ |
363 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
364 |
N("Network & Internet"), 'net-mdk', 'mcc-network', |
365 |
[ |
366 |
{ |
367 |
title => N("Manage your network devices"), |
368 |
list => [ |
369 |
"Network Center", |
370 |
"Add Connection", |
371 |
if_(!-x "$sbindir/draknetcenter", |
372 |
"Configure Internet", |
373 |
"Manage Connection", |
374 |
"Monitor Connection", |
375 |
), |
376 |
"Remove Interface", |
377 |
] |
378 |
}, |
379 |
{ |
380 |
title => N("Personalize and Secure your network"), |
381 |
list => [ |
382 |
"Proxy Configuration", |
383 |
"Connection Sharing", |
384 |
"Network Profiles", |
385 |
"VPN", |
386 |
] |
387 |
}, |
388 |
{ |
389 |
title => N("Others"), |
390 |
list => [ |
391 |
"Hosts", |
392 |
] |
393 |
}, |
394 |
], |
395 |
], |
396 |
|
397 |
[ |
398 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
399 |
N("System"), 'system-mdk', 'mcc-system', |
400 |
[ |
401 |
{ |
402 |
title => N("Manage system services"), |
403 |
list => [ |
404 |
if_(!$is_auth_wizard_installed, "Authentication"), |
405 |
"Menus", |
406 |
"Services", |
407 |
"Fonts", |
408 |
] |
409 |
}, |
410 |
{ |
411 |
title => N("Localization"), |
412 |
list => [ |
413 |
"Date & Time", |
414 |
"Localization", |
415 |
] |
416 |
}, |
417 |
{ |
418 |
title => N("Administration tools"), |
419 |
list => [ |
420 |
"Logs", |
421 |
if_($ENV{LANGUAGE} !~ /^zh/, "Console"), |
422 |
"Users", |
423 |
"Migration", |
424 |
if_(-x "$xbindir/drakcronat", "Programs scheduling"), |
425 |
"Backups", |
426 |
'Snapshots', |
427 |
"Virtualization", |
428 |
] |
429 |
}, |
430 |
] |
431 |
], |
432 |
|
433 |
[ |
434 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
435 |
N("Network Sharing"), 'network-sharing-mdk', 'mcc-networksharing', |
436 |
[ |
437 |
{ |
438 |
title => N("Configure Windows(R) shares"), |
439 |
list => [ |
440 |
"Access Windows shares", |
441 |
"Samba configuration", |
442 |
] |
443 |
}, |
444 |
{ |
445 |
title => N("Configure NFS shares"), |
446 |
list => [ |
447 |
"NFS mount points", |
448 |
"NFS exports", |
449 |
] |
450 |
}, |
451 |
{ |
452 |
title => N("Configure WebDAV shares"), |
453 |
list => [ |
454 |
"WebDAV mount points", |
455 |
] |
456 |
}, |
457 |
], |
458 |
], |
459 |
|
460 |
[ |
461 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
462 |
N("Local disks"), 'partition-mdk', 'mcc-localdisks', |
463 |
[ |
464 |
{ |
465 |
title => N("Local disks"), |
466 |
list => [ |
467 |
"Hard Drives", |
468 |
(map { |
469 |
my ($type, $scan, $text_orig, $long_text) = @$_; |
470 |
map_index { |
471 |
my $text = sprintf(translate($text_orig), $_->{info}); |
472 |
my $full_name = $text . ($::i ? $::i + 1 : ''); |
473 |
$programs{$full_name} = |
474 |
{ |
475 |
binary => "$sbindir/diskdrake --removable=$_->{device}", |
476 |
embedded => 1, |
477 |
description => $text, |
478 |
long_description => sprintf(translate($long_text), $_->{info}), |
479 |
icon => "diskdrake_$type", |
480 |
}; |
481 |
$full_name; |
482 |
} $scan->(); |
483 |
} do { |
484 |
my %cdroms_by_type; |
485 |
foreach (detect_devices::cdroms()) { |
486 |
my $type = detect_devices::isBurner($_) ? 'burner' : detect_devices::isDvdDrive($_) ? 'DVD' : 'cdrom'; |
487 |
push @{$cdroms_by_type{$type}}, $_; |
488 |
} |
489 |
([ 'cdrom', sub { @{$cdroms_by_type{cdrom} || []} }, N_("CD-ROM (%s)",), |
490 |
N_("Set where your \"%s\" CD-ROM drive is mounted"), |
491 |
], |
492 |
[ 'dvd', sub { @{$cdroms_by_type{DVD} || []} }, N_("DVD-ROM (%s)"), |
493 |
N_("Set where your \"%s\" DVD-ROM drive is mounted"), |
494 |
], |
495 |
[ 'cdwriter', sub { @{$cdroms_by_type{burner} || []} }, N_("CD/DVD burner (%s)"), |
496 |
N_("Set where your \"%s\" CD/DVD burner is mounted"), |
497 |
], |
498 |
[ 'zip', \&detect_devices::zips, N_("ZIP drive"), |
499 |
N_("Set where your ZIP drive is mounted"), |
500 |
], |
501 |
); |
502 |
}), |
503 |
"Partition Sharing", |
504 |
] |
505 |
}, |
506 |
] |
507 |
], |
508 |
|
509 |
[ |
510 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
511 |
N("Security"), 'security-mdk', 'mcc-security', |
512 |
[ |
513 |
{ |
514 |
title => N("Security"), |
515 |
list => [ |
516 |
"Security Level", |
517 |
"Tomoyo Policy", |
518 |
"Firewall", |
519 |
"Mageia Tools Authentication", |
520 |
"Invictus Firewall", |
521 |
"Parental Controls", |
522 |
] |
523 |
}, |
524 |
] |
525 |
], |
526 |
|
527 |
[ |
528 |
#-PO: please keep the following message very short: it must fit in the left list of MCC!!! |
529 |
N("Boot"), 'drakboot-mdk', 'mcc-boot', |
530 |
[ |
531 |
{ |
532 |
title => N("Configure boot steps"), |
533 |
list => [ |
534 |
#if_(detect_devices::floppies, "Boot Disk"), # kernel is too big |
535 |
"Auto login Config", |
536 |
"Boot Config", |
537 |
"Display Manager chooser", |
538 |
] |
539 |
}, |
540 |
{ |
541 |
title => N("Boot look'n feel"), |
542 |
list => [ |
543 |
#"Boot Theme", |
544 |
] |
545 |
}, |
546 |
{ |
547 |
title => N("Others"), |
548 |
list => [ |
549 |
if_(0, "Auto Install"), |
550 |
] |
551 |
}, |
552 |
] |
553 |
], |
554 |
); |
555 |
|
556 |
if (scalar glob_("$more_wizard_dir/*.conf")) { |
557 |
my @leaf = ( |
558 |
N("Additional wizards"), |
559 |
'wizard-mdk', |
560 |
[] |
561 |
); |
562 |
|
563 |
foreach my $file (glob_("$more_wizard_dir/*.conf")) { |
564 |
next unless -f $file; |
565 |
my %tmp = getVarsFromSh($file); |
566 |
$programs{$tmp{NAME}} = { |
567 |
binary => "$sbindir/drakwizard " . lc($file), |
568 |
embedded => 1, |
569 |
description => $tmp{DESCRIPTION}, |
570 |
icon => $tmp{ICON} || 'wizard-mdk', |
571 |
long_description => $tmp{LONG_DESCRIPTION}, |
572 |
}; |
573 |
push(@{$leaf[2]}, $tmp{NAME}); |
574 |
} |
575 |
push(@tree, \@leaf); |
576 |
} |
577 |
|
578 |
#------------------------------------------------------------- |
579 |
# let build the GUI |
580 |
|
581 |
|
582 |
my $align = mygtk2::text_direction_rtl() ? "right" : "left"; |
583 |
my $align2 = mygtk2::text_direction_rtl() ? "right" : "left"; |
584 |
|
585 |
|
586 |
# main window : |
587 |
|
588 |
my ($timeout, %check_boxes, $emb_socket); |
589 |
|
590 |
# set default size: |
591 |
|
592 |
my $window_global = gtkset_size_request(Gtk2::Window->new('toplevel'), $min_width, $min_height); |
593 |
mygtk2::register_main_window($window_global); |
594 |
|
595 |
if ($geometry) { |
596 |
@h{qw(HEIGTH WIDTH)} = $geometry =~ /(\d+)x(\d+)/; |
597 |
my ($x, $y) = $geometry =~ /([+-]\d+)([+-]\d+)/; |
598 |
$window_global->set_uposition($x, $y) if $x || $y; |
599 |
} else { |
600 |
} |
601 |
$window_global->resize(max($default_width, $h{WIDTH}, $min_width), max($h{HEIGTH}, $default_height, $min_height)); |
602 |
|
603 |
$window_global->set_icon(gtkcreate_pixbuf("/usr/share/icons/drakconf.png")); |
604 |
|
605 |
my $pending_app = 0; |
606 |
|
607 |
my $help_on_context = 'drakconf-intro'; |
608 |
|
609 |
my @themes = grep { -d "$themes_dir/$_" } all($themes_dir); |
610 |
|
611 |
#-PO Translators, please keep all "/" charaters !!! |
612 |
my %options = ( |
613 |
'show_log' => [ N("/_Options"), N("/Display _Logs") ], |
614 |
'embedded_mode' => [ N("/_Options"), N("/_Embedded Mode") ], |
615 |
'wiz_expert' => [ N("/_Options"), N("/Expert mode in _wizards") ], |
616 |
); |
617 |
|
618 |
my $mga_rel = common::mageia_release(); |
619 |
|
620 |
my ($steps, $view); |
621 |
|
622 |
my $release = mageia_release_info(); |
623 |
|
624 |
my @menu_items = ( |
625 |
[ N("/_File"), undef, undef, undef, '<Branch>' ], |
626 |
[ N("/_File") . N("/_Quit"), |
627 |
#-PO: "<control>" must _NOT_ be translated. This is a keyboard shortcut for "Quit". |
628 |
#-PO: you just have to select the proper letter for your language (eg: english: "Quit" => "Q") |
629 |
N("<control>Q"), \&quit_global, undef, '<Item>', N("Quit") ], |
630 |
[ N("/_Options"), undef, undef, undef, '<Branch>' ], |
631 |
[ join('', @{$options{show_log}}), undef, |
632 |
sub { |
633 |
$option_values{show_log} = $check_boxes{show_log}->get_active; |
634 |
if ($option_values{show_log}) { |
635 |
start_logdrake(); |
636 |
} else { |
637 |
kill_logdrake(); |
638 |
} |
639 |
}, |
640 |
undef, '<CheckItem>' |
641 |
], |
642 |
|
643 |
if_(0 && $isWiz, |
644 |
[ join('', @{$options{wiz_expert}}), undef, |
645 |
sub { $option_values{expert_wizard} = $check_boxes{wiz_expert}->get_active }, |
646 |
undef, '<CheckItem>', |
647 |
], |
648 |
), |
649 |
if_(@themes > 1, |
650 |
[ N("/_Themes"), undef, undef, undef, '<Branch>' ], |
651 |
(map { |
652 |
my $name = $_; |
653 |
[ N("/_Themes") . "/" . ($name eq $theme ? " O " : " ") . "_$_", undef, |
654 |
sub { |
655 |
return if $theme eq $name; |
656 |
!$pending_app || splash_warning(N("This action will restart the control center.\nAny change not applied will be lost."), 1) and do { |
657 |
# embedded app must be killed |
658 |
kill_children(); |
659 |
kill_logdrake(); |
660 |
child_just_exited(); |
661 |
exec "$0 --theme $name"; |
662 |
}; |
663 |
}, undef, '<CheckItem>' |
664 |
]; |
665 |
} @themes), |
666 |
[ N("/_Themes") . N("/_More themes"), undef, \&more_themes, undef, '<Item>' ] |
667 |
), |
668 |
[ N("/_Help"), undef, undef, undef, '<Branch>' ], |
669 |
[ N("/_Help") . N("/_Help"), undef, \&run_help, undef, '<Item>', N("Help") ], |
670 |
[ N("/_Help") . N("/_Release notes"), undef, sub { run_browser('Release_Notes') }, undef, '<Item>', N("Help") ], |
671 |
[ N("/_Help") . N("/What's _New?"), undef, sub { run_browser("What's New?") }, undef, '<Item>', N("Help") ], |
672 |
[ N("/_Help") . N("/_Errata"), undef, sub { run_browser('Errata') }, undef, '<Item>', N("Help") ], |
673 |
[ N("/_Help") . N("/_Report Bug"), undef, sub { |
674 |
run_program::raw({ detach => 1, as_user => 1 }, 'drakbug', '--report', (split(/\s/, $pending_app))[0] || 'drakconf'); |
675 |
}, undef, '<Item>' ], |
676 |
[ N("/_Help") . N("/_About..."), undef, \&about_mga_cc, undef, '<Item>' ] |
677 |
); |
678 |
|
679 |
|
680 |
sub run_help() { |
681 |
run_program::raw({ detach => 1, as_user => 1 }, 'drakhelp', '--id', $help_on_context); |
682 |
} |
683 |
|
684 |
sub run_browser { |
685 |
my ($url) = @_; |
686 |
$url = "http://wiki.mageia.org/en/$release->{distribution}_$release->{version}_$url" if $url !~ m!^http://!; |
687 |
run_program::raw({ detach => 1, as_user => 1 }, '/usr/bin/www-browser', $url); |
688 |
} |
689 |
|
690 |
my ($menu, $factory) = create_factory_menu($window_global, @menu_items); |
691 |
|
692 |
# to retrieve a path, one must prevent "accelerators completion": |
693 |
sub get_path { join('', map { my $i = $_; $i =~ s/_//g; $i } @_) } |
694 |
|
695 |
%check_boxes = map { |
696 |
$_ => $factory->get_widget("<main>" . get_path(@{$options{$_}})); |
697 |
} ('show_log', if_(0 && $isWiz, "wiz_expert")); |
698 |
|
699 |
my @buttons; |
700 |
my $offset = 15; |
701 |
|
702 |
$view = gtknew('WebKit_View', no_popup_menu => 1); |
703 |
# so that it exists when building steps: |
704 |
my $banner_notebook = Gtk2::Notebook->new; |
705 |
build_list(); |
706 |
|
707 |
gtkadd($window_global, |
708 |
gtkpack_(Gtk2::VBox->new(0, 0), |
709 |
0, $menu, |
710 |
#0, $banner_notebook, |
711 |
0, Gtk2::HSeparator->new, |
712 |
# 0, gtkset_size_request(Gtk2::VBox->new(10, 10), -1, 2), |
713 |
1, gtkpack_(Gtk2::HBox->new(0, 0), |
714 |
1, $steps = gtknew('MDV_Notebook', parent_window => $window_global, children => [ #Layout Fixed |
715 |
# 145 is the vertical offset in order to be below the actual logo: |
716 |
[ gtknew('VBox', widget_name => 'Steps', spacing => 0, width => (192 - $offset), |
717 |
children_tight => [ map { |
718 |
gtknew('HBox', spacing => 0, children => [ |
719 |
1, $_, |
720 |
0, gtknew('Alignment', width => 12), |
721 |
]); |
722 |
} @buttons ]), 0, 100 ], |
723 |
], right_child => gtknew('ScrolledWindow', child => gtkset_border_width($view, 5), |
724 |
no_shadow => 1, h_policy => 'never')), |
725 |
1, gtkpack(my $emb_box = Gtk2::VBox->new(0, 0), |
726 |
my $wait_darea = gtkset_size_request(Gtk2::DrawingArea->new, -1, -1), |
727 |
), |
728 |
), |
729 |
0, Gtk2::HSeparator->new, |
730 |
0, my $buttons = gtkadd(gtkset_layout(Gtk2::HButtonBox->new, 'start'), |
731 |
map { gtkset_border_width($_, 3) } |
732 |
gtksignal_connect(my $cancel = Gtk2::Button->new(N("Cancel")), |
733 |
clicked => sub { |
734 |
stop_wait_area(); |
735 |
kill_children(); |
736 |
child_just_exited(); |
737 |
}), |
738 |
), |
739 |
) |
740 |
); |
741 |
|
742 |
$view->set_size_request(-1, -1); |
743 |
|
744 |
$window_global->signal_connect(delete_event => \&quit_global); |
745 |
|
746 |
my $accel = Gtk2::AccelGroup->new; |
747 |
$accel->connect(Gtk2::Gdk->keyval_from_name('F1'), [], ['visible'], \&run_help); |
748 |
$window_global->add_accel_group($accel); |
749 |
|
750 |
use POSIX qw(:sys_utsname_h :math_h :sys_wait_h :unistd_h); |
751 |
my (undef, $nodename) = POSIX::uname(); |
752 |
$window_global->set_title(N("%s Control Center %s [on %s]", $branding, $version, $nodename)); |
753 |
$window_global->set_position('center'); |
754 |
|
755 |
foreach my $notebook ($banner_notebook) { |
756 |
$notebook->set_property('show-border', 0); |
757 |
$notebook->set_property('show-tabs', 0); |
758 |
} |
759 |
|
760 |
|
761 |
# banner : |
762 |
|
763 |
#add2notebook($banner_notebook, "", Gtk2::Banner->new("/usr/share/icons/large/drakconf.png", |
764 |
# N("Welcome to the %s Control Center", $branding))); |
765 |
|
766 |
|
767 |
my ($_hand_cursor, $wait_cursor) = map { Gtk2::Gdk::Cursor->new($_) } qw(hand2 watch); |
768 |
|
769 |
my $left_locked = 0; |
770 |
|
771 |
#my $spacing = 25; |
772 |
|
773 |
my %tool_callbacks; |
774 |
|
775 |
my ($page_count); |
776 |
|
777 |
|
778 |
my $conf_file = '/etc/sysconfig/mcc.conf'; |
779 |
foreach (cat_($conf_file)) { |
780 |
#s/^ENABLE_//; |
781 |
#my ($key, $val) = /^(.*)=(.*)/; |
782 |
if (my ($key, $val) = /^ENABLE_(.*)=(.*)/) { |
783 |
$key =~ s/_/ /g; |
784 |
#warn "--> ($key, $val)\n"; |
785 |
$programs{$key}{disabled} = 1 if $val ne 'yes'; |
786 |
warn ">> disabling $key\n" if $val ne 'yes'; |
787 |
} else { |
788 |
warn "bogus line in $conf_file: $_\n"; |
789 |
next; |
790 |
} |
791 |
} |
792 |
|
793 |
sub load_packages2install() { |
794 |
my $progs_conf_file = '/usr/share/mcc/progs.conf'; |
795 |
foreach (cat_($progs_conf_file)) { |
796 |
#if (my ($key, $val) = /^USE_WRAPPER_FOR_(.*)=(.*)/) { |
797 |
if (my ($key, $val) = /^INSTALL_FOR_(.*)=(.*)/) { |
798 |
$key =~ s/_/ /g; |
799 |
# we'll use gurpmi in order to install missing packages if needed; |
800 |
$programs{$key}{packages2install} = $val if $val; |
801 |
} else { |
802 |
warn "bogus line in $conf_file: $_\n"; |
803 |
next; |
804 |
} |
805 |
} |
806 |
} |
807 |
|
808 |
sub clean_list { |
809 |
my ($subtree) = @_; |
810 |
grep { |
811 |
my $stuff = $_; |
812 |
my $exec = real_bin_path($programs{$stuff}{binary}); |
813 |
my $is_present = -x $exec; |
814 |
if (!$is_present && $programs{$stuff}{packages2install}) { |
815 |
$is_present = 1; |
816 |
# override missing icon: |
817 |
$programs{$stuff}{real_icon} ||= $programs{$stuff}{icon}; |
818 |
$programs{$stuff}{icon} = 'installremoverpm'; |
819 |
} elsif ($programs{$stuff}{real_icon}) { |
820 |
$programs{$stuff}{icon} = $programs{$stuff}{real_icon}; |
821 |
} |
822 |
# do not complain about missing entries in move: |
823 |
warn qq("$exec" is not executable [$stuff]) if $mga_rel !~ /Move/ && !$is_present && !$programs{$stuff}{disabled}; |
824 |
!$programs{$stuff}{hidden} && $is_present && !$programs{$stuff}{disabled}; |
825 |
} @$subtree; |
826 |
} |
827 |
|
828 |
sub build_widget_element { |
829 |
my ($label) = @_; |
830 |
my $icon = $programs{$label}{icon}; |
831 |
|
832 |
die "$label 's icon is missing" if !$programs{$label} && $::testing; |
833 |
$tool_callbacks{$label} = sub { |
834 |
run_tool($label, undef, #$event_box, |
835 |
$icon, $programs{$label}{description}, $programs{$label}); |
836 |
}; |
837 |
# FIX ME: DO THIS AGAIN: |
838 |
$tool_feedback{$label} = sub {}; #sub { $event_box->window && $event_box->window->set_cursor($hand_cursor) }; |
839 |
my $real_icon = $icon ? '<IMG SRC="' . mygtk2::_find_imgfile($icon) . '">' : ''; |
840 |
my @widgets = ( |
841 |
qq(<TD class="img">$real_icon</TD>), |
842 |
qq(<TD style="text-align: $align"><DIV>) |
843 |
. escape_text_for_TextView_markup_format($programs{$label}{description}) |
844 |
. qq(</DIV></TD>) |
845 |
); |
846 |
qq(<TD WIDTH="49%"> |
847 |
<A HREF="$label"><TABLE align="$align2" ID="subtable"><TR> |
848 |
@widgets |
849 |
</TR></TABLE></A></TD> |
850 |
); |
851 |
} |
852 |
|
853 |
my (@strings, $current_string_idx); |
854 |
sub load_view() { |
855 |
$view->load_html_string($strings[$current_string_idx], 'file:///'); |
856 |
} |
857 |
|
858 |
sub build_list() { |
859 |
my $index = 0; |
860 |
load_packages2install(); |
861 |
my $i; |
862 |
foreach (@tree) { |
863 |
my ($text, $_icon, $help, $subtrees) = @$_; |
864 |
|
865 |
my @subtrees = grep { $_->{list} = [ clean_list($_->{list}) ]; !is_empty_array_ref($_->{list}) } @$subtrees; |
866 |
|
867 |
|
868 |
# Skip empty classes: |
869 |
next if !@subtrees; |
870 |
$i++; |
871 |
|
872 |
|
873 |
my $my_index = $index++; |
874 |
my $square_icon_uri = mygtk2::_find_imgfile('cadre-ic'); |
875 |
my $back_img = mygtk2::_find_imgfile('right-white-background_right_part_768.png'); |
876 |
my $string = join("\n", qq(<HTML> |
877 |
<HEAD> |
878 |
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> |
879 |
<style type="text/css"> |
880 |
body { |
881 |
// background-color: #ecf0f2; |
882 |
// background-image: url($back_img); |
883 |
background-repeat: no-repeat; /*pas de répition de l'image*/ |
884 |
background-attachment: fixed; |
885 |
background-position: top left |
886 |
} |
887 |
p {text-align: $align} |
888 |
td {text-align: $align} |
889 |
#title { |
890 |
text-align: $align; |
891 |
color: #grey; |
892 |
font-weight: bold; |
893 |
margin-left: 13px; |
894 |
margin-top: 16px; |
895 |
margin-bottom: 8px; |
896 |
} |
897 |
#top_margin { |
898 |
margin-top: 30px; |
899 |
} |
900 |
#block { |
901 |
margin-left: 4px; |
902 |
margin-bottom: 24px; |
903 |
border-spacing: 8px 0px; |
904 |
} |
905 |
#subtable { |
906 |
border-spacing: 8px 8px; |
907 |
} |
908 |
td.img { |
909 |
background-image: url($square_icon_uri); |
910 |
background-repeat: no-repeat; |
911 |
background-position: center center; |
912 |
width: 60; height: 60; |
913 |
text-align: center; |
914 |
} |
915 |
hr { |
916 |
background-color: #eef2f6; |
917 |
margin-left: 21px; |
918 |
margin-right: 22px; |
919 |
height: 1; |
920 |
border: 0; |
921 |
} |
922 |
tr {text-align: $align} |
923 |
td { |
924 |
margin-left: 10px; |
925 |
margin-right: 10px; |
926 |
} |
927 |
), |
928 |
if_(mygtk2::text_direction_rtl(), " a { direction: rtl; unicode-bidi: embed }"), qq( |
929 |
a:link {color:black; text-decoration:none } |
930 |
a:visited {color:black; text-decoration:none } |
931 |
a:hover {color:grey } |
932 |
// a:active {color:black; text-decoration:none } |
933 |
</style> |
934 |
|
935 |
</HEAD> |
936 |
<BODY> |
937 |
)); |
938 |
|
939 |
|
940 |
|
941 |
# Create right notebook pages : |
942 |
|
943 |
my $section; |
944 |
foreach my $subtree (@subtrees) { |
945 |
$string .= $section++ ? "<hr NOSHADE>\n" : '<div ID="top_margin"></div>'; |
946 |
my $title = mygtk2::asteriskize($subtree->{title}); |
947 |
$string .= qq( |
948 |
<div ID="title">$title</div> |
949 |
<TABLE BORDER=0 WIDTH="100%" ID="block"> |
950 |
); |
951 |
if (@{$subtree->{list}} % 2) { |
952 |
my @widgets = (pop @{$subtree->{list}}, ""); |
953 |
@widgets = reverse @widgets if mygtk2::text_direction_rtl(); |
954 |
push @{$subtree->{list}}, @widgets; |
955 |
} |
956 |
$string .= join("\n", |
957 |
map { |
958 |
"<TR>" . join("\n", grep { defined $_ } @$_) . "</TR>" } |
959 |
# "<TR>" . join("\n<TD> </TD>\n", grep { defined $_ } @$_) . "</TR>" } |
960 |
group_by(2, map { $_ ? build_widget_element($_) : '<TD></TD>' } @{$subtree->{list}}) |
961 |
); |
962 |
|
963 |
$string .= "</TABLE>\n"; |
964 |
} |
965 |
# b/c we use Pango text markup language, we've to protect '&' caracter: |
966 |
$text =~ s/&/&/g; |
967 |
$page_count++; |
968 |
|
969 |
$string .= qq(</BODY></HTML>); |
970 |
if ($save_html) { |
971 |
mkdir_p('/tmp/mcc'); |
972 |
MDK::Common::File::output_utf8("/tmp/mcc/$i.html", $string); |
973 |
} |
974 |
|
975 |
$strings[$my_index] = $string; |
976 |
push @buttons, gtknew('Button', relief => 'none', child => |
977 |
gtknew('Label_Right', text_markup => qq(<small>$text</small>), ellipsize => 'end'), |
978 |
clicked => sub { |
979 |
$current_string_idx = $my_index; |
980 |
load_view(); |
981 |
$steps->move_selection($_[0]->child); |
982 |
$banner_notebook->set_current_page($my_index); |
983 |
$help_on_context = $help; |
984 |
}, |
985 |
); |
986 |
|
987 |
} |
988 |
} |
989 |
|
990 |
$view->signal_connect('navigation-requested' => |
991 |
sub { |
992 |
my (undef, undef, $request) = @_; |
993 |
my $res = 'ignore'; |
994 |
|
995 |
# get the actual program ID: |
996 |
my $url = eval { Glib::filename_from_uri($request->get_uri) }; |
997 |
$url =~ s!^/*!!; # not a path |
998 |
|
999 |
# workaround wekbit calling navigation-requested with "/": |
1000 |
return 'accept' if !$url; |
1001 |
|
1002 |
# url can be a translated UTF-8 string (eg: for CD/DVD entries): |
1003 |
c::set_tagged_utf8($url); |
1004 |
|
1005 |
# do not crash on eg: "reload": |
1006 |
if (!$tool_callbacks{$url}) { |
1007 |
warn "Warning: invalid tool name: $url\n"; |
1008 |
return $res; |
1009 |
} |
1010 |
$tool_callbacks{$url}->(); |
1011 |
# FIXME: the following code is currently useless: |
1012 |
# should we provide a way to kill buggy embedded programs ? |
1013 |
return $res if $left_locked; |
1014 |
warn_on_startup(); |
1015 |
return $res; |
1016 |
}); |
1017 |
|
1018 |
my %icons = ( |
1019 |
); |
1020 |
|
1021 |
# manage tools not present in MCC (localedrake, drakauth, ...): |
1022 |
foreach my $label (difference2([ keys %programs ], [ keys %tool_callbacks ])) { |
1023 |
my $text = $programs{$label}{description}; |
1024 |
$tool_callbacks{$label} = sub { run_tool($label, undef, $icons{$label} || 'wizard-mdk', $text, $programs{$label}) }; |
1025 |
$tool_feedback{$label} = sub {}; |
1026 |
} |
1027 |
|
1028 |
|
1029 |
foreach (keys %check_boxes) { |
1030 |
my $widget = $check_boxes{$_}; |
1031 |
if (defined $widget) { |
1032 |
$widget->set_active($option_values{$_}); |
1033 |
} else { |
1034 |
print STDERR qq(BUG with LANGUAGE "$ENV{LANGUAGE}" for "$_"\n); |
1035 |
err_dialog(N("Error"), N("There's a bug in translations of your language (%s) |
1036 |
|
1037 |
Please report that bug.", $ENV{LANGUAGE})); |
1038 |
} |
1039 |
} |
1040 |
|
1041 |
# "wait while launching a program" area : |
1042 |
|
1043 |
my ($run_pixbuf, $run_counter, $run_counter_add, %run_pixbufs); |
1044 |
|
1045 |
sub stop_wait_area() { |
1046 |
$wait_darea->hide; |
1047 |
Glib::Source->remove($timeout) if $timeout; |
1048 |
undef %run_pixbufs; |
1049 |
} |
1050 |
|
1051 |
$wait_darea->signal_connect(expose_event => sub { |
1052 |
my ($w) = @_; |
1053 |
return if !$wait_darea->realized; |
1054 |
return unless $run_pixbuf; # some people got an expose event before we start an embedded tool |
1055 |
return unless $wait_darea->{size}; |
1056 |
$wait_darea->{layout} ||= $wait_darea->create_pango_layout(N("Loading... Please wait")); |
1057 |
my $pixbuf = $run_pixbufs{$run_counter} ||= mygtk2::_pixbuf_render_alpha($run_pixbuf, $run_counter); |
1058 |
if (!$wait_darea->{first_expose}) { |
1059 |
$wait_darea->{first_expose} = 1; |
1060 |
my $size = $wait_darea->{size}; |
1061 |
my ($d_width, $d_height) = ($size->width, $size->height); |
1062 |
($w->{pix_width}, $w->{pix_height}) = ($pixbuf->get_width, $pixbuf->get_height); |
1063 |
my ($txt_width, $txt_height) = $wait_darea->{layout}->get_pixel_size; |
1064 |
$w->{pix_yy} = ($d_width - $w->{pix_width})/2; |
1065 |
$w->{pix_xx} = ($d_height - $w->{pix_height} - $txt_height)/2; |
1066 |
$w->{text_yy} = $w->{pix_yy} + ($w->{pix_width} - $txt_width)/2; |
1067 |
$w->{text_xx} = $w->{pix_xx} + $w->{pix_height} + 5; |
1068 |
} |
1069 |
$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); |
1070 |
my $style = $wait_darea->{style} ||= $wait_darea->get_style; |
1071 |
$wait_darea->window->draw_layout($style->text_gc('normal'), $w->{text_yy}, $w->{text_xx}, $w->{layout}); |
1072 |
$run_counter += $run_counter_add; |
1073 |
$run_counter_add = -$run_counter_add if $run_counter < 100 || 255-$run_counter_add < $run_counter; |
1074 |
}); |
1075 |
|
1076 |
gtkflush(); |
1077 |
|
1078 |
# display first page (we use a timeout so that Y position of widgets is know): |
1079 |
Glib::Timeout->add(100, sub { $buttons[0]->signal_emit('clicked'); 0 }); |
1080 |
|
1081 |
$window_global->show_all; |
1082 |
|
1083 |
hide_buttons(); |
1084 |
$emb_box->hide; |
1085 |
$wait_darea->realize; |
1086 |
|
1087 |
$SIG{USR1} = 'IGNORE'; |
1088 |
$SIG{USR2} = 'IGNORE'; |
1089 |
$SIG{TERM} = \&quit_global; |
1090 |
|
1091 |
|
1092 |
$window_splash->destroy; |
1093 |
undef $window_splash; |
1094 |
Glib::Timeout->add(400, sub { $SIG{CHLD} = \&sig_child; sig_child('CHLD', 1); 1 }); |
1095 |
|
1096 |
if ($program) { |
1097 |
if (my $sub = $tool_callbacks{$program}) { |
1098 |
Glib::Timeout->add(500, sub { $sub->(); 0 }); |
1099 |
} else { |
1100 |
err_dialog(N("Error"), N("Impossible to run unknown '%s' program", $program)); |
1101 |
|
1102 |
} |
1103 |
} |
1104 |
|
1105 |
Gtk2->main; |
1106 |
|
1107 |
|
1108 |
sub group_by { |
1109 |
my $nb = shift @_; |
1110 |
my @l; |
1111 |
for (my $i = 0; $i < @_; $i += $nb) { |
1112 |
push @l, [ map { $_[$_] } $i..$i+$nb-1 ]; # $_[$i], $_[$i+1], $_[$i+2] ]; |
1113 |
} |
1114 |
@l; |
1115 |
} |
1116 |
|
1117 |
sub warn_on_startup() { |
1118 |
if ($pending_app) { |
1119 |
return if !splash_warning(N("The modifications done in the current module won't be saved."), 1); |
1120 |
kill_children(); |
1121 |
child_just_exited(); |
1122 |
} |
1123 |
} |
1124 |
|
1125 |
#------------------------------------------------------------- |
1126 |
# socket/plug managment |
1127 |
|
1128 |
# called once embedded tool has exited |
1129 |
sub child_just_exited() { |
1130 |
$pending_app = 0; |
1131 |
$left_locked = 0; |
1132 |
if ($emb_socket) { |
1133 |
$emb_socket->destroy; |
1134 |
undef $emb_socket; |
1135 |
} |
1136 |
$emb_box->hide; |
1137 |
hide_buttons(); |
1138 |
$cancel->hide; |
1139 |
gtkset_mousecursor_normal(); |
1140 |
foreach my $w ($steps, $banner_notebook) { |
1141 |
$w->show; |
1142 |
} |
1143 |
|
1144 |
stop_wait_area(); |
1145 |
} |
1146 |
|
1147 |
sub hide_socket_and_clean() { |
1148 |
$emb_box->hide; |
1149 |
$pending_app = 0; |
1150 |
} |
1151 |
|
1152 |
sub create_hidden_socket { |
1153 |
my ($icon, $label) = @_; |
1154 |
my $banner; |
1155 |
gtkpack_($emb_box, |
1156 |
0, $banner = gtkpack__(Gtk2::VBox->new(0, 0), Gtk2::Banner->new($icon, $label), |
1157 |
Gtk2::HSeparator->new, |
1158 |
), |
1159 |
1, gtksignal_connect($emb_socket = Gtk2::Socket->new, 'plug-removed' => sub { |
1160 |
$menu->show if $application_driven_menu; |
1161 |
$banner->destroy; |
1162 |
child_just_exited(); |
1163 |
})); |
1164 |
$banner->hide; |
1165 |
# signal emitted when embedded apps begin to draw: |
1166 |
$emb_socket->signal_connect('plug-added' => sub { |
1167 |
$banner_notebook->hide; |
1168 |
if ($application_driven_menu) { |
1169 |
$menu->hide; |
1170 |
} else { |
1171 |
$banner->show; |
1172 |
} |
1173 |
stop_wait_area(); |
1174 |
$left_locked = 0; |
1175 |
hide_buttons(); |
1176 |
$buttons->hide; |
1177 |
return if !$emb_socket; |
1178 |
$emb_socket->show; |
1179 |
$emb_socket->can_focus(1); |
1180 |
$emb_socket->grab_focus; |
1181 |
#$emb_socket->window->XSetInputFocus; #only need by console (no more embedded until we've vte/zvt binding) |
1182 |
}); |
1183 |
$emb_box->set_focus_child($emb_socket); |
1184 |
$emb_socket->hide; |
1185 |
} |
1186 |
|
1187 |
|
1188 |
#------------------------------------------------------------- |
1189 |
# processes managment |
1190 |
|
1191 |
# embedded processes pid will be stocked there |
1192 |
my @pid_launched; |
1193 |
|
1194 |
# logdrake pid are stocked here |
1195 |
my $pid_exp; |
1196 |
|
1197 |
sub fork_ { |
1198 |
my ($prog, $o_pid_table) = @_; |
1199 |
$o_pid_table ||= \@pid_launched; |
1200 |
my $pid = fork(); |
1201 |
if (defined $pid) { |
1202 |
!$pid and do { exec($prog) or POSIX::_exit(1) }; # immediate exit, else forked gtk+ object destructors will badly catch up parent mcc |
1203 |
push @$o_pid_table, $pid; |
1204 |
return $pid; |
1205 |
} else { |
1206 |
splash_warning(N("cannot fork: %s", "$!")); |
1207 |
child_just_exited(); |
1208 |
} |
1209 |
} |
1210 |
|
1211 |
sub real_bin_path { |
1212 |
my ($prog) = @_; |
1213 |
first(split /\s+/, $prog); |
1214 |
} |
1215 |
|
1216 |
sub run_tool { |
1217 |
my ($label, $box, $icon, $text, $tool) = @_; |
1218 |
my ($exec, $gtkplug) = @$tool{qw(binary embedded)}; |
1219 |
$application_driven_menu = $tool->{application_driven_menu}; |
1220 |
return if $tool_pids{$label}; |
1221 |
my $will_run_gurpmi; |
1222 |
if (! -x real_bin_path($exec)) { |
1223 |
if ($tool->{packages2install}) { |
1224 |
# gurpmi doesn't support being embedded yet: |
1225 |
$gtkplug = -1; |
1226 |
$exec = join(' ', "$bindir/gurpmi", split(/\s/, $tool->{packages2install})); |
1227 |
$will_run_gurpmi = 1; |
1228 |
} else { |
1229 |
splash_warning(N("cannot fork and exec \"%s\" since it is not executable", $exec)); |
1230 |
return; |
1231 |
} |
1232 |
} |
1233 |
$exec .= " --summary" if $option_values{expert_wizard} && $exec =~ /drakwizard/; |
1234 |
my $embedded = $gtkplug != -1; # not "explicitely not embedded" |
1235 |
if ($embedded) { |
1236 |
$steps->hide; |
1237 |
create_hidden_socket($icon, $text); |
1238 |
$emb_box->show; |
1239 |
$emb_socket->realize; |
1240 |
$pending_app = $tool->{binary}; |
1241 |
if ($gtkplug > 0) { |
1242 |
$buttons->show; |
1243 |
$exec .= " --embedded " . $emb_socket->get_id; |
1244 |
$wait_darea->show; |
1245 |
$cancel->show; |
1246 |
$run_pixbuf = eval { gtkcreate_pixbuf($icon . "_128") }; |
1247 |
$run_counter = 255; |
1248 |
$run_counter_add = -5; |
1249 |
undef $wait_darea->{color}; |
1250 |
undef $wait_darea->{size}; |
1251 |
undef $wait_darea->{first_expose}; |
1252 |
$wait_darea->{not_yet_complete} = 1; |
1253 |
$timeout = Glib::Timeout->add(35, sub { |
1254 |
$wait_darea->{size} ||= $wait_darea->allocation; |
1255 |
my $style = $wait_darea->{style} ||= $wait_darea->get_style; |
1256 |
my $bg = $wait_darea->{bg} ||= $style->bg('normal'); |
1257 |
my $base = $wait_darea->{base} ||= $style->base('normal'); |
1258 |
my $color = $wait_darea->{color} ||= $base; |
1259 |
# $base -> $bg |
1260 |
my @colors = ([ $color->red, $bg->red ], |
1261 |
[ $color->green, $bg->green ], |
1262 |
[ $color->blue, $bg->blue ]); |
1263 |
if ($wait_darea->{not_yet_complete} && any { $_->[0] != $_->[1] } @colors) { |
1264 |
$wait_darea->window->set_background($color); |
1265 |
$wait_darea->{color} = gtkcolor(map { $_->[0] - min(300, $_->[0] - $_->[1]) } @colors); |
1266 |
$wait_darea->queue_draw; |
1267 |
} elsif ($wait_darea->{not_yet_complete}) { |
1268 |
# set exact color |
1269 |
$wait_darea->window->set_background($bg); |
1270 |
$wait_darea->queue_draw; |
1271 |
$wait_darea->{not_yet_complete} = 0; |
1272 |
} else { |
1273 |
my $w = $wait_darea; |
1274 |
$wait_darea->queue_draw_area($w->{pix_yy}, $w->{pix_xx}, $w->{pix_width}, $w->{pix_height}); |
1275 |
} |
1276 |
1; |
1277 |
}); |
1278 |
$left_locked = 1; |
1279 |
$buttons->show; |
1280 |
$tool_pids{$label} = fork_($exec); |
1281 |
} |
1282 |
} else { # not embedded |
1283 |
# fix #3415 when $gtkplug eq -1 |
1284 |
local $option_values{embedded} = 0; |
1285 |
$tool_pids{$label} = fork_($exec); |
1286 |
$gurpmi_pid = $tool_pids{$label} if $will_run_gurpmi; |
1287 |
} |
1288 |
start_logdrake(); |
1289 |
$box->window->set_cursor($wait_cursor) if $box; |
1290 |
} |
1291 |
|
1292 |
sub start_logdrake() { |
1293 |
# (re)start logdrake if needed |
1294 |
if ($option_values{show_log} && !$pid_exp) { |
1295 |
my $exec_log = "logdrake --explain=drakxtools"; |
1296 |
$pid_exp = fork_($exec_log, []); |
1297 |
} |
1298 |
|
1299 |
} |
1300 |
|
1301 |
sub kill_them_all { |
1302 |
foreach my $pid (@_) { |
1303 |
kill('TERM', $pid) if $pid; |
1304 |
} |
1305 |
} |
1306 |
|
1307 |
sub kill_children() { |
1308 |
kill_them_all(@pid_launched); |
1309 |
} |
1310 |
|
1311 |
sub kill_logdrake() { |
1312 |
kill_them_all($pid_exp) if $pid_exp; |
1313 |
} |
1314 |
|
1315 |
sub quit_global() { |
1316 |
return 1 if mygtk2::quit_popup(); |
1317 |
|
1318 |
if (@pid_launched) { |
1319 |
&kill_children(); |
1320 |
return 1; # tell gtk+ not to quit |
1321 |
} |
1322 |
&kill_children(); |
1323 |
&kill_logdrake(); |
1324 |
my ($x, $y) = $window_global->get_size; |
1325 |
setVarsInSh($conffile, { |
1326 |
LOGS => bool2text($option_values{show_log}), |
1327 |
EXPERT_WIZARD => bool2text($option_values{expert_wizard}), |
1328 |
HEIGTH => $y, |
1329 |
WIDTH => $x, |
1330 |
THEME => $theme, |
1331 |
}); |
1332 |
gtkset_mousecursor_normal(); |
1333 |
Gtk2::exit(0); |
1334 |
standalone::exit(0); |
1335 |
} |
1336 |
|
1337 |
|
1338 |
#------------------------------------------------------------- |
1339 |
# signals managment |
1340 |
|
1341 |
# got when child died and gone in zombie state |
1342 |
sub sig_child { |
1343 |
my ($_sig, $o_is_cleaner) = @_; |
1344 |
my $child_pid; |
1345 |
do { |
1346 |
$child_pid = waitpid(-1, POSIX::WNOHANG); |
1347 |
if (my $tool = find { $tool_pids{$_} eq $child_pid } keys %tool_pids) { |
1348 |
$tool_feedback{$tool}->(); |
1349 |
delete $tool_pids{$tool}; |
1350 |
@pid_launched = grep { $_ ne $child_pid } @pid_launched; |
1351 |
if ($child_pid == $gurpmi_pid) { |
1352 |
undef $gurpmi_pid; |
1353 |
|
1354 |
my @services; |
1355 |
if ($programs{$tool}{services_to_start_after_install}) { |
1356 |
@services = @{$programs{$tool}{services_to_start_after_install}}; |
1357 |
run_program::raw({ detach => 1 }, 'service', $_, 'restart') foreach @services; |
1358 |
} |
1359 |
|
1360 |
# refresh the icon list if some package got installed (eg: system-config-printer) |
1361 |
build_list(); |
1362 |
load_view(); |
1363 |
} |
1364 |
} |
1365 |
undef $pid_exp if $pid_exp eq $child_pid; |
1366 |
} while $child_pid > 0; |
1367 |
# child unexpectedly died (cleanup since child_just_exited won't be called by plug-removed since plug never was added) |
1368 |
return if $o_is_cleaner || !$left_locked; |
1369 |
child_just_exited(); |
1370 |
splash_warning(N("This program has exited abnormally")); |
1371 |
} |
1372 |
|
1373 |
|
1374 |
#------------------------------------------------------------- |
1375 |
# mcc dialog specific functions |
1376 |
|
1377 |
sub splash_warning { |
1378 |
my ($label, $o_cancel_button) = @_; |
1379 |
warn_dialog(N("Warning"), $label, { cancel => $o_cancel_button }); |
1380 |
} |
1381 |
|
1382 |
sub new_dialog { |
1383 |
my ($title, $o_no_button) = @_; |
1384 |
my $dialog = gtkset_border_width(Gtk2::Dialog->new, 10); |
1385 |
$dialog->set_transient_for($window_global); |
1386 |
$dialog->set_position('center-on-parent'); |
1387 |
$dialog->set_title($title); |
1388 |
$dialog->action_area->pack_start(gtkadd(Gtk2::HButtonBox->new, |
1389 |
gtksignal_connect(Gtk2::Button->new(N("Close")), clicked => sub { $dialog->destroy }) |
1390 |
), |
1391 |
0,0,0) unless $o_no_button; |
1392 |
gtkset_modal($dialog, 1); |
1393 |
} |
1394 |
|
1395 |
sub more_themes() { |
1396 |
my $window_about = new_dialog(N("More themes")); |
1397 |
gtkpack_($window_about->vbox, |
1398 |
0, Gtk2::Label->new(N("Getting new themes")), |
1399 |
0, gtkadd(gtkset_shadow_type(gtkset_border_width(Gtk2::Frame->new(N("Additional themes")), 10), 'etched_out'), |
1400 |
gtkpack(Gtk2::HBox->new(0, 5), |
1401 |
N("Get additional themes on www.damz.net"), |
1402 |
) |
1403 |
) |
1404 |
); |
1405 |
$window_about->show_all; |
1406 |
} |
1407 |
|
1408 |
sub about_mga_cc() { |
1409 |
my $window_about = new_dialog(N("About - %s Control Center", $branding)); |
1410 |
my $tree_model = Gtk2::TreeStore->new("Glib::String", "Glib::String", "Glib::String"); |
1411 |
my $credits = Gtk2::TextView->new; |
1412 |
my $list = Gtk2::TreeView->new_with_model($tree_model); |
1413 |
$list->can_focus(0); |
1414 |
each_index { $list->append_column(Gtk2::TreeViewColumn->new_with_attributes(undef, Gtk2::CellRendererText->new, 'text' => $::i)) } 0..1; |
1415 |
$list->set_headers_visible(0); |
1416 |
|
1417 |
foreach my $row ([ N("Authors: "), '' ], |
1418 |
[ '', 'Chmouel Boudjnah' ], |
1419 |
[ '', 'Damien "dam\'s" Krotkine', |
1420 |
#-PO "perl" here is the programming language |
1421 |
N("(perl version)") ], |
1422 |
[ '', 'Daouda Lo' ], |
1423 |
[ '', 'Thierry Vignaud' ], |
1424 |
[ '', 'Yves Duret' ], |
1425 |
[ '', '' ], |
1426 |
[ N("Artwork: "), '', '' ], |
1427 |
[ '', 'Anh-Van Nguyen' ], |
1428 |
|
1429 |
[ '', |
1430 |
#-PO If your language allows it, use eacute for first "e" and egrave for 2nd one. |
1431 |
N("Helene Durosini"), '<ln@mandriva.com>' ], |
1432 |
) { |
1433 |
$tree_model->append_set(undef, [ map_index { $::i => $_ } @$row ]); |
1434 |
} |
1435 |
|
1436 |
my ($previous_type, $not_first_title, $not_first_block); |
1437 |
my $locale = lang::read; |
1438 |
require Gtk2::Pango; |
1439 |
foreach my $line (grep { $_ ne "\n" && !/^#/ } cat_(top(glob("/usr/share/doc/mageia-release-*/CREDITS.$locale->{lang}")))) { |
1440 |
if (my ($type, $comment, $contributor) = split(/\|/, $line, 3)) { |
1441 |
last if !$type; |
1442 |
$comment =~ s/^ //; # fix initial space of first section (CREDITS format should be enhanced to specify lines that really are sections) |
1443 |
chomp($contributor); |
1444 |
if ($previous_type ne $type) { |
1445 |
gtktext_append($credits, [ [ join('', if_($not_first_title, "\n"), translate(common::to_utf8($type)), "\n"), |
1446 |
{ 'weight' => Gtk2::Pango->PANGO_WEIGHT_BOLD, scale => Gtk2::Pango->scale_large } ] ]); |
1447 |
$previous_type = $type; |
1448 |
$not_first_title = 1; |
1449 |
} |
1450 |
if ($contributor) { |
1451 |
gtktext_append($credits, |
1452 |
#-PO: this is used as "language: translator" in credits part of the about dialog: |
1453 |
N("- %s: %s\n", map { translate($_) } $comment, $contributor) |
1454 |
); |
1455 |
} else { |
1456 |
$comment = $comment . "\n" if !$not_first_block; # fix spacing before second title; |
1457 |
gtktext_append($credits, join('', if_($not_first_block, "- "), translate($comment))); |
1458 |
$not_first_block = 1; |
1459 |
} |
1460 |
} |
1461 |
} |
1462 |
|
1463 |
|
1464 |
# Give our translators the ability to show their family and |
1465 |
# friends that thez participated ... |
1466 |
|
1467 |
#-PO Add your Name here to find it in the About section in your language. |
1468 |
my $translator_name = N("_: NAME OF TRANSLATORS\nYour names"); |
1469 |
#-PO Add your E-Mail address here if you want to show it in the about doialog. |
1470 |
my $translator_email = N("_: EMAIL OF TRANSLATORS\nYour emails"); |
1471 |
if ($translator_name ne "Your names" && 0) { |
1472 |
$tree_model->append_set(undef, [ 0 => $_->[0], 1 => $_->[1] ]) foreach [ '', '' ], [ N("Translator: ") ]; |
1473 |
$tree_model->append_set(undef, [ 0 => $_->[0], 1 => $_->[1] ]) foreach [ '', $translator_name, $translator_email ]; |
1474 |
} |
1475 |
$list->get_selection->set_mode('none'); |
1476 |
|
1477 |
gtkpack_($window_about->vbox, |
1478 |
(0, Gtk2::Banner->new('/usr/share/icons/large/drakconf.png', |
1479 |
#-PO: Here, first %s will be replaced by 'Mageia' |
1480 |
#-PO: second %s will be replaced by the version (eg: "Mageia 1 (Free) Control Center") |
1481 |
N("%s %s (%s) Control Center", |
1482 |
$branding, @$release{qw(version product)}))), |
1483 |
0, Gtk2::Label->new(""), |
1484 |
0, Gtk2::Label->new( |
1485 |
#-PO: here %s is eg: "1999-2008" |
1486 |
N("Copyright (C) %s Mandriva SA", '1999-2008') . "\n" . N("Copyright (C) %s Mageia", '2011-2013') . "\n" ), |
1487 |
0, Gtk2::HSeparator->new, |
1488 |
0, Gtk2::Label->new(""), |
1489 |
1, my $n = Gtk2::Notebook->new, |
1490 |
); |
1491 |
|
1492 |
add2notebook($n, N("Authors"), $list); |
1493 |
add2notebook($n, N("Mageia Contributors"), gtkset_size_request(create_scrolled_window($credits), 650, 50)); |
1494 |
$n->set_current_page(0); |
1495 |
|
1496 |
$window_about->show_all; |
1497 |
} |
1498 |
|
1499 |
|
1500 |
#------------------------------------------------------------- |
1501 |
# mcc specific graphic functions: |
1502 |
|
1503 |
|
1504 |
sub set_page { |
1505 |
my ($index) = @_; |
1506 |
start_logdrake(); |
1507 |
set_page_raw($index); |
1508 |
hide_buttons(); |
1509 |
} |
1510 |
|
1511 |
sub hide_buttons() { |
1512 |
$buttons->hide; |
1513 |
$cancel->hide; |
1514 |
} |
1515 |
|
1516 |
|
1517 |
sub create_packtable2 { |
1518 |
my ($options, @l) = @_; |
1519 |
my $w = Gtk2::Table->new(0, 0, 1); |
1520 |
add2hash_($options, { xpadding => 5, ypadding => 0 }); |
1521 |
each_index { |
1522 |
my ($i, $l) = ($::i, $_); |
1523 |
each_index { |
1524 |
my $j = $::i; |
1525 |
if ($_) { |
1526 |
ref $_ or $_ = gtknew('WrappedLabel', text => $_); |
1527 |
$w->attach($_, $j, $j + 1, $i, $i + 1, ['expand', 'fill'], ['expand', 'fill'], $options->{xpadding}, $options->{ypadding}); |
1528 |
$_->show; |
1529 |
} |
1530 |
} @$l; |
1531 |
} @l; |
1532 |
$w->set_col_spacings($options->{col_spacings} || 0); |
1533 |
$w->set_row_spacings($options->{row_spacings} || 0); |
1534 |
gtkset_border_width($w, $::isInstall ? 3 : 10); |
1535 |
} |
1536 |
|
1537 |
#------------------------------------------------------------- |
1538 |
# mcc specific graphic functions: |
1539 |
|
1540 |
sub render_shiner { |
1541 |
my ($pixbuf, $shine_value) = @_; |
1542 |
my $new_pixbuf = (mygtk2::_new_alpha_pixbuf($pixbuf))[2]; |
1543 |
$pixbuf->saturate_and_pixelate($new_pixbuf, $shine_value, 0); |
1544 |
$new_pixbuf; |
1545 |
} |
1546 |
|
1547 |
sub scale { |
1548 |
my ($pixbuf, $gain) = @_; |
1549 |
my ($width, $height) = ($pixbuf->get_height, $pixbuf->get_width); |
1550 |
$pixbuf->scale_simple($height+$gain, $width+$gain, 'hyper'); |
1551 |
} |
1552 |
|
1553 |
|
1554 |
1; |