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