dialog for running claws error
[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 --alternate-config-dir <dir>
35         Uses <dir> as Claws Mail configuration dir.
36
37 --clawsrc <file>
38         Uses <file> as Claws Mail resource configuration file. This sets
39         the full file name overriding any previous setting.
40
41 Multiple options are allowed, although only the last one has effect. Weird
42 option specifications may produce weird results (but otherwise correct).
43         
44 =head1 LIMITATIONS
45
46 A running Claws Mail cannot be detected if using the --clawsrc option because
47 the directory is not assumed to be a Claws Mail configuration dir. If that is 
48 the case use the --alternate-config-dir option instead.
49
50 =head1 AUTHOR
51
52 Ricardo Mones E<lt>ricardo@mones.orgE<gt>
53
54 =head1 LICENSE
55
56 Copyright (c) 2007 by Ricardo Mones
57
58 This program is free software: you can redistribute it and/or modify
59 it under the terms of the GNU General Public License as published by
60 the Free Software Foundation, either version 3 of the License, or
61 (at your option) any later version.
62
63 This program is distributed in the hope that it will be useful,
64 but WITHOUT ANY WARRANTY; without even the implied warranty of
65 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
66 GNU General Public License for more details.
67
68 You should have received a copy of the GNU General Public License
69 along with this program.  If not, see E<lt>http://www.gnu.org/licenses/E<gt>.
70
71 =cut
72
73 use strict;
74 use POSIX qw(setlocale);
75 use Locale::gettext;
76 use Glib qw(TRUE FALSE);
77 use Gtk2 -init;
78
79 my $NAME = 'clawsker';
80 my $PREFIX = '@PREFIX@';
81 my $LIBDIR = '@LIBDIR@';
82 my $VERSION = '@VERSION@';
83
84 my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG});
85 setlocale (LC_ALL, $locale);
86 bindtextdomain ($NAME, sprintf ('%s/share/locale', $PREFIX));
87 textdomain ($NAME);
88
89 sub _ {
90     my $str = shift;
91     my %par = @_;
92     my $xla = gettext ($str);
93     if (scalar(keys(%par)) > 0) {
94         foreach my $key (keys %par) {
95             $xla =~ s/\{$key\}/$par{$key}/g;
96         }
97     }
98     return $xla;
99 }
100
101 # default messages
102 %xl::s = (
103     win_title => _('Claws Mail Hidden Preferences'),
104     about => _('About...'),
105     about_title => _('Clawsker :: A Claws Mail Tweaker'),
106     about_license => _('License:'),
107     about_version => _('Version:'),
108
109     tab_colours => _('Colours'),
110     tab_behaviour => _('Behaviour'),
111     tab_gui => _('GUI'),
112     tab_other => _('Other'),
113
114     ab_frame => _('Addressbook'),
115     mem_frame => _('Memory'),
116     msgview_frame => _('Message view'),
117     log_frame => _('Log window'),
118     dnd_frame => _('Drag \'n\' drop'),
119     ssl_frame => _('Secure Sockets Layer'),
120     msgs_frame => _('Messages'),
121     stripes_frame => _('Coloured stripes'),
122     sbar_frame => _('Scroll bars'),
123     mlist_frame => _('Message list'),
124
125     l_oth_use_dlg => _('Use detached address book edit dialogue'),
126     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.'),
127     l_oth_max_use => _('Maximum memory for message cache (Kb)'),
128     h_oth_max_use => _('The maximum amount of memory to use to cache messages, in kB.'),
129     l_oth_min_time => _('Minimun time for cache elements (minutes)'),
130     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.'),
131     
132     l_gui_b_unread => _('Show unread messages with bold font'),
133     h_gui_b_unread => _('Show unread messages in the Message List using a bold font.'),
134     l_gui_no_markup => _('Don\'t use markup in compose window'),
135     h_gui_no_markup => _('Don\'t use bold and italic text in Compose dialogue\'s account selector.'),
136     l_gui_dot_lines => _('Use dotted lines in tree views'),
137     h_gui_dot_lines => _('Use the old dotted line look in the main window tree views (folder and message lists) instead of the modern lineless look.'),
138     l_gui_h_scroll => _('Enable horizontal scrollbar'),
139     h_gui_h_scroll => _('Enable the horizontal scrollbar in the Message List.'),
140     l_gui_swp_from => _('Dislay To header in From field in Sent folder'),
141     h_gui_swp_from => _('Display the sender\'s email address in the To column of the Sent folder instead of the recipient\'s.'),
142     l_gui_v_scroll => _('Folder list scrollbar behaviour'),
143     h_gui_v_scroll => _('Specify the policy of vertical scrollbar of Folder List: show always, automatic or hide always.'),
144     l_gui_v_scroll_show => _('Show always'),
145     l_gui_v_scroll_auto => _('Automatic'),
146     l_gui_v_scroll_hide => _('Hide always'),
147     l_gui_strip_off => _('Striped lines contrast'),
148     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.'),
149     l_gui_cursor_v => _('Show Cursor in message view'),
150     h_gui_cursor_v => _('Display the cursor in the message view.'),
151     l_gui_toolbar_d => _('Detachable toolbars'),
152     h_gui_toolbar_d => _('Show handles in the toolbars.'),
153     l_gui_strip_all => _('Use stripes in all tree view components'),
154     h_gui_strip_all => _('Enable alternately coloured lines in all tree view components.'),
155     l_gui_strip_sum => _('Use stripes in Folder List and Message List'),
156     h_gui_strip_sum => _('Enable alternately coloured lines in message list and folder list'),
157
158     l_beh_hover_t => _('Drag \'n\' drop hover timeout (ms)'),
159     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.'),
160     l_beh_dangerous => _('Don\'t confirm deletions (dangerous!)'),
161     h_beh_dangerous => _('Don\'t ask for confirmation before definitive deletion of emails.'),
162     l_beh_flowed => _('Respect format=flowed in messages'),
163     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.'),
164     l_beh_parts_rw => _('Allow writable temporary files'),
165     h_beh_parts_rw => _('Saves temporary files when opening attachment with write bit set.'),
166     l_beh_skip_ssl => _('Don\'t check SSL certificates'),
167     h_beh_skip_ssl => _('Disables the verification of SSL certificates.'),
168     l_beh_up_step => _('Progress bar update step (items)'),
169     h_beh_up_step => _('Update stepping in progress bars.'),
170     l_beh_thread_a => _('Maximum age when threading by subject (days)'),
171     h_beh_thread_a => _('Number of days to include a message in a thread when using "Thread using subject in addition to standard headers".'),
172     l_beh_unsafe_ssl => _('Allow unsafe SSL certificates'),
173     h_beh_unsafe_ssl => _('Allows Claws Mail to remember multiple SSL certificates for a given server/port.'),
174     l_beh_use_utf8 => _('Force UTF-8 for broken mails'),
175     h_beh_use_utf8 => _('Use UTF-8 encoding for broken mails instead of current locale.'),
176     l_beh_warn_dnd => _('Warn on drag \'n\' drop'),
177     h_beh_warn_dnd => _('Display a confirmation dialogue on drag \'n\' drop of folders.'),
178
179     l_col_emphasis => _('X-Mailer header'),
180     h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
181     l_col_log_err => _('Error messages'),
182     h_col_log_err => _('Colour for error messages in log window.'),
183     l_col_log_in => _('Server messages'),
184     h_col_log_in => _('Colour for input messages in log window.'),
185     l_col_log_msg => _('Standard messages'),
186     h_col_log_msg => _('Colour for messages in log window.'),
187     l_col_log_out => _('Client messages'),
188     h_col_log_out => _('Colour for output messages in log window.'),
189     l_col_log_warn => _('Warnings'),
190     h_col_log_warn => _('Colour for warning messages in log window.'),
191
192     e_error => _('Error: '),
193     e_noclawsrc => _('resource file for Claws Mail was not found.'),
194     e_running => _('seems Claws Mail is currently running, close it first.'),
195     e_requireddir => _('option requires a directory name.'),
196     e_requiredfile => _('option requires a file name.'),
197     e_notadir => _('specified name is not a directory or does not exist.'),
198     e_notafile => _('specified name is not a file or does not exist.'),
199 );
200
201 # all preferences read by load_preferences
202 my %PREFS = ();
203 # values of all preferences handled by clawsker
204 my %HPVALUE = ();
205 # default config dir and file name
206 my $ALTCONFIGDIR = FALSE;
207 my $CONFIGDIR = $ENV{HOME} . '/.claws-mail/';
208 my $CONFIGRC = 'clawsrc';
209
210 # index constants for preference arrays
211 use constant NAME  => 0; # the name on the rc file
212 use constant LABEL => 1; # the label on the GUI
213 use constant DESC  => 2; # the description for the hint/help
214 use constant TYPE  => 3; # data type: bool, int, float, string, color
215 use constant GUI   => 4; # GUI element
216
217 # constants for GUI spacing
218 use constant HBOX_SPC => 5;
219 use constant FRAME_SPC => 2;
220 use constant PAGE_SPC => 5;
221
222 # data handlers and auxiliar functions
223
224 sub handle_bool_value {
225     my ($widget, $event, $dataref) = @_;
226     $$dataref = ($widget->get_active ())? '1': '0';
227 }
228
229 sub handle_int_value {
230     my ($widget, $event, $dataref) = @_;
231     $_ = $widget->get_text ();
232     s/^\s+//;
233     s/\s+$//;
234     if (/^[0-9]+$/) {
235         $$dataref = $_;
236         $widget->set_text ($_);
237     }
238     else {
239         $widget->set_text ($$dataref);
240     }
241 }
242
243 sub handle_string_value {
244     my ($widget, $event, $dataref) = @_;
245     $$dataref = $widget->get_text ();
246 }
247
248 sub gdk_color_from_str {
249     my ($str) = @_;
250     my ($rr, $gg, $bb) = (0, 0 ,0);
251     $_ = uc ($str);
252     if (/\#([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])/) {
253         $rr = hex($1) * 256;
254         $gg = hex($2) * 256; 
255         $bb = hex($3) * 256;
256     }
257     my $color = Gtk2::Gdk::Color->new ($rr, $gg, $bb);
258     return $color;
259 }
260
261 sub str_from_gdk_color {
262     my ($color) = @_;
263     my $rr = $color->red / 256;
264     my $gg = $color->green / 256;
265     my $bb = $color->blue / 256;
266     my $str = sprintf ("#%.2x%.2x%.2x", $rr, $gg, $bb);
267     return $str;
268 }
269
270 sub handle_color_value {
271     my ($widget, $event, $dataref) = @_;
272     my $newcol = $widget->get_color;
273     $$dataref = &str_from_gdk_color ($newcol);
274 }
275
276 sub handle_selection_value {
277     my ($widget, $event, $dataref) = @_;
278     $$dataref = $widget->get_active;
279 }
280
281 sub get_rc_filename {
282     return $CONFIGDIR . $CONFIGRC;
283 }
284
285 sub set_rc_filename {
286     my ($fullname) = @_;
287     my @parts = split ('/', $fullname);
288     $CONFIGRC = $parts[$#parts];
289     $parts[$#parts] = '';
290     $CONFIGDIR = join ('/', @parts);
291 }
292
293 sub check_claws_not_running() {
294     my ($parent) = @_;
295     my $socket = (not $ALTCONFIGDIR)? "/tmp/": $CONFIGDIR;
296     $socket .= "claws-mail-$<";
297     -S $socket and do {
298         my $emsg = "$xl::s{e_error}$xl::s{e_running}";
299         die "$emsg\n" unless defined($parent);
300         my $errordlg = Gtk2::MessageDialog->new ($parent, 'modal', 'error', 'cancel', $emsg);
301         $errordlg->set_title (_('Error'));
302         $errordlg->run;
303         $errordlg->destroy;
304         return FALSE;
305      };
306
307      return TRUE;
308 }
309
310 # graphic element creation 
311
312 sub new_check_button_for {
313     my ($hash, $key) = @_;
314     my $name = $$hash{$key}[NAME];
315     my $label = $$hash{$key}[LABEL];
316     #
317     my $hbox = Gtk2::HBox->new (FALSE, 5);
318     my $cb = Gtk2::CheckButton->new ($label);
319     $$hash{$key}[GUI] = $cb;
320     $cb->set_active ($HPVALUE{$name} eq '1');
321     $cb->signal_connect (clicked => sub {
322             my ($w, $e) = @_;
323             &handle_bool_value($w, $e, \$HPVALUE{$name});
324         });
325     $hbox->pack_start ($cb, FALSE, FALSE, HBOX_SPC);
326     #
327     return $hbox;
328 }
329
330 sub new_text_box_for {
331     my ($hash, $key) = @_;
332     my $name = $$hash{$key}[NAME];
333     my $label = $$hash{$key}[LABEL];
334     #
335     my $hbox = Gtk2::HBox->new (FALSE, 5);
336     my $glabel = Gtk2::Label->new ($label);
337     my $gentry = Gtk2::Entry->new;
338     $gentry->set_width_chars (8);
339     $$hash{$key}[GUI] = $gentry;
340     $gentry->set_text($HPVALUE{$name});
341     $gentry->signal_connect(changed => sub {
342             my ($w, $e) = @_;
343             &handle_int_value($w, $e, \$HPVALUE{$name}); # FIXME int only
344         });
345     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
346     $hbox->pack_start ($gentry, FALSE, FALSE, HBOX_SPC);
347     #
348     return $hbox;
349 }
350
351 sub new_color_button_for {
352     my ($hash, $key) = @_;
353     my $name = $$hash{$key}[NAME];
354     my $label = $$hash{$key}[LABEL];
355     #
356     my $col = &gdk_color_from_str ($HPVALUE{$name});
357     my $hbox = Gtk2::HBox->new (FALSE, 5);
358     my $glabel = Gtk2::Label->new ($label);
359     my $button = Gtk2::ColorButton->new_with_color ($col);
360     $$hash{$key}[GUI] = $button;
361     $button->set_title ($label);
362     $button->set_relief ('none');
363     $button->signal_connect ('color-set' => sub {
364             my ($w, $e) = @_;
365             &handle_color_value($w, $e, \$HPVALUE{$name}); 
366         });
367     $hbox->pack_start ($button, FALSE, FALSE, HBOX_SPC);
368     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
369     #
370     return $hbox;
371 }
372
373 sub new_selection_box_for {
374     my ($hash, $key) = @_;
375     my $name = $$hash{$key}[NAME];
376     my $label = $$hash{$key}[LABEL];
377     #
378     my $hbox = Gtk2::HBox->new (FALSE, 5);
379     my $glabel = Gtk2::Label->new ($label);
380     my $combo = Gtk2::ComboBox->new_text;
381     $$hash{$key}[GUI] = $combo;
382     my @options = split (';', $$hash{$key}[TYPE]);
383     foreach my $opt (@options) {
384         my ($index, $textkey) = split ('=', $opt);
385         $combo->insert_text ($index, $xl::s{$textkey});
386     }
387     $combo->signal_connect ('changed' => sub {
388             my ($w, $e) = @_;
389             &handle_selection_value($w, $e, \$HPVALUE{$name});
390         });
391     $combo->set_active ($HPVALUE{$name});
392     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
393     $hbox->pack_start ($combo, FALSE, FALSE, HBOX_SPC);
394     #
395     return $hbox;
396 }
397
398 # preference maps and corresponding page creation subs
399
400 %pr::oth = ( # other preferences
401     use_dlg => [ 
402         'addressbook_use_editaddress_dialog',
403         $xl::s{l_oth_use_dlg},
404         $xl::s{h_oth_use_dlg},
405         'bool',
406         undef,
407     ],
408     max_use => [
409         'cache_max_mem_usage',
410         $xl::s{l_oth_max_use},
411         $xl::s{h_oth_max_use},
412         'int',
413         undef,
414     ],
415     min_time => [
416         'cache_min_keep_time',
417         $xl::s{l_oth_min_time},
418         $xl::s{h_oth_min_time},
419         'int',
420         undef,
421     ],
422 );
423
424 sub new_other_page() {
425     my $of = Gtk2::VBox->new (FALSE, 5);
426     $of->set_border_width (PAGE_SPC);
427
428     my $ab_frame = Gtk2::Frame->new ($xl::s{ab_frame}); 
429     my $cb_use_dlg = &new_check_button_for(\%pr::oth, 'use_dlg');
430     my $vb1 = Gtk2::VBox->new (FALSE, 5);
431     $vb1->set_border_width (PAGE_SPC); 
432     $vb1->pack_start ($cb_use_dlg, FALSE, FALSE, 0);
433     $ab_frame->add ($vb1);
434
435     my $mem_frame = Gtk2::Frame->new ($xl::s{mem_frame}); 
436     my $tb_max_use = &new_text_box_for(\%pr::oth, 'max_use');
437     my $tb_min_time = &new_text_box_for(\%pr::oth, 'min_time');
438     my $vb2 = Gtk2::VBox->new (FALSE, 5);
439     $vb2->set_border_width (PAGE_SPC); 
440     $vb2->pack_start ($tb_max_use, FALSE, FALSE, 0);
441     $vb2->pack_start ($tb_min_time, FALSE, FALSE, 0);
442     $mem_frame->add ($vb2);
443
444     $of->pack_start ($ab_frame, FALSE, FALSE, FRAME_SPC);
445     $of->pack_start ($mem_frame, FALSE, FALSE, FRAME_SPC);
446
447     return $of;
448 }
449
450 %pr::gui = ( # gui bells and whistles
451     b_unread => [ 
452         'bold_unread',
453         $xl::s{l_gui_b_unread},
454         $xl::s{h_gui_b_unread},
455         'bool',
456         undef,
457     ],
458     no_markup => [
459         'compose_no_markup',
460         $xl::s{l_gui_no_markup},
461         $xl::s{h_gui_no_markup},
462         'bool',
463         undef,
464     ],
465     dot_lines => [
466         'enable_dotted_lines',
467         $xl::s{l_gui_dot_lines},
468         $xl::s{h_gui_dot_lines},
469         'bool',
470         undef,
471     ],
472     h_scroll => [
473         'enable_hscrollbar',
474         $xl::s{l_gui_h_scroll},
475         $xl::s{h_gui_h_scroll},
476         'bool',
477         undef,
478     ],
479     swp_from => [
480         'enable_swap_from',
481         $xl::s{l_gui_swp_from},
482         $xl::s{h_gui_swp_from},
483         'bool',
484         undef,
485     ],
486     v_scroll => [
487         'folderview_vscrollbar_policy',
488         $xl::s{l_gui_v_scroll},
489         $xl::s{h_gui_v_scroll},
490         '0=l_gui_v_scroll_show;1=l_gui_v_scroll_auto;2=l_gui_v_scroll_hide',
491         undef,
492     ],
493     strip_off => [
494         'stripes_color_offset',
495         $xl::s{l_gui_strip_off},
496         $xl::s{h_gui_strip_off},
497         'int',
498         undef,
499     ],
500     cursor_v => [
501         'textview_cursor_visible',
502         $xl::s{l_gui_cursor_v},
503         $xl::s{h_gui_cursor_v},
504         'bool',
505         undef,
506     ],
507     toolbar_d => [
508         'toolbar_detachable',
509         $xl::s{l_gui_toolbar_d},
510         $xl::s{h_gui_toolbar_d},
511         'bool',
512         undef,
513     ],
514     strip_all => [
515         'use_stripes_everywhere',
516         $xl::s{l_gui_strip_all},
517         $xl::s{h_gui_strip_all},
518         'bool',
519         undef,
520     ],
521     strip_sum => [
522         'use_stripes_in_summaries',
523         $xl::s{l_gui_strip_sum},
524         $xl::s{h_gui_strip_sum},
525         'bool',
526         undef,
527     ],
528 );
529
530 sub new_gui_page() {
531     my $gf = Gtk2::VBox->new (FALSE, 5);
532     $gf->set_border_width (PAGE_SPC);
533
534     my $stripes_frame = Gtk2::Frame->new ($xl::s{stripes_frame});
535     my $cb_strip_all = &new_check_button_for (\%pr::gui, 'strip_all');
536     my $cb_strip_sum = &new_check_button_for (\%pr::gui, 'strip_sum');
537     my $tb_strip_off = &new_text_box_for (\%pr::gui, 'strip_off');
538     my $vb1 = Gtk2::VBox->new (FALSE, 5);
539     $vb1->set_border_width (PAGE_SPC);
540     $vb1->pack_start ($cb_strip_all, FALSE, FALSE, 0);
541     $vb1->pack_start ($cb_strip_sum, FALSE, FALSE, 0);
542     $vb1->pack_start ($tb_strip_off, FALSE, FALSE, 0);
543     $stripes_frame->add ($vb1);
544
545     my $mlist_frame = Gtk2::Frame->new ($xl::s{mlist_frame});
546     my $cb_b_unread = &new_check_button_for (\%pr::gui, 'b_unread');
547     my $cb_swp_from = &new_check_button_for (\%pr::gui, 'swp_from');
548     my $vb3 = Gtk2::VBox->new (FALSE, 5);
549     $vb3->set_border_width (PAGE_SPC);
550     $vb3->pack_start ($cb_b_unread, FALSE, FALSE, 0);
551     $vb3->pack_start ($cb_swp_from, FALSE, FALSE, 0);
552     $mlist_frame->add ($vb3);
553
554     my $sbar_frame = Gtk2::Frame->new ($xl::s{sbar_frame});
555     my $cb_h_scroll = &new_check_button_for (\%pr::gui, 'h_scroll');
556     my $sb_v_scroll = &new_selection_box_for (\%pr::gui, 'v_scroll');
557     my $vb2 = Gtk2::VBox->new (FALSE, 5);
558     $vb2->set_border_width (PAGE_SPC);
559     $vb2->pack_start ($cb_h_scroll, FALSE, FALSE, 0);
560     $vb2->pack_start ($sb_v_scroll, FALSE, FALSE, 0);
561     $sbar_frame->add ($vb2);
562
563     my $cb_no_markup = &new_check_button_for (\%pr::gui, 'no_markup'); 
564     my $cb_dot_lines = &new_check_button_for (\%pr::gui, 'dot_lines'); 
565     my $cb_cursor_v = &new_check_button_for (\%pr::gui, 'cursor_v');
566     my $cb_toolbar_d = &new_check_button_for (\%pr::gui, 'toolbar_d');
567
568     $gf->pack_start ($stripes_frame, FALSE, FALSE, FRAME_SPC);
569     $gf->pack_start ($mlist_frame, FALSE, FALSE, FRAME_SPC);
570     $gf->pack_start ($cb_no_markup, FALSE, FALSE, 0);
571     $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
572     $gf->pack_start ($cb_cursor_v, FALSE, FALSE, 0);
573     $gf->pack_start ($cb_toolbar_d, FALSE, FALSE, 0);
574     $gf->pack_start ($sbar_frame, FALSE, FALSE, FRAME_SPC);
575
576     return $gf;
577 }
578
579 %pr::beh = ( # tweak some behaviour
580     hover_t => [
581         'hover_timeout',
582         $xl::s{l_beh_hover_t},
583         $xl::s{h_beh_hover_t},
584         'int',
585         undef,
586     ],
587     dangerous => [
588         'live_dangerously',
589         $xl::s{l_beh_dangerous},
590         $xl::s{h_beh_dangerous},
591         'bool',
592         undef,
593     ],
594     flowed => [
595         'respect_flowed_format',
596         $xl::s{l_beh_flowed},
597         $xl::s{h_beh_flowed},
598         'bool',
599         undef,
600     ],
601     parts_rw => [
602         'save_parts_readwrite',
603         $xl::s{l_beh_parts_rw},
604         $xl::s{h_beh_parts_rw},
605         'bool',
606         undef,
607     ],
608     skip_ssl => [
609         'skip_ssl_cert_check',
610         $xl::s{l_beh_skip_ssl},
611         $xl::s{h_beh_skip_ssl},
612         'bool',
613         undef,
614     ],
615     up_step => [
616         'statusbar_update_step',
617         $xl::s{l_beh_up_step},
618         $xl::s{h_beh_up_step},
619         'int',
620         undef,
621     ],
622     thread_a => [
623         'thread_by_subject_max_age',
624         $xl::s{l_beh_thread_a},
625         $xl::s{h_beh_thread_a},
626         'int',
627         undef,
628     ],
629     unsafe_ssl => [
630         'unsafe_ssl_certs',
631         $xl::s{l_beh_unsafe_ssl},
632         $xl::s{h_beh_unsafe_ssl},
633         'bool',
634         undef,
635     ],
636     use_utf8 => [
637         'utf8_instead_of_locale_for_broken_mail',
638         $xl::s{l_beh_use_utf8},
639         $xl::s{h_beh_use_utf8},
640         'bool',
641         undef,
642     ],
643     warn_dnd => [
644         'warn_dnd',
645         $xl::s{l_beh_warn_dnd},
646         $xl::s{h_beh_warn_dnd},
647         'bool',
648         undef,
649     ],
650 );
651
652 sub new_behaviour_page() {
653     my $bf = Gtk2::VBox->new (FALSE, 5);
654     $bf->set_border_width (PAGE_SPC);
655
656     my $dnd_frame = Gtk2::Frame->new ($xl::s{dnd_frame});
657     my $tb_hoover_t = &new_text_box_for (\%pr::beh, 'hover_t');
658     my $cb_warn_dnd = &new_check_button_for (\%pr::beh, 'warn_dnd');
659     my $vb1 = Gtk2::VBox->new (FALSE, 5);
660     $vb1->set_border_width (PAGE_SPC);
661     $vb1->pack_start ($cb_warn_dnd, FALSE, FALSE, 0);
662     $vb1->pack_start ($tb_hoover_t, FALSE, FALSE, 0);
663     $dnd_frame->add ($vb1);
664
665     my $ssl_frame = Gtk2::Frame->new ($xl::s{ssl_frame});
666     my $cb_skip_ssl = &new_check_button_for (\%pr::beh, 'skip_ssl');
667     my $cb_unsafe_ssl = &new_check_button_for (\%pr::beh, 'unsafe_ssl');
668     my $hb1 = Gtk2::HBox->new (FALSE, 5);
669     $hb1->set_border_width (PAGE_SPC);
670     $hb1->pack_start ($cb_skip_ssl, FALSE, FALSE, 0);
671     $hb1->pack_start ($cb_unsafe_ssl, FALSE, FALSE, 0);
672     $ssl_frame->add ($hb1);
673
674     my $tb_up_step = &new_text_box_for (\%pr::beh, 'up_step');
675     my $tb_thread_a = &new_text_box_for (\%pr::beh, 'thread_a');
676
677     my $msgs_frame = Gtk2::Frame->new ($xl::s{msgs_frame});
678     my $cb_flowed = &new_check_button_for (\%pr::beh, 'flowed');
679     my $cb_parts_rw = &new_check_button_for (\%pr::beh, 'parts_rw');
680     my $cb_use_utf8 = &new_check_button_for (\%pr::beh, 'use_utf8');
681     my $cb_dangerous = &new_check_button_for (\%pr::beh, 'dangerous');
682     my $vb2 = Gtk2::VBox->new (FALSE, 5);    
683     $vb2->set_border_width (PAGE_SPC);
684     $vb2->pack_start ($cb_flowed, FALSE, FALSE, 0);
685     $vb2->pack_start ($cb_parts_rw, FALSE, FALSE, 0);
686     $vb2->pack_start ($cb_use_utf8, FALSE, FALSE, 0);
687     $vb2->pack_start ($cb_dangerous, FALSE, FALSE, 0);
688     $msgs_frame->add ($vb2);
689
690     $bf->pack_start ($dnd_frame, FALSE, FALSE, FRAME_SPC);
691     $bf->pack_start ($ssl_frame, FALSE, FALSE, FRAME_SPC);
692     $bf->pack_start ($tb_up_step, FALSE, FALSE, 0);
693     $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0);
694     $bf->pack_start ($msgs_frame, FALSE, FALSE, FRAME_SPC);
695
696     return $bf;
697 }
698
699 %pr::col = ( # a variety of colours
700     emphasis => [
701         'emphasis_color',
702         $xl::s{l_col_emphasis},
703         $xl::s{h_col_emphasis},
704         'color',
705         undef,
706     ],
707     log_err => [
708         'log_error_color',
709         $xl::s{l_col_log_err},
710         $xl::s{h_col_log_err},
711         'color',
712         undef,
713     ],
714     log_in => [
715         'log_in_color',
716         $xl::s{l_col_log_in},
717         $xl::s{h_col_log_in},
718         'color',
719         undef,
720     ],
721     log_msg => [
722         'log_msg_color',
723         $xl::s{l_col_log_msg},
724         $xl::s{h_col_log_msg},
725         'color',
726         undef,
727     ],
728     log_out => [
729         'log_out_color',
730         $xl::s{l_col_log_out},
731         $xl::s{h_col_log_out},
732         'color',
733         undef,
734     ],
735     log_warn => [
736         'log_warn_color',
737         $xl::s{l_col_log_warn},
738         $xl::s{h_col_log_warn},
739         'color',
740         undef,
741     ],
742 );
743
744 sub new_colours_page() {
745     my $cf = Gtk2::VBox->new (FALSE, 5);
746     $cf->set_border_width (PAGE_SPC);
747
748     my $msgview_frame = Gtk2::Frame->new ($xl::s{msgview_frame}); 
749     my $cb_emphasis = &new_color_button_for (\%pr::col, 'emphasis');
750     my $vb1 = Gtk2::VBox->new (FALSE, 5);
751     $vb1->set_border_width (PAGE_SPC);
752     $vb1->pack_start ($cb_emphasis, FALSE, FALSE, 0);
753     $msgview_frame->add ($vb1);
754     
755     my $log_frame = Gtk2::Frame->new ($xl::s{log_frame}); 
756     my $cb_log_err = &new_color_button_for (\%pr::col, 'log_err');
757     my $cb_log_in = &new_color_button_for (\%pr::col, 'log_in');
758     my $cb_log_msg = &new_color_button_for (\%pr::col, 'log_msg');
759     my $cb_log_out = &new_color_button_for (\%pr::col, 'log_out');
760     my $cb_log_warn = &new_color_button_for (\%pr::col, 'log_warn');
761     my $vb2 = Gtk2::VBox->new (FALSE, 5);
762     $vb2->set_border_width (PAGE_SPC);
763     $vb2->pack_start ($cb_log_err, FALSE, FALSE, 0);
764     $vb2->pack_start ($cb_log_in, FALSE, FALSE, 0);
765     $vb2->pack_start ($cb_log_msg, FALSE, FALSE, 0);
766     $vb2->pack_start ($cb_log_out, FALSE, FALSE, 0);
767     $vb2->pack_start ($cb_log_warn, FALSE, FALSE, 0);
768     $log_frame->add ($vb2);
769
770     $cf->pack_start ($msgview_frame, FALSE, FALSE, 0);
771     $cf->pack_start ($log_frame, FALSE, FALSE, 0);
772
773     return $cf;
774 }
775
776 # the command line help
777 sub print_help() {
778     my $line = '-' x length ($xl::s{about_title}) . "\n";
779     print $line;
780     print $xl::s{about_title} . "\n";
781     print $xl::s{about_version} . " $VERSION\n";
782     print $line;
783     print _("Syntax:\n");
784     print _("    clawsker [options]\n");
785     print _("Options:\n");
786     print _("    --help                         Prints this help screen.\n");
787     print _("    --alternate-config-dir <dir>   Uses <dir> as Claws Mail config dir.\n");
788     print _("    --clawsrc <file>               Uses <file> as full resource name.\n");
789 }
790
791 # parse the command line
792 sub parse_command_line() {
793     my $arg = 0;
794     while (defined($ARGV[$arg])) {
795         for ($ARGV[$arg]) {
796             /--help/ && do { 
797                 &print_help; 
798                 exit 0;
799             };
800             /--alternate-config-dir/ && do {
801                 ++$arg;
802                 die "$xl::s{e_error}$xl::s{e_requireddir}\n" 
803                     unless defined($ARGV[$arg]);
804                 die "$xl::s{e_error}$xl::s{e_notadir}\n" 
805                     unless -d $ARGV[$arg];
806                 $CONFIGDIR = $ARGV[$arg];
807                 $ALTCONFIGDIR = TRUE;
808                 last;
809             };
810             /--clawsrc/ && do {
811                 ++$arg;
812                 die "$xl::s{e_error}$xl::s{e_requiredfile}\n" 
813                     unless defined($ARGV[$arg]);
814                 die "$xl::s{e_error}$xl::s{e_notafile}\n" 
815                     unless -f $ARGV[$arg];
816                 &set_rc_filename ($ARGV[$arg]);
817                 last;
818             };
819             /.*/ && die $xl::s{e_error} 
820                         . _("unknown option '{opt}'.\n", opt => $ARGV[$arg]);
821         }
822         ++$arg;
823     }
824 }
825
826 # update the hidden preferences status from loaded values
827 sub init_hidden_preferences() {
828     foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth) {
829         foreach my $key (keys %$hash) { 
830             $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]};
831         }
832     }
833 }
834
835 # load current status from disc
836 sub load_preferences() {
837     my $rc = &get_rc_filename;
838     -f $rc or die "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
839     &check_claws_not_running ();
840     open (RCF, "<$rc");
841     while (<RCF>) {
842         chomp;
843         if (/^([8a-z_]+)=(.*)$/) {
844             $PREFS{$1} = "$2";
845         }
846     }
847     close (RCF);
848 }
849
850 # save current preferences to disc
851 sub save_preferences() {
852     my ($parent) = @_;
853     my $rc = &get_rc_filename;
854     -f $rc or die "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
855     &check_claws_not_running ($parent) or return;
856     my $rcbak = "$rc.backup";
857     rename ($rc, $rcbak);
858     open (RCF, ">$rc");
859     open (RCB, "<$rcbak");
860     while (<RCB>) {
861         chomp;
862         if (/^([8a-z_]+)=(.*)$/) {
863             if (defined($HPVALUE{$1})) {
864                 print RCF $1 . "=" . $HPVALUE{$1} . "\n";
865             }
866             else {
867                 print RCF $_ . "\n";
868             }
869         }
870         else {
871             print RCF $_ . "\n";
872         }
873     }
874     close (RCB);
875     close (RCF);
876 }
877
878 # create notebook
879 sub new_notebook() {
880     my $nb = Gtk2::Notebook->new;
881     # 
882     $nb->append_page (&new_behaviour_page, $xl::s{tab_behaviour});
883     $nb->append_page (&new_colours_page, $xl::s{tab_colours});
884     $nb->append_page (&new_gui_page, $xl::s{tab_gui});
885     $nb->append_page (&new_other_page, $xl::s{tab_other});
886
887     return $nb;
888 }
889
890 # create an about dialog
891 sub new_about_dialog() {
892     my ($parent) = @_;
893     my $title = $xl::s{about_title};
894     my $lic = $xl::s{about_license};
895     my $vers = $xl::s{about_version} . " $VERSION";
896     my $license = 
897 "This program is free software: you can redistribute it and/or modify
898 it under the terms of the GNU General Public License as published by
899 the Free Software Foundation, either version 3 of the License, or
900 (at your option) any later version.
901
902 This program is distributed in the hope that it will be useful,
903 but WITHOUT ANY WARRANTY; without even the implied warranty of
904 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
905 GNU General Public License for more details.
906
907 You should have received a copy of the GNU General Public License
908 along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.";
909     my $year = "2007";
910     my $holder = "Ricardo Mones &lt;ricardo\@mones.org&gt;";
911
912     my $dialog = Gtk2::MessageDialog->new_with_markup ($parent, 
913                     [qw/modal destroy-with-parent/], 
914                     'info', 'close', 
915                     "<span size=\"x-large\" weight=\"bold\">$title</span>\n"
916                     . "<span size=\"large\">$vers</span>\n"
917                     . "<span>Copyright $year by $holder</span>\n\n"
918                     . "<span size=\"large\">$lic</span>\n\n"
919                     . "<span size=\"small\">$license</span>");
920     $dialog->set_title ($xl::s{about});
921     #
922     return $dialog;
923 }
924
925 # create buttons box
926 sub new_button_box() {
927     my ($parent, $adlg) = @_;
928     my $b_about = Gtk2::Button->new_from_stock ('gtk-about');
929     my $b_exit = Gtk2::Button->new_from_stock ('gtk-quit');
930     my $b_apply = Gtk2::Button->new_from_stock ('gtk-apply');
931     # disable button until is really implemented
932     # my $b_undo = Gtk2::Button->new_from_stock ('gtk-undo');
933     my $hbox = Gtk2::HBox->new (FALSE, 5);
934     # signal handlers 
935     $b_exit->signal_connect (clicked => sub { Gtk2->main_quit });
936     $b_apply->signal_connect (clicked => sub { &save_preferences ($parent) });
937     # $b_undo->signal_connect (clicked => sub { &undo_current_changes });
938     $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
939     # package them
940     $hbox->pack_end ($b_exit, FALSE, FALSE, 0);
941     $hbox->pack_end ($b_apply, FALSE, FALSE, 0);
942     # $hbox->pack_end ($b_undo, FALSE, FALSE, 0);
943     $hbox->pack_start ($b_about, FALSE, FALSE, 0);
944     #
945     return $hbox;
946 }
947
948 # initialise
949 &parse_command_line;
950 &load_preferences;
951 &init_hidden_preferences;
952 # create main GUI
953 my $window = Gtk2::Window->new ('toplevel');
954 my $box = Gtk2::VBox->new (FALSE, 5);
955 $box->set_border_width(3);
956 my $about = &new_about_dialog;
957 $box->pack_start (&new_notebook, FALSE, FALSE, 0);
958 $box->pack_end (&new_button_box ($window, $about), FALSE, FALSE, 0);
959 $window->signal_connect (delete_event => sub { Gtk2->main_quit });
960 $window->set_title ($xl::s{win_title});
961 $window->add ($box);
962 $window->show_all;
963 Gtk2->main;
964