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