fix typo
[clawsker.git] / clawsker
1 #!/usr/bin/perl -w
2 #
3 # $Id$
4 #
5
6 =pod
7
8 =head1 NAME
9
10 Clawsker - A Claws Mail Tweaker
11
12 =head1 SYNOPSIS
13
14 clawsker [options]
15
16 =head1 DESCRIPTION
17
18 Clawsker is an applet to edit the so called Claws Mail hidden preferences.
19
20 Claws Mail is a fast, lightweight and feature-rich MUA with a high number 
21 of configurable options. To keep the binary small and fast some of these 
22 preferences which are not widely used are not provided with a graphical
23 interface for inspection and/or modification.
24
25 Users wanting to edit such preferences had to face raw editing of their 
26 configuration files, now you can do it with a convenient GTK2 interface
27 using Clawsker.
28
29 =head1 OPTIONS
30
31 --help
32         Shows a brief help screen.
33
34 --version
35         Show information about program, Claws Mail and Perl-GTK versions.
36
37 --verbose
38         Tells more on the standard output.
39
40 --alternate-config-dir <dir>
41         Uses <dir> as Claws Mail configuration dir.
42
43 --clawsrc <file>
44         Uses <file> as Claws Mail resource configuration file. This sets
45         the full file name overriding any previous setting.
46
47 Multiple options are allowed, although only the last one has effect. Weird
48 option specifications may produce weird results (but otherwise correct).
49         
50 =head1 LIMITATIONS
51
52 A running Claws Mail cannot be detected if using the --clawsrc option because
53 the directory is not assumed to be a Claws Mail configuration dir. If that is 
54 the case use the --alternate-config-dir option instead.
55
56 =head1 AUTHOR
57
58 Ricardo Mones E<lt>ricardo@mones.orgE<gt>
59
60 =head1 LICENSE
61
62 Copyright (c) 2007-2009 by Ricardo Mones Lastra
63
64 This program is free software: you can redistribute it and/or modify
65 it under the terms of the GNU General Public License as published by
66 the Free Software Foundation, either version 3 of the License, or
67 (at your option) any later version.
68
69 This program is distributed in the hope that it will be useful,
70 but WITHOUT ANY WARRANTY; without even the implied warranty of
71 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
72 GNU General Public License for more details.
73
74 You should have received a copy of the GNU General Public License
75 along with this program.  If not, see E<lt>http://www.gnu.org/licenses/E<gt>.
76
77 =cut
78
79 use strict;
80 use encoding 'utf8';
81 use Glib qw(TRUE FALSE);
82 use Gtk2 -init;
83 use POSIX qw(setlocale);
84 use Locale::gettext;
85
86 my $NAME = 'clawsker';
87 my $PREFIX = '@PREFIX@';
88 my $LIBDIR = '@LIBDIR@';
89 my $VERSION = '@VERSION@';
90 my $VERBOSE = FALSE;
91 my $CLAWSV = undef;
92 my $main_window = undef;
93
94 my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG});
95 $locale = "C" unless defined($locale);
96 setlocale (LC_ALL, $locale);
97 bindtextdomain ($NAME, sprintf ('%s/share/locale', $PREFIX));
98 textdomain ($NAME);
99
100 my $SHOWHINTS = FALSE;
101 $SHOWHINTS = TRUE if ($Gtk2::VERSION >= 1.040 and Gtk2->CHECK_VERSION (2, 12, 0));
102
103 sub _ {
104     my $str = shift;
105     my %par = @_;
106     my $xla = gettext ($str);
107     if (scalar(keys(%par)) > 0) {
108         foreach my $key (keys %par) {
109             $xla =~ s/\{$key\}/$par{$key}/g;
110         }
111     }
112     return $xla;
113 }
114
115 # default messages
116 %xl::s = (
117     win_title => _('Claws Mail Hidden Preferences'),
118     about => _('About...'),
119     about_title => _('Clawsker :: A Claws Mail Tweaker'),
120     about_license => _('License:'),
121     about_version => _('Version:'),
122
123     tab_colours => _('Colours'),
124     tab_behaviour => _('Behaviour'),
125     tab_gui => _('GUI'),
126     tab_other => _('Other'),
127
128     ab_frame => _('Addressbook'),
129     mem_frame => _('Memory'),
130     msgview_frame => _('Message view'),
131     log_frame => _('Log window'),
132     dnd_frame => _('Drag \'n\' drop'),
133     ssl_frame => _('Secure Sockets Layer'),
134     msgs_frame => _('Messages'),
135     stripes_frame => _('Coloured stripes'),
136     sbar_frame => _('Scroll bars'),
137     mlist_frame => _('Message List'),
138     netm_frame => _('NetworkManager'),
139
140     l_oth_use_dlg => _('Use detached address book edit dialogue'),
141     h_oth_use_dlg => _('If true use a separate dialogue to edit a person\'s details. Otherwise will use a form embedded in the address book\'s main window.'),
142     l_oth_max_use => _('Maximum memory for message cache (kB)'),
143     h_oth_max_use => _('The maximum amount of memory to use to cache messages, in kilobytes.'),
144     l_oth_min_time => _('Minimun time for cache elements (minutes)'),
145     h_oth_min_time => _('The minimum time in minutes to keep a cache in memory. Caches more recent than this time will not be freed, even if the memory usage is too high.'),
146     l_oth_use_netm => _('Use NetworkManager'),
147     h_oth_use_netm => _('Use NetworkManager to switch offline automatically.'),
148     
149     l_gui_b_unread => _('Show unread messages with bold font'),
150     h_gui_b_unread => _('Show unread messages in the Message List using a bold font.'),
151     l_gui_no_markup => _('Don\'t use markup in compose window'),
152     h_gui_no_markup => _('Don\'t use bold and italic text in Compose dialogue\'s account selector.'),
153     l_gui_dot_lines => _('Use dotted lines in tree view components'),
154     h_gui_dot_lines => _('Use the old dotted line look in the main window tree views (Folder, Message and other lists) instead of the modern lineless look.'),
155     l_gui_h_scroll => _('Enable horizontal scrollbar'),
156     h_gui_h_scroll => _('Enable the horizontal scrollbar in the Message List.'),
157     l_gui_swp_from => _('Display To column instead From column in Sent folder'),
158     h_gui_swp_from => _('Display the recipient\'s email address in a To column of the Sent folder instead of the originator\'s one in a From column.'),
159     l_gui_v_scroll => _('Folder List scrollbar behaviour'),
160     h_gui_v_scroll => _('Specify the policy of vertical scrollbar of Folder List: show always, automatic or hide always.'),
161     l_gui_v_scroll_show => _('Show always'),
162     l_gui_v_scroll_auto => _('Automatic'),
163     l_gui_v_scroll_hide => _('Hide always'),
164     l_gui_strip_off => _('Coloured lines contrast'),
165     h_gui_strip_off => _('Specify the value to use when creating alternately coloured lines in tree view components. The smaller the value, the less visible the difference in the alternating colours of the lines.'),
166     l_gui_cursor_v => _('Show Cursor in message view'),
167     h_gui_cursor_v => _('Display the cursor in the message view.'),
168     l_gui_toolbar_d => _('Detachable toolbars'),
169     h_gui_toolbar_d => _('Show handles in the toolbars.'),
170     l_gui_strip_all => _('Use stripes in all tree view components'),
171     h_gui_strip_all => _('Enable alternately coloured lines in all tree view components.'),
172     l_gui_strip_sum => _('Use stripes in Folder List and Message List'),
173     h_gui_strip_sum => _('Enable alternately coloured lines in Message list and Folder list.'),
174     l_gui_two_line_v => _('2 lines per Message List item in 3-column layout'),
175     h_gui_two_line_v => _('Spread Message List information over two lines when using the three column mode.'),
176
177     l_beh_hover_t => _('Drag \'n\' drop hover timeout (ms)'),
178     h_beh_hover_t => _('Time in milliseconds that will cause a folder tree to expand when the mouse cursor is held over it during drag and drop.'),
179     l_beh_dangerous => _('Don\'t confirm deletions (dangerous!)'),
180     h_beh_dangerous => _('Don\'t ask for confirmation before definitive deletion of emails.'),
181     l_beh_flowed => _('Respect format=flowed in messages'),
182     h_beh_flowed => _('Respect format=flowed on text/plain message parts. This will cause some mails to have long lines, but will fix some URLs that would otherwise be wrapped.'),
183     l_beh_parts_rw => _('Allow writable temporary files'),
184     h_beh_parts_rw => _('Saves temporary files when opening attachment with write bit set.'),
185     l_beh_skip_ssl => _('Don\'t check SSL certificates'),
186     h_beh_skip_ssl => _('Disables the verification of SSL certificates.'),
187     l_beh_up_step => _('Progress bar update step (items)'),
188     h_beh_up_step => _('Update stepping in progress bars.'),
189     l_beh_thread_a => _('Maximum age when threading by subject (days)'),
190     h_beh_thread_a => _('Number of days to include a message in a thread when using "Thread using subject in addition to standard headers".'),
191     l_beh_unsafe_ssl => _('Allow unsafe SSL certificates'),
192     h_beh_unsafe_ssl => _('Allows Claws Mail to remember multiple SSL certificates for a given server/port.'),
193     l_beh_use_utf8 => _('Force UTF-8 for broken mails'),
194     h_beh_use_utf8 => _('Use UTF-8 encoding for broken mails instead of current locale.'),
195     l_beh_warn_dnd => _('Warn on drag \'n\' drop'),
196     h_beh_warn_dnd => _('Display a confirmation dialogue on drag \'n\' drop of folders.'),
197     l_beh_out_ascii => _('Ougoing messages fallback to ASCII'),
198     h_beh_out_ascii => _('If content allows, ASCII will be used to encode outgoing messages, otherwise the user-defined encoding is enforced always.'),
199     l_beh_pp_unsel => _('Primary paste unselects selection'),
200     h_beh_pp_unsel => _('Controls how pasting using middle-click changes the selected text and insertion point.'),
201
202     l_col_emphasis => _('X-Mailer header'),
203     h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
204     l_col_log_err => _('Error messages'),
205     h_col_log_err => _('Colour for error messages in log window.'),
206     l_col_log_in => _('Server messages'),
207     h_col_log_in => _('Colour for messages received from servers in log window.'),
208     l_col_log_msg => _('Standard messages'),
209     h_col_log_msg => _('Colour for messages in log window.'),
210     l_col_log_out => _('Client messages'),
211     h_col_log_out => _('Colour for messages sent to servers in log window.'),
212     l_col_log_warn => _('Warnings'),
213     h_col_log_warn => _('Colour for warning messages in log window.'),
214
215     e_error => _('Error: '),
216     e_noclawsrc => _('resource file for Claws Mail was not found.'),
217     e_running => _('seems Claws Mail is currently running, close it first.'),
218     e_requireddir => _('option requires a directory name.'),
219     e_requiredfile => _('option requires a file name.'),
220     e_notadir => _('specified name is not a directory or does not exist.'),
221     e_notafile => _('specified name is not a file or does not exist.'),
222 );
223
224 # all preferences read by load_preferences
225 my %PREFS = ();
226 # values of all preferences handled by clawsker
227 my %HPVALUE = ();
228 # default config dir and file name
229 my $ALTCONFIGDIR = FALSE;
230 my $CONFIGDIR = $ENV{HOME} . '/.claws-mail/';
231 my $CONFIGRC = 'clawsrc';
232
233 # index constants for preference arrays
234 use constant NAME  => 0; # the name on the rc file
235 use constant LABEL => 1; # the label on the GUI
236 use constant DESC  => 2; # the description for the hint/help
237 use constant TYPE  => 3; # data type: bool, int, float, string, color
238 use constant CMVER => 4; # lowest Claws Mail version the feature exists
239 use constant CMDEF => 5; # default value for the preference in Claws Mail
240 use constant GUI   => 6; # GUI element
241
242 # constants for GUI spacing
243 use constant HBOX_SPC => 5;
244 use constant FRAME_SPC => 2;
245 use constant PAGE_SPC => 5;
246
247 # version functions
248
249 sub version_greater() {
250   my ($version, $refvers) = @_;
251   my @version = split (/\./, $version);
252   my @refvers = split (/\./, $refvers);
253   while ($#version < $#refvers) {
254     push (@version, '0');
255   }
256   my $idx = 0;
257   while (($idx <= $#refvers) 
258          and (int ($version[$idx]) == int ($refvers[$idx]))) {
259     ++$idx;
260   }
261   return TRUE if (($idx > $#refvers) 
262                or (int ($version[$idx]) > int ($refvers[$idx])));
263   return FALSE;
264 }
265
266 sub get_claws_version() {
267     my @cmbin = (
268         'claws-mail',
269     );
270     my $res = "";
271     foreach my $bin (@cmbin) {
272         $_ = qx/which $bin/;
273         chomp;
274         last if ($_ ne "");
275     }
276     return "" unless ($_); # not installed
277     $_ = qx/$_ -v/;
278     chomp;
279     my @fver = split (/ /);
280     die "Invalid version string" unless ($fver[2] eq "version");
281     my @ver = split (/\./, $fver[3]);
282     $res .= "$ver[0].";
283     $res .= "$ver[1].";
284     if ($ver[2] =~ /(\d+)cvs(\d+)/) {
285         $res .= "$1.$2";
286     }
287     else {
288         $res .= "$ver[2].0";
289     }
290     return $res;
291 }
292
293 # data handlers and auxiliar functions
294
295 sub handle_bool_value {
296     my ($widget, $event, $dataref) = @_;
297     $$dataref = ($widget->get_active ())? '1': '0';
298 }
299
300 sub handle_int_value {
301     my ($widget, $event, $dataref) = @_;
302     $_ = $widget->get_text ();
303     s/^\s+//;
304     s/\s+$//;
305     if (/^[0-9]+$/) {
306         $$dataref = $_;
307         $widget->set_text ($_);
308     }
309     else {
310         $widget->set_text ($$dataref);
311     }
312 }
313
314 sub handle_string_value {
315     my ($widget, $event, $dataref) = @_;
316     $$dataref = $widget->get_text ();
317 }
318
319 sub gdk_color_from_str {
320     my ($str) = @_;
321     my ($rr, $gg, $bb) = (0, 0 ,0);
322     $_ = uc ($str);
323     if (/\#([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])/) {
324         $rr = hex($1) * 256;
325         $gg = hex($2) * 256; 
326         $bb = hex($3) * 256;
327     }
328     my $color = Gtk2::Gdk::Color->new ($rr, $gg, $bb);
329     return $color;
330 }
331
332 sub str_from_gdk_color {
333     my ($color) = @_;
334     my $rr = $color->red / 256;
335     my $gg = $color->green / 256;
336     my $bb = $color->blue / 256;
337     my $str = sprintf ("#%.2x%.2x%.2x", $rr, $gg, $bb);
338     return $str;
339 }
340
341 sub handle_color_value {
342     my ($widget, $event, $dataref) = @_;
343     my $newcol = $widget->get_color;
344     $$dataref = &str_from_gdk_color ($newcol);
345 }
346
347 sub handle_selection_value {
348     my ($widget, $event, $dataref) = @_;
349     $$dataref = $widget->get_active;
350 }
351
352 sub get_rc_filename {
353     return $CONFIGDIR . $CONFIGRC;
354 }
355
356 sub set_rc_filename {
357     my ($fullname) = @_;
358     my @parts = split ('/', $fullname);
359     $CONFIGRC = $parts[$#parts];
360     $parts[$#parts] = '';
361     $CONFIGDIR = join ('/', @parts);
362 }
363
364 sub log_message {
365     my ($mesg, $fatal) = @_;
366     if (defined($fatal) && $fatal eq 'die') {
367         die "$NAME: $mesg\n";
368     }
369     if ($VERBOSE) {
370         print "$NAME: $mesg\n";
371     }
372 }
373
374 sub error_dialog {
375     my ($emsg) = @_;
376     my $markup = "<span weight=\"bold\" size=\"large\">" . $emsg . "</span>";
377     my $errordlg = Gtk2::MessageDialog->new_with_markup ($main_window, 'modal', 'error', 'cancel', $markup);
378     $errordlg->set_title (_('Error message'));
379     $errordlg->run;
380     $errordlg->destroy;
381 }
382
383 sub check_claws_not_running() {
384     my $socket = (not $ALTCONFIGDIR)? "/tmp/": $CONFIGDIR;
385     $socket .= "claws-mail-$<";
386     -S $socket and do {
387         my $emsg = "$xl::s{e_error}$xl::s{e_running}";
388         log_message ($emsg);
389         error_dialog ($emsg);
390         return FALSE;
391      };
392
393      return TRUE;
394 }
395
396 sub check_rc_file() {
397     my ($rcfile) = @_;
398     (defined($rcfile) && -f $rcfile) or do {
399        my $emsg = "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
400         log_message ($emsg);
401         error_dialog ($emsg);
402         return FALSE;
403     };
404
405     return TRUE;
406 }
407
408 sub set_widget_hint() {
409     if ($SHOWHINTS) {
410         my ($wdgt, $hint) = @_;    
411         $wdgt->set_tooltip_text ($hint);
412         $wdgt->set_has_tooltip (TRUE);
413     }
414 }
415
416 # graphic element creation 
417
418 sub new_check_button_for {
419     my ($hash, $key) = @_;
420     my $name = $$hash{$key}[NAME];
421     my $label = $$hash{$key}[LABEL];
422     #
423     my $hbox = Gtk2::HBox->new (FALSE, 5);
424     my $cb = Gtk2::CheckButton->new ($label);
425     $$hash{$key}[GUI] = $cb;
426     if (defined ($HPVALUE{$name})) {
427         $cb->set_active ($HPVALUE{$name} eq '1');
428     }
429     $cb->signal_connect (clicked => sub {
430             my ($w, $e) = @_;
431             &handle_bool_value($w, $e, \$HPVALUE{$name});
432         });
433     &set_widget_hint ($cb, $$hash{$key}[DESC]);
434     $hbox->pack_start ($cb, FALSE, FALSE, HBOX_SPC);
435     #
436     return $hbox;
437 }
438
439 sub new_text_box_for_int {
440     my ($hash, $key) = @_;
441     my $name = $$hash{$key}[NAME];
442     my $label = $$hash{$key}[LABEL];
443     my @type = split (/,/, $$hash{$key}[TYPE]);
444     push (@type, 0), push (@type, 10000) unless ($#type > 0); 
445     #
446     my $hbox = Gtk2::HBox->new (FALSE, 5);
447     my $glabel = Gtk2::Label->new ($label);
448     my $pagei = int (($type[2] - $type[1]) / 10);
449     my $adjust = Gtk2::Adjustment->new (
450             $HPVALUE{$name}, $type[1], $type[2], 1, $pagei, 10
451         );
452     my $gentry = Gtk2::SpinButton->new ($adjust, 1, 0);
453     $gentry->set_numeric (TRUE);
454     $$hash{$key}[GUI] = $gentry;
455     $gentry->signal_connect('value-changed' => sub {
456             my ($w, $e) = @_;
457             &handle_int_value($w, $e, \$HPVALUE{$name});
458         });
459     &set_widget_hint ($gentry, $$hash{$key}[DESC]);
460     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
461     $hbox->pack_start ($gentry, FALSE, FALSE, HBOX_SPC);
462     #
463     return $hbox;
464 }
465
466 sub new_color_button_for {
467     my ($hash, $key) = @_;
468     my $name = $$hash{$key}[NAME];
469     my $label = $$hash{$key}[LABEL];
470     #
471     my $col = &gdk_color_from_str ($HPVALUE{$name});
472     my $hbox = Gtk2::HBox->new (FALSE, 5);
473     my $glabel = Gtk2::Label->new ($label);
474     my $button = Gtk2::ColorButton->new_with_color ($col);
475     $$hash{$key}[GUI] = $button;
476     $button->set_title ($label);
477     $button->set_relief ('none');
478     $button->signal_connect ('color-set' => sub {
479             my ($w, $e) = @_;
480             &handle_color_value($w, $e, \$HPVALUE{$name}); 
481         });
482     &set_widget_hint ($button, $$hash{$key}[DESC]);
483     $hbox->pack_start ($button, FALSE, FALSE, HBOX_SPC);
484     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
485     #
486     return $hbox;
487 }
488
489 sub new_selection_box_for {
490     my ($hash, $key) = @_;
491     my $name = $$hash{$key}[NAME];
492     my $label = $$hash{$key}[LABEL];
493     #
494     my $hbox = Gtk2::HBox->new (FALSE, 5);
495     my $glabel = Gtk2::Label->new ($label);
496     my $combo = Gtk2::ComboBox->new_text;
497     $$hash{$key}[GUI] = $combo;
498     my @options = split (';', $$hash{$key}[TYPE]);
499     foreach my $opt (@options) {
500         my ($index, $textkey) = split ('=', $opt);
501         $combo->insert_text ($index, $xl::s{$textkey});
502     }
503     $combo->signal_connect ('changed' => sub {
504             my ($w, $e) = @_;
505             &handle_selection_value($w, $e, \$HPVALUE{$name});
506         });
507     $combo->set_active ($HPVALUE{$name});
508     &set_widget_hint ($combo, $$hash{$key}[DESC]);
509     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
510     $hbox->pack_start ($combo, FALSE, FALSE, HBOX_SPC);
511     #
512     return $hbox;
513 }
514
515 # preference maps and corresponding page creation subs
516
517 %pr::oth = ( # other preferences
518     use_dlg => [ 
519         'addressbook_use_editaddress_dialog',
520         $xl::s{l_oth_use_dlg},
521         $xl::s{h_oth_use_dlg},
522         'bool',
523         '2.7.0',
524         '0',
525         undef,
526     ],
527     max_use => [
528         'cache_max_mem_usage',
529         $xl::s{l_oth_max_use},
530         $xl::s{h_oth_max_use},
531         'int,0,262144', # 0 Kb - 256 Mb
532         '0.0.0',
533         '4096',
534         undef,
535     ],
536     min_time => [
537         'cache_min_keep_time',
538         $xl::s{l_oth_min_time},
539         $xl::s{h_oth_min_time},
540         'int,0,120', # 0 minutes - 2 hours
541         '0.0.0',
542         '15',
543         undef,
544     ],
545     use_netm => [
546         'use_networkmanager',
547         $xl::s{l_oth_use_netm},
548         $xl::s{h_oth_use_netm},
549         'bool',
550         '3.3.1',
551         '1',
552         undef,
553     ],
554 );
555
556 sub new_other_page() {
557     my $of = Gtk2::VBox->new (FALSE, 5);
558     $of->set_border_width (PAGE_SPC);
559
560     my $ab_frame = Gtk2::Frame->new ($xl::s{ab_frame}); 
561     my $cb_use_dlg = &new_check_button_for(\%pr::oth, 'use_dlg');
562     my $vb1 = Gtk2::VBox->new (FALSE, 5);
563     $vb1->set_border_width (PAGE_SPC); 
564     $vb1->pack_start ($cb_use_dlg, FALSE, FALSE, 0);
565     $ab_frame->add ($vb1);
566
567     my $mem_frame = Gtk2::Frame->new ($xl::s{mem_frame}); 
568     my $tb_max_use = &new_text_box_for_int(\%pr::oth, 'max_use');
569     my $tb_min_time = &new_text_box_for_int(\%pr::oth, 'min_time');
570     my $vb2 = Gtk2::VBox->new (FALSE, 5);
571     $vb2->set_border_width (PAGE_SPC); 
572     $vb2->pack_start ($tb_max_use, FALSE, FALSE, 0);
573     $vb2->pack_start ($tb_min_time, FALSE, FALSE, 0);
574     $mem_frame->add ($vb2);
575
576     my $netm_frame = Gtk2::Frame->new ($xl::s{netm_frame});
577     my $cb_use_netm = &new_check_button_for(\%pr::oth, 'use_netm');
578     my $vb3 = Gtk2::VBox->new (FALSE, 5);
579     $vb3->set_border_width (PAGE_SPC);
580     $vb3->pack_start ($cb_use_netm, FALSE, FALSE, 0);
581     $netm_frame->add ($vb3);
582
583     $of->pack_start ($ab_frame, FALSE, FALSE, FRAME_SPC);
584     $of->pack_start ($mem_frame, FALSE, FALSE, FRAME_SPC);
585     $of->pack_start ($netm_frame, FALSE, FALSE, FRAME_SPC);
586
587     return $of;
588 }
589
590 %pr::gui = ( # gui bells and whistles
591     b_unread => [ 
592         'bold_unread',
593         $xl::s{l_gui_b_unread},
594         $xl::s{h_gui_b_unread},
595         'bool',
596         '0.0.0',
597         '1',
598         undef,
599     ],
600     no_markup => [
601         'compose_no_markup',
602         $xl::s{l_gui_no_markup},
603         $xl::s{h_gui_no_markup},
604         'bool',
605         '0.0.0',
606         '0',
607         undef,
608     ],
609     dot_lines => [
610         'enable_dotted_lines',
611         $xl::s{l_gui_dot_lines},
612         $xl::s{h_gui_dot_lines},
613         'bool',
614         '0.0.0',
615         '0',
616         undef,
617     ],
618     h_scroll => [
619         'enable_hscrollbar',
620         $xl::s{l_gui_h_scroll},
621         $xl::s{h_gui_h_scroll},
622         'bool',
623         '0.0.0',
624         '1',
625         undef,
626     ],
627     swp_from => [
628         'enable_swap_from',
629         $xl::s{l_gui_swp_from},
630         $xl::s{h_gui_swp_from},
631         'bool',
632         '0.0.0',
633         '0',
634         undef,
635     ],
636     v_scroll => [
637         'folderview_vscrollbar_policy',
638         $xl::s{l_gui_v_scroll},
639         $xl::s{h_gui_v_scroll},
640         '0=l_gui_v_scroll_show;1=l_gui_v_scroll_auto;2=l_gui_v_scroll_hide',
641         '0.0.0',
642         '0',
643         undef,
644     ],
645     strip_off => [
646         'stripes_color_offset',
647         $xl::s{l_gui_strip_off},
648         $xl::s{h_gui_strip_off},
649         'int,0,10000', # no idea what this number means
650         '0.0.0',
651         '4000',
652         undef,
653     ],
654     cursor_v => [
655         'textview_cursor_visible',
656         $xl::s{l_gui_cursor_v},
657         $xl::s{h_gui_cursor_v},
658         'bool',
659         '0.0.0',
660         '0',
661         undef,
662     ],
663     toolbar_d => [
664         'toolbar_detachable',
665         $xl::s{l_gui_toolbar_d},
666         $xl::s{h_gui_toolbar_d},
667         'bool',
668         '0.0.0',
669         '0',
670         undef,
671     ],
672     strip_all => [
673         'use_stripes_everywhere',
674         $xl::s{l_gui_strip_all},
675         $xl::s{h_gui_strip_all},
676         'bool',
677         '0.0.0',
678         '1',
679         undef,
680     ],
681     strip_sum => [
682         'use_stripes_in_summaries',
683         $xl::s{l_gui_strip_sum},
684         $xl::s{h_gui_strip_sum},
685         'bool',
686         '0.0.0',
687         '1',
688         undef,
689     ],
690     two_linev => [
691         'two_line_vertical',
692         $xl::s{l_gui_two_line_v},
693         $xl::s{h_gui_two_line_v},
694         'bool',
695         '3.4.0.7',
696         '0',
697         undef,
698     ],
699 );
700
701 sub new_gui_page() {
702     my $gf = Gtk2::VBox->new (FALSE, 5);
703     $gf->set_border_width (PAGE_SPC);
704
705     my $stripes_frame = Gtk2::Frame->new ($xl::s{stripes_frame});
706     my $cb_strip_all = &new_check_button_for (\%pr::gui, 'strip_all');
707     my $cb_strip_sum = &new_check_button_for (\%pr::gui, 'strip_sum');
708     my $tb_strip_off = &new_text_box_for_int (\%pr::gui, 'strip_off');
709     my $vb1 = Gtk2::VBox->new (FALSE, 5);
710     $vb1->set_border_width (PAGE_SPC);
711     $vb1->pack_start ($cb_strip_all, FALSE, FALSE, 0);
712     $vb1->pack_start ($cb_strip_sum, FALSE, FALSE, 0);
713     $vb1->pack_start ($tb_strip_off, FALSE, FALSE, 0);
714     $stripes_frame->add ($vb1);
715
716     my $mlist_frame = Gtk2::Frame->new ($xl::s{mlist_frame});
717     my $cb_b_unread = &new_check_button_for (\%pr::gui, 'b_unread');
718     my $cb_swp_from = &new_check_button_for (\%pr::gui, 'swp_from');
719     my $vb3 = Gtk2::VBox->new (FALSE, 5);
720     $vb3->set_border_width (PAGE_SPC);
721     $vb3->pack_start ($cb_b_unread, FALSE, FALSE, 0);
722     $vb3->pack_start ($cb_swp_from, FALSE, FALSE, 0);
723     $mlist_frame->add ($vb3);
724
725     my $sbar_frame = Gtk2::Frame->new ($xl::s{sbar_frame});
726     my $cb_h_scroll = &new_check_button_for (\%pr::gui, 'h_scroll');
727     my $sb_v_scroll = &new_selection_box_for (\%pr::gui, 'v_scroll');
728     my $vb2 = Gtk2::VBox->new (FALSE, 5);
729     $vb2->set_border_width (PAGE_SPC);
730     $vb2->pack_start ($cb_h_scroll, FALSE, FALSE, 0);
731     $vb2->pack_start ($sb_v_scroll, FALSE, FALSE, 0);
732     $sbar_frame->add ($vb2);
733
734     my $cb_no_markup = &new_check_button_for (\%pr::gui, 'no_markup'); 
735     my $cb_dot_lines = &new_check_button_for (\%pr::gui, 'dot_lines'); 
736     my $cb_cursor_v = &new_check_button_for (\%pr::gui, 'cursor_v');
737     my $cb_toolbar_d = &new_check_button_for (\%pr::gui, 'toolbar_d');
738     my $cb_two_linev = &new_check_button_for (\%pr::gui, 'two_linev');
739
740     $gf->pack_start ($stripes_frame, FALSE, FALSE, FRAME_SPC);
741     $gf->pack_start ($mlist_frame, FALSE, FALSE, FRAME_SPC);
742     $gf->pack_start ($cb_no_markup, FALSE, FALSE, 0);
743     $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
744     $gf->pack_start ($cb_cursor_v, FALSE, FALSE, 0);
745     $gf->pack_start ($cb_toolbar_d, FALSE, FALSE, 0);
746     $gf->pack_start ($cb_two_linev, FALSE, FALSE, 0);
747     $gf->pack_start ($sbar_frame, FALSE, FALSE, FRAME_SPC);
748
749     return $gf;
750 }
751
752 %pr::beh = ( # tweak some behaviour
753     hover_t => [
754         'hover_timeout',
755         $xl::s{l_beh_hover_t},
756         $xl::s{h_beh_hover_t},
757         'int,100,3000', # 0.1 seconds - 3 seconds
758         '0.0.0',
759         '500',
760         undef,
761     ],
762     dangerous => [
763         'live_dangerously',
764         $xl::s{l_beh_dangerous},
765         $xl::s{h_beh_dangerous},
766         'bool',
767         '0.0.0',
768         '0',
769         undef,
770     ],
771     flowed => [
772         'respect_flowed_format',
773         $xl::s{l_beh_flowed},
774         $xl::s{h_beh_flowed},
775         'bool',
776         '0.0.0',
777         '0',
778         undef,
779     ],
780     parts_rw => [
781         'save_parts_readwrite',
782         $xl::s{l_beh_parts_rw},
783         $xl::s{h_beh_parts_rw},
784         'bool',
785         '0.0.0',
786         '0',
787         undef,
788     ],
789     skip_ssl => [
790         'skip_ssl_cert_check',
791         $xl::s{l_beh_skip_ssl},
792         $xl::s{h_beh_skip_ssl},
793         'bool',
794         '0.0.0',
795         '0',
796         undef,
797     ],
798     up_step => [
799         'statusbar_update_step',
800         $xl::s{l_beh_up_step},
801         $xl::s{h_beh_up_step},
802         'int,1,200', # 1 item - 200 items
803         '0.0.0',
804         '10',
805         undef,
806     ],
807     thread_a => [
808         'thread_by_subject_max_age',
809         $xl::s{l_beh_thread_a},
810         $xl::s{h_beh_thread_a},
811         'int,1,30', # 1 day - 30 days
812         '0.0.0',
813         '10',
814         undef,
815     ],
816     unsafe_ssl => [
817         'unsafe_ssl_certs',
818         $xl::s{l_beh_unsafe_ssl},
819         $xl::s{h_beh_unsafe_ssl},
820         'bool',
821         '0.0.0',
822         '0',
823         undef,
824     ],
825     use_utf8 => [
826         'utf8_instead_of_locale_for_broken_mail',
827         $xl::s{l_beh_use_utf8},
828         $xl::s{h_beh_use_utf8},
829         'bool',
830         '0.0.0',
831         '0',
832         undef,
833     ],
834     warn_dnd => [
835         'warn_dnd',
836         $xl::s{l_beh_warn_dnd},
837         $xl::s{h_beh_warn_dnd},
838         'bool',
839         '0.0.0',
840         '1',
841         undef,
842     ],
843     out_ascii => [
844         'outgoing_fallback_to_ascii',
845         $xl::s{l_beh_out_ascii},
846         $xl::s{h_beh_out_ascii},
847         'bool',
848         '3.4.0.37',
849         '1',
850         undef,
851     ],
852     pp_unsel => [
853         'primary_paste_unselects',
854         $xl::s{l_beh_pp_unsel},
855         $xl::s{h_beh_pp_unsel},
856         'bool',
857         '3.6.1.35',
858         '0',
859         undef,
860     ],
861 );
862
863 sub new_behaviour_page() {
864     my $bf = Gtk2::VBox->new (FALSE, 5);
865     $bf->set_border_width (PAGE_SPC);
866
867     my $dnd_frame = Gtk2::Frame->new ($xl::s{dnd_frame});
868     my $tb_hoover_t = &new_text_box_for_int (\%pr::beh, 'hover_t');
869     my $cb_warn_dnd = &new_check_button_for (\%pr::beh, 'warn_dnd');
870     my $vb1 = Gtk2::VBox->new (FALSE, 5);
871     $vb1->set_border_width (PAGE_SPC);
872     $vb1->pack_start ($cb_warn_dnd, FALSE, FALSE, 0);
873     $vb1->pack_start ($tb_hoover_t, FALSE, FALSE, 0);
874     $dnd_frame->add ($vb1);
875
876     my $ssl_frame = Gtk2::Frame->new ($xl::s{ssl_frame});
877     my $cb_skip_ssl = &new_check_button_for (\%pr::beh, 'skip_ssl');
878     my $cb_unsafe_ssl = &new_check_button_for (\%pr::beh, 'unsafe_ssl');
879     my $hb1 = Gtk2::HBox->new (FALSE, 5);
880     $hb1->set_border_width (PAGE_SPC);
881     $hb1->pack_start ($cb_skip_ssl, FALSE, FALSE, 0);
882     $hb1->pack_start ($cb_unsafe_ssl, FALSE, FALSE, 0);
883     $ssl_frame->add ($hb1);
884
885     my $tb_up_step = &new_text_box_for_int (\%pr::beh, 'up_step');
886     my $tb_thread_a = &new_text_box_for_int (\%pr::beh, 'thread_a');
887
888     my $msgs_frame = Gtk2::Frame->new ($xl::s{msgs_frame});
889     my $cb_flowed = &new_check_button_for (\%pr::beh, 'flowed');
890     my $cb_parts_rw = &new_check_button_for (\%pr::beh, 'parts_rw');
891     my $cb_use_utf8 = &new_check_button_for (\%pr::beh, 'use_utf8');
892     my $cb_dangerous = &new_check_button_for (\%pr::beh, 'dangerous');
893     my $cb_out_ascii = &new_check_button_for (\%pr::beh, 'out_ascii');
894     my $cb_pp_unsel = &new_check_button_for (\%pr::beh, 'pp_unsel');
895     my $vb2 = Gtk2::VBox->new (FALSE, 5);    
896     $vb2->set_border_width (PAGE_SPC);
897     $vb2->pack_start ($cb_flowed, FALSE, FALSE, 0);
898     $vb2->pack_start ($cb_parts_rw, FALSE, FALSE, 0);
899     $vb2->pack_start ($cb_use_utf8, FALSE, FALSE, 0);
900     $vb2->pack_start ($cb_dangerous, FALSE, FALSE, 0);
901     $vb2->pack_start ($cb_out_ascii, FALSE, FALSE, 0);
902     $vb2->pack_start ($cb_pp_unsel, FALSE, FALSE, 0);
903     $msgs_frame->add ($vb2);
904
905     $bf->pack_start ($dnd_frame, FALSE, FALSE, FRAME_SPC);
906     $bf->pack_start ($ssl_frame, FALSE, FALSE, FRAME_SPC);
907     $bf->pack_start ($tb_up_step, FALSE, FALSE, 0);
908     $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0);
909     $bf->pack_start ($msgs_frame, FALSE, FALSE, FRAME_SPC);
910
911     return $bf;
912 }
913
914 %pr::col = ( # a variety of colours
915     emphasis => [
916         'emphasis_color',
917         $xl::s{l_col_emphasis},
918         $xl::s{h_col_emphasis},
919         'color',
920         '0.0.0',
921         '#0000cf',
922         undef,
923     ],
924     log_err => [
925         'log_error_color',
926         $xl::s{l_col_log_err},
927         $xl::s{h_col_log_err},
928         'color',
929         '0.0.0',
930         '#af0000',
931         undef,
932     ],
933     log_in => [
934         'log_in_color',
935         $xl::s{l_col_log_in},
936         $xl::s{h_col_log_in},
937         'color',
938         '0.0.0',
939         '#000000',
940         undef,
941     ],
942     log_msg => [
943         'log_msg_color',
944         $xl::s{l_col_log_msg},
945         $xl::s{h_col_log_msg},
946         'color',
947         '0.0.0',
948         '#00af00',
949         undef,
950     ],
951     log_out => [
952         'log_out_color',
953         $xl::s{l_col_log_out},
954         $xl::s{h_col_log_out},
955         'color',
956         '0.0.0',
957         '#0000ef',
958         undef,
959     ],
960     log_warn => [
961         'log_warn_color',
962         $xl::s{l_col_log_warn},
963         $xl::s{h_col_log_warn},
964         'color',
965         '0.0.0',
966         '#af0000',
967         undef,
968     ],
969 );
970
971 sub new_colours_page() {
972     my $cf = Gtk2::VBox->new (FALSE, 5);
973     $cf->set_border_width (PAGE_SPC);
974
975     my $msgview_frame = Gtk2::Frame->new ($xl::s{msgview_frame}); 
976     my $cb_emphasis = &new_color_button_for (\%pr::col, 'emphasis');
977     my $vb1 = Gtk2::VBox->new (FALSE, 5);
978     $vb1->set_border_width (PAGE_SPC);
979     $vb1->pack_start ($cb_emphasis, FALSE, FALSE, 0);
980     $msgview_frame->add ($vb1);
981     
982     my $log_frame = Gtk2::Frame->new ($xl::s{log_frame}); 
983     my $cb_log_err = &new_color_button_for (\%pr::col, 'log_err');
984     my $cb_log_in = &new_color_button_for (\%pr::col, 'log_in');
985     my $cb_log_msg = &new_color_button_for (\%pr::col, 'log_msg');
986     my $cb_log_out = &new_color_button_for (\%pr::col, 'log_out');
987     my $cb_log_warn = &new_color_button_for (\%pr::col, 'log_warn');
988     my $vb2 = Gtk2::VBox->new (FALSE, 5);
989     $vb2->set_border_width (PAGE_SPC);
990     $vb2->pack_start ($cb_log_err, FALSE, FALSE, 0);
991     $vb2->pack_start ($cb_log_in, FALSE, FALSE, 0);
992     $vb2->pack_start ($cb_log_msg, FALSE, FALSE, 0);
993     $vb2->pack_start ($cb_log_out, FALSE, FALSE, 0);
994     $vb2->pack_start ($cb_log_warn, FALSE, FALSE, 0);
995     $log_frame->add ($vb2);
996
997     $cf->pack_start ($msgview_frame, FALSE, FALSE, 0);
998     $cf->pack_start ($log_frame, FALSE, FALSE, 0);
999
1000     return $cf;
1001 }
1002
1003 # version info
1004 sub print_version() {
1005     print $xl::s{about_title} . "\n";
1006     print $xl::s{about_version} . " $VERSION\n";
1007     print "Perl-GLib " . $Glib::VERSION;
1008     # version info stuff appeared in 1.040
1009     if ($Glib::VERSION >= 1.040) {
1010         print _(", built for ") . join(".", Glib->GET_VERSION_INFO) 
1011               . _(", running with ") . join(".", &Glib::major_version, 
1012                   &Glib::minor_version, &Glib::micro_version);
1013     }
1014     print "\n";
1015     print "Perl-GTK2 " . $Gtk2::VERSION;
1016     if ($Gtk2::VERSION >= 1.040) {
1017         print _(", built for ") . join(".", Gtk2->GET_VERSION_INFO)
1018               . _(", running with ") . join(".", &Gtk2::major_version, 
1019                   &Gtk2::minor_version, &Gtk2::micro_version);
1020     }
1021     print "\n";
1022     my $clawsver = ($CLAWSV eq "") ? 
1023                 _("was not found!") : 
1024                 (_("returned version ") . $CLAWSV);
1025     print "Claws Mail " . $clawsver . "\n";
1026 }
1027
1028 # the command line help
1029 sub print_help() {
1030     my $line = '-' x length ($xl::s{about_title}) . "\n";
1031     print $line;
1032     print $xl::s{about_title} . "\n";
1033     print $line;
1034     print _("Syntax:\n");
1035     print _("    clawsker [options]\n");
1036     print _("Options:\n");
1037     print _("    --help                         Prints this help screen.\n");
1038     print _("    --version                      Prints version infos.\n");
1039     print _("    --verbose                      More messages on standard output.\n");
1040     print _("    --alternate-config-dir <dir>   Uses <dir> as Claws Mail config dir.\n");
1041     print _("    --clawsrc <file>               Uses <file> as full resource name.\n");
1042 }
1043
1044 # handle errors which don't allow to run
1045 sub command_line_fatal() {
1046     my $reason = shift;
1047     my $emsg = $xl::s{e_error} . $reason;
1048     error_dialog ($emsg);
1049     log_message ("$emsg", 'die');
1050 }
1051
1052 # parse the command line
1053 sub parse_command_line() {
1054     $CLAWSV = &get_claws_version;
1055     my $arg = 0;
1056     while (defined($ARGV[$arg])) {
1057         for ($ARGV[$arg]) {
1058             /--help/ && do { 
1059                 &print_help; 
1060                 return FALSE;
1061             };
1062             /--version/ && do { 
1063                 &print_version; 
1064                 return FALSE;
1065             };
1066             /--verbose/ && do {
1067                 $VERBOSE = TRUE;
1068                 last;
1069             };
1070             /--use-claws-version/ && do {
1071                 ++$arg;
1072                 &command_line_fatal ("required version")
1073                     unless defined($ARGV[$arg]);
1074                 &command_line_fatal ("required a dotted numeric value")
1075                     unless ($ARGV[$arg] =~ /[\d\.]+/);
1076                 $CLAWSV = $ARGV[$arg];
1077             };
1078             /--alternate-config-dir/ && do {
1079                 ++$arg;
1080                 &command_line_fatal ($xl::s{e_requireddir})
1081                     unless defined($ARGV[$arg]);
1082                 &command_line_fatal ($xl::s{e_notadir})
1083                     unless -d $ARGV[$arg];
1084                 $CONFIGDIR = $ARGV[$arg];
1085                 $CONFIGDIR .= "/" 
1086                     unless ($CONFIGDIR =~ /.*\/$/);
1087                 $ALTCONFIGDIR = TRUE;
1088                 last;
1089             };
1090             /--clawsrc/ && do {
1091                 ++$arg;
1092                 &command_line_fatal($xl::s{e_requiredfile}) 
1093                     unless defined($ARGV[$arg]);
1094                 &command_line_fatal($xl::s{e_notafile}) 
1095                     unless -f $ARGV[$arg];
1096                 &set_rc_filename ($ARGV[$arg]);
1097                 last;
1098             };
1099             /.*/ && &command_line_fatal (
1100                         _("unknown option '{opt}'.\n", opt => $ARGV[$arg]));
1101         }
1102         ++$arg;
1103     }
1104     # eveything continues...
1105     return TRUE;
1106 }
1107
1108 # update the hidden preferences status from loaded values
1109 sub init_hidden_preferences() {
1110     foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth) {
1111         foreach my $key (keys %$hash) { 
1112             $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]};
1113         }
1114     }
1115     return TRUE;
1116 }
1117
1118 # load current status from disc
1119 sub load_preferences() {
1120     my $rc = &get_rc_filename;
1121     &log_message ("Loading preferences from $rc\n");
1122     return FALSE unless &check_rc_file ($rc);
1123     return FALSE unless &check_claws_not_running;
1124     open (RCF, "<$rc");
1125     while (<RCF>) {
1126         chomp;
1127         if (/^([8a-z_]+)=(.*)$/) {
1128             $PREFS{$1} = "$2";
1129         }
1130     }
1131     close (RCF);
1132     return TRUE;
1133 }
1134
1135 # save current preferences to disc
1136 sub save_preferences() {
1137     my $rc = &get_rc_filename;
1138     &log_message ("Saving preferences to $rc\n");
1139     return FALSE unless &check_rc_file ($rc);
1140     return FALSE unless &check_claws_not_running;
1141     my $rcbak = "$rc.backup";
1142     rename ($rc, $rcbak);
1143     open (RCF, ">$rc");
1144     open (RCB, "<$rcbak");
1145     while (<RCB>) {
1146         chomp;
1147         if (/^([8a-z_]+)=(.*)$/) {
1148             if (defined($HPVALUE{$1})) {
1149                 print RCF $1 . "=" . $HPVALUE{$1} . "\n";
1150             }
1151             else {
1152                 print RCF $_ . "\n";
1153             }
1154         }
1155         else {
1156             print RCF $_ . "\n";
1157         }
1158     }
1159     close (RCB);
1160     close (RCF);
1161     return TRUE;
1162 }
1163
1164 # create notebook
1165 sub new_notebook() {
1166     my $nb = Gtk2::Notebook->new;
1167     # 
1168     $nb->append_page (&new_behaviour_page, $xl::s{tab_behaviour});
1169     $nb->append_page (&new_colours_page, $xl::s{tab_colours});
1170     $nb->append_page (&new_gui_page, $xl::s{tab_gui});
1171     $nb->append_page (&new_other_page, $xl::s{tab_other});
1172
1173     return $nb;
1174 }
1175
1176 # create an about dialog
1177 sub new_about_dialog() {
1178     my ($parent) = @_;
1179     my $title = $xl::s{about_title};
1180     my $lic = $xl::s{about_license};
1181     my $vers = $xl::s{about_version} . " $VERSION";
1182     my $license = 
1183 "This program is free software: you can redistribute it and/or modify
1184 it under the terms of the GNU General Public License as published by
1185 the Free Software Foundation, either version 3 of the License, or
1186 (at your option) any later version.
1187
1188 This program is distributed in the hope that it will be useful,
1189 but WITHOUT ANY WARRANTY; without even the implied warranty of
1190 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1191 GNU General Public License for more details.
1192
1193 You should have received a copy of the GNU General Public License
1194 along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.";
1195     my $year = "2007-2009";
1196     my $holder = "Ricardo Mones &lt;ricardo\@mones.org&gt;";
1197
1198     my $dialog = Gtk2::MessageDialog->new_with_markup ($parent, 
1199                     [qw/modal destroy-with-parent/], 
1200                     'info', 'close', 
1201                     "<span size=\"x-large\" weight=\"bold\">$title</span>\n"
1202                     . "<span size=\"large\">$vers</span>\n"
1203                     . "<span>Copyright $year by $holder</span>\n\n"
1204                     . "<span size=\"large\">$lic</span>\n\n"
1205                     . "<span size=\"small\">$license</span>");
1206     $dialog->set_title ($xl::s{about});
1207     #
1208     return $dialog;
1209 }
1210
1211 # create buttons box
1212 sub new_button_box() {
1213     my ($parent, $adlg) = @_;
1214     my $b_about = Gtk2::Button->new_from_stock ('gtk-about');
1215     my $b_exit = Gtk2::Button->new_from_stock ('gtk-quit');
1216     my $b_apply = Gtk2::Button->new_from_stock ('gtk-apply');
1217     # disable button until is really implemented
1218     # my $b_undo = Gtk2::Button->new_from_stock ('gtk-undo');
1219     my $hbox = Gtk2::HBox->new (FALSE, 5);
1220     # signal handlers 
1221     $b_exit->signal_connect (clicked => sub { Gtk2->main_quit });
1222     $b_apply->signal_connect (clicked => sub { &save_preferences ($parent) });
1223     # $b_undo->signal_connect (clicked => sub { &undo_current_changes });
1224     $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
1225     # package them
1226     $hbox->pack_end ($b_apply, FALSE, FALSE, 0);
1227     $hbox->pack_end ($b_exit, FALSE, FALSE, 0);
1228     # $hbox->pack_end ($b_undo, FALSE, FALSE, 0);
1229     $hbox->pack_start ($b_about, FALSE, FALSE, 0);
1230     #
1231     return $hbox;
1232 }
1233
1234 # initialise
1235 $main_window = Gtk2::Window->new ('toplevel');
1236 exit unless &parse_command_line;
1237 exit unless &load_preferences;
1238 exit unless &init_hidden_preferences;
1239 # create main GUI
1240 my $box = Gtk2::VBox->new (FALSE, 5);
1241 $box->set_border_width(3);
1242 my $about = &new_about_dialog;
1243 $box->pack_start (&new_notebook, FALSE, FALSE, 0);
1244 $box->pack_end (&new_button_box ($main_window, $about), FALSE, FALSE, 0);
1245 $main_window->signal_connect (delete_event => sub { Gtk2->main_quit });
1246 $main_window->set_title ($xl::s{win_title});
1247 $main_window->add ($box);
1248 $main_window->show_all;
1249 Gtk2->main;
1250