enable/disable prefs according version
[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_or_equal() {
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 sub set_widget_sens() {
417     my ($wdgt, $minver) = @_;
418     $wdgt->set_sensitive (&version_greater_or_equal ($CLAWSV, $minver));
419 }
420
421 # graphic element creation 
422
423 sub new_check_button_for {
424     my ($hash, $key) = @_;
425     my $name = $$hash{$key}[NAME];
426     my $label = $$hash{$key}[LABEL];
427     #
428     my $hbox = Gtk2::HBox->new (FALSE, 5);
429     my $cb = Gtk2::CheckButton->new ($label);
430     $$hash{$key}[GUI] = $cb;
431     if (defined ($HPVALUE{$name})) {
432         $cb->set_active ($HPVALUE{$name} eq '1');
433     }
434     $cb->signal_connect (clicked => sub {
435             my ($w, $e) = @_;
436             &handle_bool_value($w, $e, \$HPVALUE{$name});
437         });
438     &set_widget_hint ($cb, $$hash{$key}[DESC]);
439     &set_widget_sens ($cb, $$hash{$key}[CMVER]);
440     $hbox->pack_start ($cb, FALSE, FALSE, HBOX_SPC);
441     #
442     return $hbox;
443 }
444
445 sub new_text_box_for_int {
446     my ($hash, $key) = @_;
447     my $name = $$hash{$key}[NAME];
448     my $label = $$hash{$key}[LABEL];
449     my @type = split (/,/, $$hash{$key}[TYPE]);
450     push (@type, 0), push (@type, 10000) unless ($#type > 0); 
451     #
452     my $hbox = Gtk2::HBox->new (FALSE, 5);
453     my $glabel = Gtk2::Label->new ($label);
454     my $pagei = int (($type[2] - $type[1]) / 10);
455     my $adjust = Gtk2::Adjustment->new (
456             $HPVALUE{$name}, $type[1], $type[2], 1, $pagei, 10
457         );
458     my $gentry = Gtk2::SpinButton->new ($adjust, 1, 0);
459     $gentry->set_numeric (TRUE);
460     $$hash{$key}[GUI] = $gentry;
461     $gentry->signal_connect('value-changed' => sub {
462             my ($w, $e) = @_;
463             &handle_int_value($w, $e, \$HPVALUE{$name});
464         });
465     &set_widget_hint ($gentry, $$hash{$key}[DESC]);
466     &set_widget_sens ($gentry, $$hash{$key}[CMVER]);
467     $glabel->set_sensitive ($gentry->sensitive);
468     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
469     $hbox->pack_start ($gentry, FALSE, FALSE, HBOX_SPC);
470     #
471     return $hbox;
472 }
473
474 sub new_color_button_for {
475     my ($hash, $key) = @_;
476     my $name = $$hash{$key}[NAME];
477     my $label = $$hash{$key}[LABEL];
478     #
479     my $col = &gdk_color_from_str ($HPVALUE{$name});
480     my $hbox = Gtk2::HBox->new (FALSE, 5);
481     my $glabel = Gtk2::Label->new ($label);
482     my $button = Gtk2::ColorButton->new_with_color ($col);
483     $$hash{$key}[GUI] = $button;
484     $button->set_title ($label);
485     $button->set_relief ('none');
486     $button->signal_connect ('color-set' => sub {
487             my ($w, $e) = @_;
488             &handle_color_value($w, $e, \$HPVALUE{$name}); 
489         });
490     &set_widget_hint ($button, $$hash{$key}[DESC]);
491     &set_widget_sens ($button, $$hash{$key}[CMVER]);
492     $glabel->set_sensitive ($button->sensitive);
493     $hbox->pack_start ($button, FALSE, FALSE, HBOX_SPC);
494     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
495     #
496     return $hbox;
497 }
498
499 sub new_selection_box_for {
500     my ($hash, $key) = @_;
501     my $name = $$hash{$key}[NAME];
502     my $label = $$hash{$key}[LABEL];
503     #
504     my $hbox = Gtk2::HBox->new (FALSE, 5);
505     my $glabel = Gtk2::Label->new ($label);
506     my $combo = Gtk2::ComboBox->new_text;
507     $$hash{$key}[GUI] = $combo;
508     my @options = split (';', $$hash{$key}[TYPE]);
509     foreach my $opt (@options) {
510         my ($index, $textkey) = split ('=', $opt);
511         $combo->insert_text ($index, $xl::s{$textkey});
512     }
513     $combo->signal_connect ('changed' => sub {
514             my ($w, $e) = @_;
515             &handle_selection_value($w, $e, \$HPVALUE{$name});
516         });
517     $combo->set_active ($HPVALUE{$name});
518     &set_widget_hint ($combo, $$hash{$key}[DESC]);
519     &set_widget_sens ($combo, $$hash{$key}[CMVER]);
520     $glabel->set_sensitive ($combo->sensitive);
521     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
522     $hbox->pack_start ($combo, FALSE, FALSE, HBOX_SPC);
523     #
524     return $hbox;
525 }
526
527 # preference maps and corresponding page creation subs
528
529 %pr::oth = ( # other preferences
530     use_dlg => [ 
531         'addressbook_use_editaddress_dialog',
532         $xl::s{l_oth_use_dlg},
533         $xl::s{h_oth_use_dlg},
534         'bool',
535         '2.7.0',
536         '0',
537         undef,
538     ],
539     max_use => [
540         'cache_max_mem_usage',
541         $xl::s{l_oth_max_use},
542         $xl::s{h_oth_max_use},
543         'int,0,262144', # 0 Kb - 256 Mb
544         '0.0.0',
545         '4096',
546         undef,
547     ],
548     min_time => [
549         'cache_min_keep_time',
550         $xl::s{l_oth_min_time},
551         $xl::s{h_oth_min_time},
552         'int,0,120', # 0 minutes - 2 hours
553         '0.0.0',
554         '15',
555         undef,
556     ],
557     use_netm => [
558         'use_networkmanager',
559         $xl::s{l_oth_use_netm},
560         $xl::s{h_oth_use_netm},
561         'bool',
562         '3.3.1',
563         '1',
564         undef,
565     ],
566 );
567
568 sub new_other_page() {
569     my $of = Gtk2::VBox->new (FALSE, 5);
570     $of->set_border_width (PAGE_SPC);
571
572     my $ab_frame = Gtk2::Frame->new ($xl::s{ab_frame}); 
573     my $cb_use_dlg = &new_check_button_for(\%pr::oth, 'use_dlg');
574     my $vb1 = Gtk2::VBox->new (FALSE, 5);
575     $vb1->set_border_width (PAGE_SPC); 
576     $vb1->pack_start ($cb_use_dlg, FALSE, FALSE, 0);
577     $ab_frame->add ($vb1);
578
579     my $mem_frame = Gtk2::Frame->new ($xl::s{mem_frame}); 
580     my $tb_max_use = &new_text_box_for_int(\%pr::oth, 'max_use');
581     my $tb_min_time = &new_text_box_for_int(\%pr::oth, 'min_time');
582     my $vb2 = Gtk2::VBox->new (FALSE, 5);
583     $vb2->set_border_width (PAGE_SPC); 
584     $vb2->pack_start ($tb_max_use, FALSE, FALSE, 0);
585     $vb2->pack_start ($tb_min_time, FALSE, FALSE, 0);
586     $mem_frame->add ($vb2);
587
588     my $netm_frame = Gtk2::Frame->new ($xl::s{netm_frame});
589     my $cb_use_netm = &new_check_button_for(\%pr::oth, 'use_netm');
590     my $vb3 = Gtk2::VBox->new (FALSE, 5);
591     $vb3->set_border_width (PAGE_SPC);
592     $vb3->pack_start ($cb_use_netm, FALSE, FALSE, 0);
593     $netm_frame->add ($vb3);
594
595     $of->pack_start ($ab_frame, FALSE, FALSE, FRAME_SPC);
596     $of->pack_start ($mem_frame, FALSE, FALSE, FRAME_SPC);
597     $of->pack_start ($netm_frame, FALSE, FALSE, FRAME_SPC);
598
599     return $of;
600 }
601
602 %pr::gui = ( # gui bells and whistles
603     b_unread => [ 
604         'bold_unread',
605         $xl::s{l_gui_b_unread},
606         $xl::s{h_gui_b_unread},
607         'bool',
608         '0.0.0',
609         '1',
610         undef,
611     ],
612     no_markup => [
613         'compose_no_markup',
614         $xl::s{l_gui_no_markup},
615         $xl::s{h_gui_no_markup},
616         'bool',
617         '0.0.0',
618         '0',
619         undef,
620     ],
621     dot_lines => [
622         'enable_dotted_lines',
623         $xl::s{l_gui_dot_lines},
624         $xl::s{h_gui_dot_lines},
625         'bool',
626         '0.0.0',
627         '0',
628         undef,
629     ],
630     h_scroll => [
631         'enable_hscrollbar',
632         $xl::s{l_gui_h_scroll},
633         $xl::s{h_gui_h_scroll},
634         'bool',
635         '0.0.0',
636         '1',
637         undef,
638     ],
639     swp_from => [
640         'enable_swap_from',
641         $xl::s{l_gui_swp_from},
642         $xl::s{h_gui_swp_from},
643         'bool',
644         '0.0.0',
645         '0',
646         undef,
647     ],
648     v_scroll => [
649         'folderview_vscrollbar_policy',
650         $xl::s{l_gui_v_scroll},
651         $xl::s{h_gui_v_scroll},
652         '0=l_gui_v_scroll_show;1=l_gui_v_scroll_auto;2=l_gui_v_scroll_hide',
653         '0.0.0',
654         '0',
655         undef,
656     ],
657     strip_off => [
658         'stripes_color_offset',
659         $xl::s{l_gui_strip_off},
660         $xl::s{h_gui_strip_off},
661         'int,0,10000', # no idea what this number means
662         '0.0.0',
663         '4000',
664         undef,
665     ],
666     cursor_v => [
667         'textview_cursor_visible',
668         $xl::s{l_gui_cursor_v},
669         $xl::s{h_gui_cursor_v},
670         'bool',
671         '0.0.0',
672         '0',
673         undef,
674     ],
675     toolbar_d => [
676         'toolbar_detachable',
677         $xl::s{l_gui_toolbar_d},
678         $xl::s{h_gui_toolbar_d},
679         'bool',
680         '0.0.0',
681         '0',
682         undef,
683     ],
684     strip_all => [
685         'use_stripes_everywhere',
686         $xl::s{l_gui_strip_all},
687         $xl::s{h_gui_strip_all},
688         'bool',
689         '0.0.0',
690         '1',
691         undef,
692     ],
693     strip_sum => [
694         'use_stripes_in_summaries',
695         $xl::s{l_gui_strip_sum},
696         $xl::s{h_gui_strip_sum},
697         'bool',
698         '0.0.0',
699         '1',
700         undef,
701     ],
702     two_linev => [
703         'two_line_vertical',
704         $xl::s{l_gui_two_line_v},
705         $xl::s{h_gui_two_line_v},
706         'bool',
707         '3.4.0.7',
708         '0',
709         undef,
710     ],
711 );
712
713 sub new_gui_page() {
714     my $gf = Gtk2::VBox->new (FALSE, 5);
715     $gf->set_border_width (PAGE_SPC);
716
717     my $stripes_frame = Gtk2::Frame->new ($xl::s{stripes_frame});
718     my $cb_strip_all = &new_check_button_for (\%pr::gui, 'strip_all');
719     my $cb_strip_sum = &new_check_button_for (\%pr::gui, 'strip_sum');
720     my $tb_strip_off = &new_text_box_for_int (\%pr::gui, 'strip_off');
721     my $vb1 = Gtk2::VBox->new (FALSE, 5);
722     $vb1->set_border_width (PAGE_SPC);
723     $vb1->pack_start ($cb_strip_all, FALSE, FALSE, 0);
724     $vb1->pack_start ($cb_strip_sum, FALSE, FALSE, 0);
725     $vb1->pack_start ($tb_strip_off, FALSE, FALSE, 0);
726     $stripes_frame->add ($vb1);
727
728     my $mlist_frame = Gtk2::Frame->new ($xl::s{mlist_frame});
729     my $cb_b_unread = &new_check_button_for (\%pr::gui, 'b_unread');
730     my $cb_swp_from = &new_check_button_for (\%pr::gui, 'swp_from');
731     my $vb3 = Gtk2::VBox->new (FALSE, 5);
732     $vb3->set_border_width (PAGE_SPC);
733     $vb3->pack_start ($cb_b_unread, FALSE, FALSE, 0);
734     $vb3->pack_start ($cb_swp_from, FALSE, FALSE, 0);
735     $mlist_frame->add ($vb3);
736
737     my $sbar_frame = Gtk2::Frame->new ($xl::s{sbar_frame});
738     my $cb_h_scroll = &new_check_button_for (\%pr::gui, 'h_scroll');
739     my $sb_v_scroll = &new_selection_box_for (\%pr::gui, 'v_scroll');
740     my $vb2 = Gtk2::VBox->new (FALSE, 5);
741     $vb2->set_border_width (PAGE_SPC);
742     $vb2->pack_start ($cb_h_scroll, FALSE, FALSE, 0);
743     $vb2->pack_start ($sb_v_scroll, FALSE, FALSE, 0);
744     $sbar_frame->add ($vb2);
745
746     my $cb_no_markup = &new_check_button_for (\%pr::gui, 'no_markup'); 
747     my $cb_dot_lines = &new_check_button_for (\%pr::gui, 'dot_lines'); 
748     my $cb_cursor_v = &new_check_button_for (\%pr::gui, 'cursor_v');
749     my $cb_toolbar_d = &new_check_button_for (\%pr::gui, 'toolbar_d');
750     my $cb_two_linev = &new_check_button_for (\%pr::gui, 'two_linev');
751
752     $gf->pack_start ($stripes_frame, FALSE, FALSE, FRAME_SPC);
753     $gf->pack_start ($mlist_frame, FALSE, FALSE, FRAME_SPC);
754     $gf->pack_start ($cb_no_markup, FALSE, FALSE, 0);
755     $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
756     $gf->pack_start ($cb_cursor_v, FALSE, FALSE, 0);
757     $gf->pack_start ($cb_toolbar_d, FALSE, FALSE, 0);
758     $gf->pack_start ($cb_two_linev, FALSE, FALSE, 0);
759     $gf->pack_start ($sbar_frame, FALSE, FALSE, FRAME_SPC);
760
761     return $gf;
762 }
763
764 %pr::beh = ( # tweak some behaviour
765     hover_t => [
766         'hover_timeout',
767         $xl::s{l_beh_hover_t},
768         $xl::s{h_beh_hover_t},
769         'int,100,3000', # 0.1 seconds - 3 seconds
770         '0.0.0',
771         '500',
772         undef,
773     ],
774     dangerous => [
775         'live_dangerously',
776         $xl::s{l_beh_dangerous},
777         $xl::s{h_beh_dangerous},
778         'bool',
779         '0.0.0',
780         '0',
781         undef,
782     ],
783     flowed => [
784         'respect_flowed_format',
785         $xl::s{l_beh_flowed},
786         $xl::s{h_beh_flowed},
787         'bool',
788         '0.0.0',
789         '0',
790         undef,
791     ],
792     parts_rw => [
793         'save_parts_readwrite',
794         $xl::s{l_beh_parts_rw},
795         $xl::s{h_beh_parts_rw},
796         'bool',
797         '0.0.0',
798         '0',
799         undef,
800     ],
801     skip_ssl => [
802         'skip_ssl_cert_check',
803         $xl::s{l_beh_skip_ssl},
804         $xl::s{h_beh_skip_ssl},
805         'bool',
806         '0.0.0',
807         '0',
808         undef,
809     ],
810     up_step => [
811         'statusbar_update_step',
812         $xl::s{l_beh_up_step},
813         $xl::s{h_beh_up_step},
814         'int,1,200', # 1 item - 200 items
815         '0.0.0',
816         '10',
817         undef,
818     ],
819     thread_a => [
820         'thread_by_subject_max_age',
821         $xl::s{l_beh_thread_a},
822         $xl::s{h_beh_thread_a},
823         'int,1,30', # 1 day - 30 days
824         '0.0.0',
825         '10',
826         undef,
827     ],
828     unsafe_ssl => [
829         'unsafe_ssl_certs',
830         $xl::s{l_beh_unsafe_ssl},
831         $xl::s{h_beh_unsafe_ssl},
832         'bool',
833         '0.0.0',
834         '0',
835         undef,
836     ],
837     use_utf8 => [
838         'utf8_instead_of_locale_for_broken_mail',
839         $xl::s{l_beh_use_utf8},
840         $xl::s{h_beh_use_utf8},
841         'bool',
842         '0.0.0',
843         '0',
844         undef,
845     ],
846     warn_dnd => [
847         'warn_dnd',
848         $xl::s{l_beh_warn_dnd},
849         $xl::s{h_beh_warn_dnd},
850         'bool',
851         '0.0.0',
852         '1',
853         undef,
854     ],
855     out_ascii => [
856         'outgoing_fallback_to_ascii',
857         $xl::s{l_beh_out_ascii},
858         $xl::s{h_beh_out_ascii},
859         'bool',
860         '3.4.0.37',
861         '1',
862         undef,
863     ],
864     pp_unsel => [
865         'primary_paste_unselects',
866         $xl::s{l_beh_pp_unsel},
867         $xl::s{h_beh_pp_unsel},
868         'bool',
869         '3.6.1.35',
870         '0',
871         undef,
872     ],
873 );
874
875 sub new_behaviour_page() {
876     my $bf = Gtk2::VBox->new (FALSE, 5);
877     $bf->set_border_width (PAGE_SPC);
878
879     my $dnd_frame = Gtk2::Frame->new ($xl::s{dnd_frame});
880     my $tb_hoover_t = &new_text_box_for_int (\%pr::beh, 'hover_t');
881     my $cb_warn_dnd = &new_check_button_for (\%pr::beh, 'warn_dnd');
882     my $vb1 = Gtk2::VBox->new (FALSE, 5);
883     $vb1->set_border_width (PAGE_SPC);
884     $vb1->pack_start ($cb_warn_dnd, FALSE, FALSE, 0);
885     $vb1->pack_start ($tb_hoover_t, FALSE, FALSE, 0);
886     $dnd_frame->add ($vb1);
887
888     my $ssl_frame = Gtk2::Frame->new ($xl::s{ssl_frame});
889     my $cb_skip_ssl = &new_check_button_for (\%pr::beh, 'skip_ssl');
890     my $cb_unsafe_ssl = &new_check_button_for (\%pr::beh, 'unsafe_ssl');
891     my $hb1 = Gtk2::HBox->new (FALSE, 5);
892     $hb1->set_border_width (PAGE_SPC);
893     $hb1->pack_start ($cb_skip_ssl, FALSE, FALSE, 0);
894     $hb1->pack_start ($cb_unsafe_ssl, FALSE, FALSE, 0);
895     $ssl_frame->add ($hb1);
896
897     my $tb_up_step = &new_text_box_for_int (\%pr::beh, 'up_step');
898     my $tb_thread_a = &new_text_box_for_int (\%pr::beh, 'thread_a');
899
900     my $msgs_frame = Gtk2::Frame->new ($xl::s{msgs_frame});
901     my $cb_flowed = &new_check_button_for (\%pr::beh, 'flowed');
902     my $cb_parts_rw = &new_check_button_for (\%pr::beh, 'parts_rw');
903     my $cb_use_utf8 = &new_check_button_for (\%pr::beh, 'use_utf8');
904     my $cb_dangerous = &new_check_button_for (\%pr::beh, 'dangerous');
905     my $cb_out_ascii = &new_check_button_for (\%pr::beh, 'out_ascii');
906     my $cb_pp_unsel = &new_check_button_for (\%pr::beh, 'pp_unsel');
907     my $vb2 = Gtk2::VBox->new (FALSE, 5);    
908     $vb2->set_border_width (PAGE_SPC);
909     $vb2->pack_start ($cb_flowed, FALSE, FALSE, 0);
910     $vb2->pack_start ($cb_parts_rw, FALSE, FALSE, 0);
911     $vb2->pack_start ($cb_use_utf8, FALSE, FALSE, 0);
912     $vb2->pack_start ($cb_dangerous, FALSE, FALSE, 0);
913     $vb2->pack_start ($cb_out_ascii, FALSE, FALSE, 0);
914     $vb2->pack_start ($cb_pp_unsel, FALSE, FALSE, 0);
915     $msgs_frame->add ($vb2);
916
917     $bf->pack_start ($dnd_frame, FALSE, FALSE, FRAME_SPC);
918     $bf->pack_start ($ssl_frame, FALSE, FALSE, FRAME_SPC);
919     $bf->pack_start ($tb_up_step, FALSE, FALSE, 0);
920     $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0);
921     $bf->pack_start ($msgs_frame, FALSE, FALSE, FRAME_SPC);
922
923     return $bf;
924 }
925
926 %pr::col = ( # a variety of colours
927     emphasis => [
928         'emphasis_color',
929         $xl::s{l_col_emphasis},
930         $xl::s{h_col_emphasis},
931         'color',
932         '0.0.0',
933         '#0000cf',
934         undef,
935     ],
936     log_err => [
937         'log_error_color',
938         $xl::s{l_col_log_err},
939         $xl::s{h_col_log_err},
940         'color',
941         '0.0.0',
942         '#af0000',
943         undef,
944     ],
945     log_in => [
946         'log_in_color',
947         $xl::s{l_col_log_in},
948         $xl::s{h_col_log_in},
949         'color',
950         '0.0.0',
951         '#000000',
952         undef,
953     ],
954     log_msg => [
955         'log_msg_color',
956         $xl::s{l_col_log_msg},
957         $xl::s{h_col_log_msg},
958         'color',
959         '0.0.0',
960         '#00af00',
961         undef,
962     ],
963     log_out => [
964         'log_out_color',
965         $xl::s{l_col_log_out},
966         $xl::s{h_col_log_out},
967         'color',
968         '0.0.0',
969         '#0000ef',
970         undef,
971     ],
972     log_warn => [
973         'log_warn_color',
974         $xl::s{l_col_log_warn},
975         $xl::s{h_col_log_warn},
976         'color',
977         '0.0.0',
978         '#af0000',
979         undef,
980     ],
981 );
982
983 sub new_colours_page() {
984     my $cf = Gtk2::VBox->new (FALSE, 5);
985     $cf->set_border_width (PAGE_SPC);
986
987     my $msgview_frame = Gtk2::Frame->new ($xl::s{msgview_frame}); 
988     my $cb_emphasis = &new_color_button_for (\%pr::col, 'emphasis');
989     my $vb1 = Gtk2::VBox->new (FALSE, 5);
990     $vb1->set_border_width (PAGE_SPC);
991     $vb1->pack_start ($cb_emphasis, FALSE, FALSE, 0);
992     $msgview_frame->add ($vb1);
993     
994     my $log_frame = Gtk2::Frame->new ($xl::s{log_frame}); 
995     my $cb_log_err = &new_color_button_for (\%pr::col, 'log_err');
996     my $cb_log_in = &new_color_button_for (\%pr::col, 'log_in');
997     my $cb_log_msg = &new_color_button_for (\%pr::col, 'log_msg');
998     my $cb_log_out = &new_color_button_for (\%pr::col, 'log_out');
999     my $cb_log_warn = &new_color_button_for (\%pr::col, 'log_warn');
1000     my $vb2 = Gtk2::VBox->new (FALSE, 5);
1001     $vb2->set_border_width (PAGE_SPC);
1002     $vb2->pack_start ($cb_log_err, FALSE, FALSE, 0);
1003     $vb2->pack_start ($cb_log_in, FALSE, FALSE, 0);
1004     $vb2->pack_start ($cb_log_msg, FALSE, FALSE, 0);
1005     $vb2->pack_start ($cb_log_out, FALSE, FALSE, 0);
1006     $vb2->pack_start ($cb_log_warn, FALSE, FALSE, 0);
1007     $log_frame->add ($vb2);
1008
1009     $cf->pack_start ($msgview_frame, FALSE, FALSE, 0);
1010     $cf->pack_start ($log_frame, FALSE, FALSE, 0);
1011
1012     return $cf;
1013 }
1014
1015 # version info
1016 sub print_version() {
1017     print $xl::s{about_title} . "\n";
1018     print $xl::s{about_version} . " $VERSION\n";
1019     print "Perl-GLib " . $Glib::VERSION;
1020     # version info stuff appeared in 1.040
1021     if ($Glib::VERSION >= 1.040) {
1022         print _(", built for ") . join(".", Glib->GET_VERSION_INFO) 
1023               . _(", running with ") . join(".", &Glib::major_version, 
1024                   &Glib::minor_version, &Glib::micro_version);
1025     }
1026     print "\n";
1027     print "Perl-GTK2 " . $Gtk2::VERSION;
1028     if ($Gtk2::VERSION >= 1.040) {
1029         print _(", built for ") . join(".", Gtk2->GET_VERSION_INFO)
1030               . _(", running with ") . join(".", &Gtk2::major_version, 
1031                   &Gtk2::minor_version, &Gtk2::micro_version);
1032     }
1033     print "\n";
1034     my $clawsver = ($CLAWSV eq "") ? 
1035                 _("was not found!") : 
1036                 (_("returned version ") . $CLAWSV);
1037     print "Claws Mail " . $clawsver . "\n";
1038 }
1039
1040 # the command line help
1041 sub print_help() {
1042     my $line = '-' x length ($xl::s{about_title}) . "\n";
1043     print $line;
1044     print $xl::s{about_title} . "\n";
1045     print $line;
1046     print _("Syntax:\n");
1047     print _("    clawsker [options]\n");
1048     print _("Options:\n");
1049     print _("    --help                         Prints this help screen.\n");
1050     print _("    --version                      Prints version infos.\n");
1051     print _("    --verbose                      More messages on standard output.\n");
1052     print _("    --alternate-config-dir <dir>   Uses <dir> as Claws Mail config dir.\n");
1053     print _("    --clawsrc <file>               Uses <file> as full resource name.\n");
1054 }
1055
1056 # handle errors which don't allow to run
1057 sub command_line_fatal() {
1058     my $reason = shift;
1059     my $emsg = $xl::s{e_error} . $reason;
1060     error_dialog ($emsg);
1061     log_message ("$emsg", 'die');
1062 }
1063
1064 # parse the command line
1065 sub parse_command_line() {
1066     $CLAWSV = &get_claws_version;
1067     my $arg = 0;
1068     while (defined($ARGV[$arg])) {
1069         for ($ARGV[$arg]) {
1070             /--help/ && do { 
1071                 &print_help; 
1072                 return FALSE;
1073             };
1074             /--version/ && do { 
1075                 &print_version; 
1076                 return FALSE;
1077             };
1078             /--verbose/ && do {
1079                 $VERBOSE = TRUE;
1080                 last;
1081             };
1082             /--use-claws-version/ && do {
1083                 ++$arg;
1084                 &command_line_fatal ("required version")
1085                     unless defined($ARGV[$arg]);
1086                 &command_line_fatal ("required a dotted numeric value")
1087                     unless ($ARGV[$arg] =~ /[\d\.]+/);
1088                 $CLAWSV = $ARGV[$arg];
1089                 last;
1090             };
1091             /--alternate-config-dir/ && do {
1092                 ++$arg;
1093                 &command_line_fatal ($xl::s{e_requireddir})
1094                     unless defined($ARGV[$arg]);
1095                 &command_line_fatal ($xl::s{e_notadir})
1096                     unless -d $ARGV[$arg];
1097                 $CONFIGDIR = $ARGV[$arg];
1098                 $CONFIGDIR .= "/" 
1099                     unless ($CONFIGDIR =~ /.*\/$/);
1100                 $ALTCONFIGDIR = TRUE;
1101                 last;
1102             };
1103             /--clawsrc/ && do {
1104                 ++$arg;
1105                 &command_line_fatal($xl::s{e_requiredfile}) 
1106                     unless defined($ARGV[$arg]);
1107                 &command_line_fatal($xl::s{e_notafile}) 
1108                     unless -f $ARGV[$arg];
1109                 &set_rc_filename ($ARGV[$arg]);
1110                 last;
1111             };
1112             /.*/ && &command_line_fatal (
1113                         _("unknown option '{opt}'.\n", opt => $ARGV[$arg]));
1114         }
1115         ++$arg;
1116     }
1117     # eveything continues...
1118     return TRUE;
1119 }
1120
1121 # update the hidden preferences status from loaded values
1122 sub init_hidden_preferences() {
1123     foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth) {
1124         foreach my $key (keys %$hash) { 
1125             $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]};
1126         }
1127     }
1128     return TRUE;
1129 }
1130
1131 # load current status from disc
1132 sub load_preferences() {
1133     my $rc = &get_rc_filename;
1134     &log_message ("Loading preferences from $rc\n");
1135     return FALSE unless &check_rc_file ($rc);
1136     return FALSE unless &check_claws_not_running;
1137     open (RCF, "<$rc");
1138     while (<RCF>) {
1139         chomp;
1140         if (/^([8a-z_]+)=(.*)$/) {
1141             $PREFS{$1} = "$2";
1142         }
1143     }
1144     close (RCF);
1145     return TRUE;
1146 }
1147
1148 # save current preferences to disc
1149 sub save_preferences() {
1150     my $rc = &get_rc_filename;
1151     &log_message ("Saving preferences to $rc\n");
1152     return FALSE unless &check_rc_file ($rc);
1153     return FALSE unless &check_claws_not_running;
1154     my $rcbak = "$rc.backup";
1155     rename ($rc, $rcbak);
1156     open (RCF, ">$rc");
1157     open (RCB, "<$rcbak");
1158     while (<RCB>) {
1159         chomp;
1160         if (/^([8a-z_]+)=(.*)$/) {
1161             if (defined($HPVALUE{$1})) {
1162                 print RCF $1 . "=" . $HPVALUE{$1} . "\n";
1163             }
1164             else {
1165                 print RCF $_ . "\n";
1166             }
1167         }
1168         else {
1169             print RCF $_ . "\n";
1170         }
1171     }
1172     close (RCB);
1173     close (RCF);
1174     return TRUE;
1175 }
1176
1177 # create notebook
1178 sub new_notebook() {
1179     my $nb = Gtk2::Notebook->new;
1180     # 
1181     $nb->append_page (&new_behaviour_page, $xl::s{tab_behaviour});
1182     $nb->append_page (&new_colours_page, $xl::s{tab_colours});
1183     $nb->append_page (&new_gui_page, $xl::s{tab_gui});
1184     $nb->append_page (&new_other_page, $xl::s{tab_other});
1185
1186     return $nb;
1187 }
1188
1189 # create an about dialog
1190 sub new_about_dialog() {
1191     my ($parent) = @_;
1192     my $title = $xl::s{about_title};
1193     my $lic = $xl::s{about_license};
1194     my $vers = $xl::s{about_version} . " $VERSION";
1195     my $license = 
1196 "This program is free software: you can redistribute it and/or modify
1197 it under the terms of the GNU General Public License as published by
1198 the Free Software Foundation, either version 3 of the License, or
1199 (at your option) any later version.
1200
1201 This program is distributed in the hope that it will be useful,
1202 but WITHOUT ANY WARRANTY; without even the implied warranty of
1203 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
1204 GNU General Public License for more details.
1205
1206 You should have received a copy of the GNU General Public License
1207 along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.";
1208     my $year = "2007-2009";
1209     my $holder = "Ricardo Mones &lt;ricardo\@mones.org&gt;";
1210
1211     my $dialog = Gtk2::MessageDialog->new_with_markup ($parent, 
1212                     [qw/modal destroy-with-parent/], 
1213                     'info', 'close', 
1214                     "<span size=\"x-large\" weight=\"bold\">$title</span>\n"
1215                     . "<span size=\"large\">$vers</span>\n"
1216                     . "<span>Copyright $year by $holder</span>\n\n"
1217                     . "<span size=\"large\">$lic</span>\n\n"
1218                     . "<span size=\"small\">$license</span>");
1219     $dialog->set_title ($xl::s{about});
1220     #
1221     return $dialog;
1222 }
1223
1224 # create buttons box
1225 sub new_button_box() {
1226     my ($parent, $adlg) = @_;
1227     my $b_about = Gtk2::Button->new_from_stock ('gtk-about');
1228     my $b_exit = Gtk2::Button->new_from_stock ('gtk-quit');
1229     my $b_apply = Gtk2::Button->new_from_stock ('gtk-apply');
1230     # disable button until is really implemented
1231     # my $b_undo = Gtk2::Button->new_from_stock ('gtk-undo');
1232     my $hbox = Gtk2::HBox->new (FALSE, 5);
1233     # signal handlers 
1234     $b_exit->signal_connect (clicked => sub { Gtk2->main_quit });
1235     $b_apply->signal_connect (clicked => sub { &save_preferences ($parent) });
1236     # $b_undo->signal_connect (clicked => sub { &undo_current_changes });
1237     $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
1238     # package them
1239     $hbox->pack_end ($b_apply, FALSE, FALSE, 0);
1240     $hbox->pack_end ($b_exit, FALSE, FALSE, 0);
1241     # $hbox->pack_end ($b_undo, FALSE, FALSE, 0);
1242     $hbox->pack_start ($b_about, FALSE, FALSE, 0);
1243     #
1244     return $hbox;
1245 }
1246
1247 # initialise
1248 $main_window = Gtk2::Window->new ('toplevel');
1249 exit unless &parse_command_line;
1250 exit unless &load_preferences;
1251 exit unless &init_hidden_preferences;
1252 # create main GUI
1253 my $box = Gtk2::VBox->new (FALSE, 5);
1254 $box->set_border_width(3);
1255 my $about = &new_about_dialog;
1256 $box->pack_start (&new_notebook, FALSE, FALSE, 0);
1257 $box->pack_end (&new_button_box ($main_window, $about), FALSE, FALSE, 0);
1258 $main_window->signal_connect (delete_event => sub { Gtk2->main_quit });
1259 $main_window->set_title ($xl::s{win_title});
1260 $main_window->add ($box);
1261 $main_window->show_all;
1262 Gtk2->main;
1263