33cff4286600749c0937e7d34db0a8860aebccdd
[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 not widely used are not provided with a graphical interface 
23 for inspection and/or modification.
24
25 Users wanting to edit such preferences had to face raw edition 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 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 and 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 address book dialog'),
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 addressbook\'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 => _('Swap 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 => _('Cursor visible 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 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 summary view'),
156     h_gui_strip_sum => _('Enable alternately coloured lines in summary view and folder list'),
157
158     l_beh_hover_t => _('Drag and 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 flowed format 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 update step (items)'),
169     h_beh_up_step => _('Update stepping in progress bars.'),
170     l_beh_thread_a => _('Maximum age in 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 => _('Use UTF-8 in broken mails'),
175     h_beh_use_utf8 => _('Use UTF-8 encoding for broken mails instead of current locale.'),
176     l_beh_warn_dnd => _('Warning on drag and drop'),
177     h_beh_warn_dnd => _('Display a confirmation dialogue on drag \'n\' drop of folders.'),
178
179     l_col_emphasis => _('Emphasised X-Mailer colour'),
180     h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
181     l_col_log_err => _('Errors colour'),
182     h_col_log_err => _('Colour for error messages in log window.'),
183     l_col_log_in => _('Input colour'),
184     h_col_log_in => _('Colour for input messages in log window.'),
185     l_col_log_msg => _('Messages colour'),
186     h_col_log_msg => _('Colour for messages in log window.'),
187     l_col_log_out => _('Output colour'),
188     h_col_log_out => _('Colour for output messages in log window.'),
189     l_col_log_warn => _('Warnings colour'),
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 $socket = ($ALTCONFIGDIR)? "/tmp/": $CONFIGDIR;
295     $socket .= "claws-mail-$<";
296     -S $socket and die "$xl::s{e_error}$xl::s{e_running}\n";
297 }
298
299 # graphic element creation 
300
301 sub new_check_button_for {
302     my ($hash, $key) = @_;
303     my $name = $$hash{$key}[NAME];
304     my $label = $$hash{$key}[LABEL];
305     #
306     my $hbox = Gtk2::HBox->new (FALSE, 5);
307     my $cb = Gtk2::CheckButton->new ($label);
308     $$hash{$key}[GUI] = $cb;
309     $cb->set_active ($HPVALUE{$name} eq '1');
310     $cb->signal_connect (clicked => sub {
311             my ($w, $e) = @_;
312             &handle_bool_value($w, $e, \$HPVALUE{$name});
313         });
314     $hbox->pack_start ($cb, FALSE, FALSE, HBOX_SPC);
315     #
316     return $hbox;
317 }
318
319 sub new_text_box_for {
320     my ($hash, $key) = @_;
321     my $name = $$hash{$key}[NAME];
322     my $label = $$hash{$key}[LABEL];
323     #
324     my $hbox = Gtk2::HBox->new (FALSE, 5);
325     my $glabel = Gtk2::Label->new ($label);
326     my $gentry = Gtk2::Entry->new;
327     $gentry->set_width_chars (8);
328     $$hash{$key}[GUI] = $gentry;
329     $gentry->set_text($HPVALUE{$name});
330     $gentry->signal_connect(changed => sub {
331             my ($w, $e) = @_;
332             &handle_int_value($w, $e, \$HPVALUE{$name}); # FIXME int only
333         });
334     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
335     $hbox->pack_start ($gentry, FALSE, FALSE, HBOX_SPC);
336     #
337     return $hbox;
338 }
339
340 sub new_color_button_for {
341     my ($hash, $key) = @_;
342     my $name = $$hash{$key}[NAME];
343     my $label = $$hash{$key}[LABEL];
344     #
345     my $col = &gdk_color_from_str ($HPVALUE{$name});
346     my $hbox = Gtk2::HBox->new (FALSE, 5);
347     my $glabel = Gtk2::Label->new ($label);
348     my $button = Gtk2::ColorButton->new_with_color ($col);
349     $$hash{$key}[GUI] = $button;
350     $button->set_title ($label);
351     $button->set_relief ('none');
352     $button->signal_connect ('color-set' => sub {
353             my ($w, $e) = @_;
354             &handle_color_value($w, $e, \$HPVALUE{$name}); 
355         });
356     $hbox->pack_start ($button, FALSE, FALSE, HBOX_SPC);
357     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
358     #
359     return $hbox;
360 }
361
362 sub new_selection_box_for {
363     my ($hash, $key) = @_;
364     my $name = $$hash{$key}[NAME];
365     my $label = $$hash{$key}[LABEL];
366     #
367     my $hbox = Gtk2::HBox->new (FALSE, 5);
368     my $glabel = Gtk2::Label->new ($label);
369     my $combo = Gtk2::ComboBox->new_text;
370     $$hash{$key}[GUI] = $combo;
371     my @options = split (';', $$hash{$key}[TYPE]);
372     foreach my $opt (@options) {
373         my ($index, $textkey) = split ('=', $opt);
374         $combo->insert_text ($index, $xl::s{$textkey});
375     }
376     $combo->signal_connect ('changed' => sub {
377             my ($w, $e) = @_;
378             &handle_selection_value($w, $e, \$HPVALUE{$name});
379         });
380     $combo->set_active ($HPVALUE{$name});
381     $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
382     $hbox->pack_start ($combo, FALSE, FALSE, HBOX_SPC);
383     #
384     return $hbox;
385 }
386
387 # preference maps and corresponding page creation subs
388
389 %pr::oth = ( # other preferences
390     use_dlg => [ 
391         'addressbook_use_editaddress_dialog',
392         $xl::s{l_oth_use_dlg},
393         $xl::s{h_oth_use_dlg},
394         'bool',
395         undef,
396     ],
397     max_use => [
398         'cache_max_mem_usage',
399         $xl::s{l_oth_max_use},
400         $xl::s{h_oth_max_use},
401         'int',
402         undef,
403     ],
404     min_time => [
405         'cache_min_keep_time',
406         $xl::s{l_oth_min_time},
407         $xl::s{h_oth_min_time},
408         'int',
409         undef,
410     ],
411 );
412
413 sub new_other_page() {
414     my $of = Gtk2::VBox->new (FALSE, 5);
415     $of->set_border_width (PAGE_SPC);
416
417     my $ab_frame = Gtk2::Frame->new ($xl::s{ab_frame}); 
418     my $cb_use_dlg = &new_check_button_for(\%pr::oth, 'use_dlg');
419     my $vb1 = Gtk2::VBox->new (FALSE, 5);
420     $vb1->set_border_width (PAGE_SPC); 
421     $vb1->pack_start ($cb_use_dlg, FALSE, FALSE, 0);
422     $ab_frame->add ($vb1);
423
424     my $mem_frame = Gtk2::Frame->new ($xl::s{mem_frame}); 
425     my $tb_max_use = &new_text_box_for(\%pr::oth, 'max_use');
426     my $tb_min_time = &new_text_box_for(\%pr::oth, 'min_time');
427     my $vb2 = Gtk2::VBox->new (FALSE, 5);
428     $vb2->set_border_width (PAGE_SPC); 
429     $vb2->pack_start ($tb_max_use, FALSE, FALSE, 0);
430     $vb2->pack_start ($tb_min_time, FALSE, FALSE, 0);
431     $mem_frame->add ($vb2);
432
433     $of->pack_start ($ab_frame, FALSE, FALSE, FRAME_SPC);
434     $of->pack_start ($mem_frame, FALSE, FALSE, FRAME_SPC);
435
436     return $of;
437 }
438
439 %pr::gui = ( # gui bells and whistles
440     b_unread => [ 
441         'bold_unread',
442         $xl::s{l_gui_b_unread},
443         $xl::s{h_gui_b_unread},
444         'bool',
445         undef,
446     ],
447     no_markup => [
448         'compose_no_markup',
449         $xl::s{l_gui_no_markup},
450         $xl::s{h_gui_no_markup},
451         'bool',
452         undef,
453     ],
454     dot_lines => [
455         'enable_dotted_lines',
456         $xl::s{l_gui_dot_lines},
457         $xl::s{h_gui_dot_lines},
458         'bool',
459         undef,
460     ],
461     h_scroll => [
462         'enable_hscrollbar',
463         $xl::s{l_gui_h_scroll},
464         $xl::s{h_gui_h_scroll},
465         'bool',
466         undef,
467     ],
468     swp_from => [
469         'enable_swap_from',
470         $xl::s{l_gui_swp_from},
471         $xl::s{h_gui_swp_from},
472         'bool',
473         undef,
474     ],
475     v_scroll => [
476         'folderview_vscrollbar_policy',
477         $xl::s{l_gui_v_scroll},
478         $xl::s{h_gui_v_scroll},
479         '0=l_gui_v_scroll_show;1=l_gui_v_scroll_auto;2=l_gui_v_scroll_hide',
480         undef,
481     ],
482     strip_off => [
483         'stripes_color_offset',
484         $xl::s{l_gui_strip_off},
485         $xl::s{h_gui_strip_off},
486         'int',
487         undef,
488     ],
489     cursor_v => [
490         'textview_cursor_visible',
491         $xl::s{l_gui_cursor_v},
492         $xl::s{h_gui_cursor_v},
493         'bool',
494         undef,
495     ],
496     toolbar_d => [
497         'toolbar_detachable',
498         $xl::s{l_gui_toolbar_d},
499         $xl::s{h_gui_toolbar_d},
500         'bool',
501         undef,
502     ],
503     strip_all => [
504         'use_stripes_everywhere',
505         $xl::s{l_gui_strip_all},
506         $xl::s{h_gui_strip_all},
507         'bool',
508         undef,
509     ],
510     strip_sum => [
511         'use_stripes_in_summaries',
512         $xl::s{l_gui_strip_sum},
513         $xl::s{h_gui_strip_sum},
514         'bool',
515         undef,
516     ],
517 );
518
519 sub new_gui_page() {
520     my $gf = Gtk2::VBox->new (FALSE, 5);
521     $gf->set_border_width (PAGE_SPC);
522
523     my $stripes_frame = Gtk2::Frame->new ($xl::s{stripes_frame});
524     my $cb_strip_all = &new_check_button_for (\%pr::gui, 'strip_all');
525     my $cb_strip_sum = &new_check_button_for (\%pr::gui, 'strip_sum');
526     my $tb_strip_off = &new_text_box_for (\%pr::gui, 'strip_off');
527     my $vb1 = Gtk2::VBox->new (FALSE, 5);
528     $vb1->set_border_width (PAGE_SPC);
529     $vb1->pack_start ($cb_strip_all, FALSE, FALSE, 0);
530     $vb1->pack_start ($cb_strip_sum, FALSE, FALSE, 0);
531     $vb1->pack_start ($tb_strip_off, FALSE, FALSE, 0);
532     $stripes_frame->add ($vb1);
533
534     my $mlist_frame = Gtk2::Frame->new ($xl::s{mlist_frame});
535     my $cb_b_unread = &new_check_button_for (\%pr::gui, 'b_unread');
536     my $cb_swp_from = &new_check_button_for (\%pr::gui, 'swp_from');
537     my $vb3 = Gtk2::VBox->new (FALSE, 5);
538     $vb3->set_border_width (PAGE_SPC);
539     $vb3->pack_start ($cb_b_unread, FALSE, FALSE, 0);
540     $vb3->pack_start ($cb_swp_from, FALSE, FALSE, 0);
541     $mlist_frame->add ($vb3);
542
543     my $sbar_frame = Gtk2::Frame->new ($xl::s{sbar_frame});
544     my $cb_h_scroll = &new_check_button_for (\%pr::gui, 'h_scroll');
545     my $sb_v_scroll = &new_selection_box_for (\%pr::gui, 'v_scroll');
546     my $vb2 = Gtk2::VBox->new (FALSE, 5);
547     $vb2->set_border_width (PAGE_SPC);
548     $vb2->pack_start ($cb_h_scroll, FALSE, FALSE, 0);
549     $vb2->pack_start ($sb_v_scroll, FALSE, FALSE, 0);
550     $sbar_frame->add ($vb2);
551
552     my $cb_no_markup = &new_check_button_for (\%pr::gui, 'no_markup'); 
553     my $cb_dot_lines = &new_check_button_for (\%pr::gui, 'dot_lines'); 
554     my $cb_cursor_v = &new_check_button_for (\%pr::gui, 'cursor_v');
555     my $cb_toolbar_d = &new_check_button_for (\%pr::gui, 'toolbar_d');
556
557     $gf->pack_start ($stripes_frame, FALSE, FALSE, FRAME_SPC);
558     $gf->pack_start ($mlist_frame, FALSE, FALSE, FRAME_SPC);
559     $gf->pack_start ($cb_no_markup, FALSE, FALSE, 0);
560     $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
561     $gf->pack_start ($cb_cursor_v, FALSE, FALSE, 0);
562     $gf->pack_start ($cb_toolbar_d, FALSE, FALSE, 0);
563     $gf->pack_start ($sbar_frame, FALSE, FALSE, FRAME_SPC);
564
565     return $gf;
566 }
567
568 %pr::beh = ( # tweak some behaviour
569     hover_t => [
570         'hover_timeout',
571         $xl::s{l_beh_hover_t},
572         $xl::s{h_beh_hover_t},
573         'int',
574         undef,
575     ],
576     dangerous => [
577         'live_dangerously',
578         $xl::s{l_beh_dangerous},
579         $xl::s{h_beh_dangerous},
580         'bool',
581         undef,
582     ],
583     flowed => [
584         'respect_flowed_format',
585         $xl::s{l_beh_flowed},
586         $xl::s{h_beh_flowed},
587         'bool',
588         undef,
589     ],
590     parts_rw => [
591         'save_parts_readwrite',
592         $xl::s{l_beh_parts_rw},
593         $xl::s{h_beh_parts_rw},
594         'bool',
595         undef,
596     ],
597     skip_ssl => [
598         'skip_ssl_cert_check',
599         $xl::s{l_beh_skip_ssl},
600         $xl::s{h_beh_skip_ssl},
601         'bool',
602         undef,
603     ],
604     up_step => [
605         'statusbar_update_step',
606         $xl::s{l_beh_up_step},
607         $xl::s{h_beh_up_step},
608         'int',
609         undef,
610     ],
611     thread_a => [
612         'thread_by_subject_max_age',
613         $xl::s{l_beh_thread_a},
614         $xl::s{h_beh_thread_a},
615         'int',
616         undef,
617     ],
618     unsafe_ssl => [
619         'unsafe_ssl_certs',
620         $xl::s{l_beh_unsafe_ssl},
621         $xl::s{h_beh_unsafe_ssl},
622         'bool',
623         undef,
624     ],
625     use_utf8 => [
626         'utf8_instead_of_locale_for_broken_mail',
627         $xl::s{l_beh_use_utf8},
628         $xl::s{h_beh_use_utf8},
629         'bool',
630         undef,
631     ],
632     warn_dnd => [
633         'warn_dnd',
634         $xl::s{l_beh_warn_dnd},
635         $xl::s{h_beh_warn_dnd},
636         'bool',
637         undef,
638     ],
639 );
640
641 sub new_behaviour_page() {
642     my $bf = Gtk2::VBox->new (FALSE, 5);
643     $bf->set_border_width (PAGE_SPC);
644
645     my $dnd_frame = Gtk2::Frame->new ($xl::s{dnd_frame});
646     my $tb_hoover_t = &new_text_box_for (\%pr::beh, 'hover_t');
647     my $cb_warn_dnd = &new_check_button_for (\%pr::beh, 'warn_dnd');
648     my $vb1 = Gtk2::VBox->new (FALSE, 5);
649     $vb1->set_border_width (PAGE_SPC);
650     $vb1->pack_start ($cb_warn_dnd, FALSE, FALSE, 0);
651     $vb1->pack_start ($tb_hoover_t, FALSE, FALSE, 0);
652     $dnd_frame->add ($vb1);
653
654     my $ssl_frame = Gtk2::Frame->new ($xl::s{ssl_frame});
655     my $cb_skip_ssl = &new_check_button_for (\%pr::beh, 'skip_ssl');
656     my $cb_unsafe_ssl = &new_check_button_for (\%pr::beh, 'unsafe_ssl');
657     my $hb1 = Gtk2::HBox->new (FALSE, 5);
658     $hb1->set_border_width (PAGE_SPC);
659     $hb1->pack_start ($cb_skip_ssl, FALSE, FALSE, 0);
660     $hb1->pack_start ($cb_unsafe_ssl, FALSE, FALSE, 0);
661     $ssl_frame->add ($hb1);
662
663     my $tb_up_step = &new_text_box_for (\%pr::beh, 'up_step');
664     my $tb_thread_a = &new_text_box_for (\%pr::beh, 'thread_a');
665
666     my $msgs_frame = Gtk2::Frame->new ($xl::s{msgs_frame});
667     my $cb_flowed = &new_check_button_for (\%pr::beh, 'flowed');
668     my $cb_parts_rw = &new_check_button_for (\%pr::beh, 'parts_rw');
669     my $cb_use_utf8 = &new_check_button_for (\%pr::beh, 'use_utf8');
670     my $cb_dangerous = &new_check_button_for (\%pr::beh, 'dangerous');
671     my $vb2 = Gtk2::VBox->new (FALSE, 5);    
672     $vb2->set_border_width (PAGE_SPC);
673     $vb2->pack_start ($cb_flowed, FALSE, FALSE, 0);
674     $vb2->pack_start ($cb_parts_rw, FALSE, FALSE, 0);
675     $vb2->pack_start ($cb_use_utf8, FALSE, FALSE, 0);
676     $vb2->pack_start ($cb_dangerous, FALSE, FALSE, 0);
677     $msgs_frame->add ($vb2);
678
679     $bf->pack_start ($dnd_frame, FALSE, FALSE, FRAME_SPC);
680     $bf->pack_start ($ssl_frame, FALSE, FALSE, FRAME_SPC);
681     $bf->pack_start ($tb_up_step, FALSE, FALSE, 0);
682     $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0);
683     $bf->pack_start ($msgs_frame, FALSE, FALSE, FRAME_SPC);
684
685     return $bf;
686 }
687
688 %pr::col = ( # a variety of colours
689     emphasis => [
690         'emphasis_color',
691         $xl::s{l_col_emphasis},
692         $xl::s{h_col_emphasis},
693         'color',
694         undef,
695     ],
696     log_err => [
697         'log_error_color',
698         $xl::s{l_col_log_err},
699         $xl::s{h_col_log_err},
700         'color',
701         undef,
702     ],
703     log_in => [
704         'log_in_color',
705         $xl::s{l_col_log_in},
706         $xl::s{h_col_log_in},
707         'color',
708         undef,
709     ],
710     log_msg => [
711         'log_msg_color',
712         $xl::s{l_col_log_msg},
713         $xl::s{h_col_log_msg},
714         'color',
715         undef,
716     ],
717     log_out => [
718         'log_out_color',
719         $xl::s{l_col_log_out},
720         $xl::s{h_col_log_out},
721         'color',
722         undef,
723     ],
724     log_warn => [
725         'log_warn_color',
726         $xl::s{l_col_log_warn},
727         $xl::s{h_col_log_warn},
728         'color',
729         undef,
730     ],
731 );
732
733 sub new_colours_page() {
734     my $cf = Gtk2::VBox->new (FALSE, 5);
735     $cf->set_border_width (PAGE_SPC);
736
737     my $msgview_frame = Gtk2::Frame->new ($xl::s{msgview_frame}); 
738     my $cb_emphasis = &new_color_button_for (\%pr::col, 'emphasis');
739     my $vb1 = Gtk2::VBox->new (FALSE, 5);
740     $vb1->set_border_width (PAGE_SPC);
741     $vb1->pack_start ($cb_emphasis, FALSE, FALSE, 0);
742     $msgview_frame->add ($vb1);
743     
744     my $log_frame = Gtk2::Frame->new ($xl::s{log_frame}); 
745     my $cb_log_err = &new_color_button_for (\%pr::col, 'log_err');
746     my $cb_log_in = &new_color_button_for (\%pr::col, 'log_in');
747     my $cb_log_msg = &new_color_button_for (\%pr::col, 'log_msg');
748     my $cb_log_out = &new_color_button_for (\%pr::col, 'log_out');
749     my $cb_log_warn = &new_color_button_for (\%pr::col, 'log_warn');
750     my $vb2 = Gtk2::VBox->new (FALSE, 5);
751     $vb2->set_border_width (PAGE_SPC);
752     $vb2->pack_start ($cb_log_err, FALSE, FALSE, 0);
753     $vb2->pack_start ($cb_log_in, FALSE, FALSE, 0);
754     $vb2->pack_start ($cb_log_msg, FALSE, FALSE, 0);
755     $vb2->pack_start ($cb_log_out, FALSE, FALSE, 0);
756     $vb2->pack_start ($cb_log_warn, FALSE, FALSE, 0);
757     $log_frame->add ($vb2);
758
759     $cf->pack_start ($msgview_frame, FALSE, FALSE, 0);
760     $cf->pack_start ($log_frame, FALSE, FALSE, 0);
761
762     return $cf;
763 }
764
765 # the command line help
766 sub print_help() {
767     my $line = '-' x length ($xl::s{about_title}) . "\n";
768     print $line;
769     print $xl::s{about_title} . "\n";
770     print $xl::s{about_version} . " $VERSION\n";
771     print $line;
772     print _("Syntax:\n");
773     print _("    clawsker [options]\n");
774     print _("Options:\n");
775     print _("    --help                         Prints this help screen.\n");
776     print _("    --alternate-config-dir <dir>   Uses <dir> as Claws Mail config dir.\n");
777     print _("    --clawsrc <file>               Uses <file> as full resource name.\n");
778 }
779
780 # parse the command line
781 sub parse_command_line() {
782     my $arg = 0;
783     while (defined($ARGV[$arg])) {
784         for ($ARGV[$arg]) {
785             /--help/ && do { 
786                 &print_help; 
787                 exit 0;
788             };
789             /--alternate-config-dir/ && do {
790                 ++$arg;
791                 die "$xl::s{e_error}$xl::s{e_requireddir}\n" 
792                     unless defined($ARGV[$arg]);
793                 die "$xl::s{e_error}$xl::s{e_notadir}\n" 
794                     unless -d $ARGV[$arg];
795                 $CONFIGDIR = $ARGV[$arg];
796                 $ALTCONFIGDIR = TRUE;
797                 last;
798             };
799             /--clawsrc/ && do {
800                 ++$arg;
801                 die "$xl::s{e_error}$xl::s{e_requiredfile}\n" 
802                     unless defined($ARGV[$arg]);
803                 die "$xl::s{e_error}$xl::s{e_notafile}\n" 
804                     unless -f $ARGV[$arg];
805                 &set_rc_filename ($ARGV[$arg]);
806                 last;
807             };
808             /.*/ && die $xl::s{e_error} 
809                         . _("unknown option '{opt}'.\n", opt => $ARGV[$arg]);
810         }
811         ++$arg;
812     }
813 }
814
815 # update the hidden preferences status from loaded values
816 sub init_hidden_preferences() {
817     foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth) {
818         foreach my $key (keys %$hash) { 
819             $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]};
820         }
821     }
822 }
823
824 # load current status from disc
825 sub load_preferences() {
826     my $rc = &get_rc_filename;
827     -f $rc or die "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
828     &check_claws_not_running;
829     open (RCF, "<$rc");
830     while (<RCF>) {
831         chomp;
832         if (/^([8a-z_]+)=(.*)$/) {
833             $PREFS{$1} = "$2";
834         }
835     }
836     close (RCF);
837 }
838
839 # save current preferences to disc
840 sub save_preferences() {
841     my $rc = &get_rc_filename;
842     -f $rc or die "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
843     &check_claws_not_running;
844     my $rcbak = "$rc.backup";
845     rename ($rc, $rcbak);
846     open (RCF, ">$rc");
847     open (RCB, "<$rcbak");
848     while (<RCB>) {
849         chomp;
850         if (/^([8a-z_]+)=(.*)$/) {
851             if (defined($HPVALUE{$1})) {
852                 print RCF $1 . "=" . $HPVALUE{$1} . "\n";
853             }
854             else {
855                 print RCF $_ . "\n";
856             }
857         }
858         else {
859             print RCF $_ . "\n";
860         }
861     }
862     close (RCB);
863     close (RCF);
864 }
865
866 # create notebook
867 sub new_notebook() {
868     my $nb = Gtk2::Notebook->new;
869     # 
870     $nb->append_page (&new_behaviour_page, $xl::s{tab_behaviour});
871     $nb->append_page (&new_colours_page, $xl::s{tab_colours});
872     $nb->append_page (&new_gui_page, $xl::s{tab_gui});
873     $nb->append_page (&new_other_page, $xl::s{tab_other});
874
875     return $nb;
876 }
877
878 # create an about dialog
879 sub new_about_dialog() {
880     my ($parent) = @_;
881     my $title = $xl::s{about_title};
882     my $lic = $xl::s{about_license};
883     my $vers = $xl::s{about_version} . " $VERSION";
884     my $license = 
885 "This program is free software: you can redistribute it and/or modify
886 it under the terms of the GNU General Public License as published by
887 the Free Software Foundation, either version 3 of the License, or
888 (at your option) any later version.
889
890 This program is distributed in the hope that it will be useful,
891 but WITHOUT ANY WARRANTY; without even the implied warranty of
892 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
893 GNU General Public License for more details.
894
895 You should have received a copy of the GNU General Public License
896 along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.";
897     my $year = "2007";
898     my $holder = "Ricardo Mones &lt;ricardo\@mones.org&gt;";
899
900     my $dialog = Gtk2::MessageDialog->new_with_markup ($parent, 
901                     [qw/modal destroy-with-parent/], 
902                     'info', 'close', 
903                     "<span size=\"x-large\" weight=\"bold\">$title</span>\n"
904                     . "<span size=\"large\">$vers</span>\n"
905                     . "<span>Copyright $year by $holder</span>\n\n"
906                     . "<span size=\"large\">$lic</span>\n\n"
907                     . "<span size=\"small\">$license</span>");
908     $dialog->set_title ($xl::s{about});
909     #
910     return $dialog;
911 }
912
913 # create buttons box
914 sub new_button_box() {
915     my ($adlg) = @_;
916     my $b_about = Gtk2::Button->new_from_stock ('gtk-about');
917     my $b_exit = Gtk2::Button->new_from_stock ('gtk-quit');
918     my $b_apply = Gtk2::Button->new_from_stock ('gtk-apply');
919     # disable button until is really implemented
920     # my $b_undo = Gtk2::Button->new_from_stock ('gtk-undo');
921     my $hbox = Gtk2::HBox->new (FALSE, 5);
922     # signal handlers 
923     $b_exit->signal_connect (clicked => sub { Gtk2->main_quit });
924     $b_apply->signal_connect (clicked => sub { &save_preferences });
925     # $b_undo->signal_connect (clicked => sub { &undo_current_changes });
926     $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
927     # package them
928     $hbox->pack_end ($b_exit, FALSE, FALSE, 0);
929     $hbox->pack_end ($b_apply, FALSE, FALSE, 0);
930     # $hbox->pack_end ($b_undo, FALSE, FALSE, 0);
931     $hbox->pack_start ($b_about, FALSE, FALSE, 0);
932     #
933     return $hbox;
934 }
935
936 # initialise
937 &parse_command_line;
938 &load_preferences;
939 &init_hidden_preferences;
940 # create main GUI
941 my $window = Gtk2::Window->new ('toplevel');
942 my $box = Gtk2::VBox->new (FALSE, 5);
943 $box->set_border_width(3);
944 my $about = &new_about_dialog;
945 $box->pack_start (&new_notebook, FALSE, FALSE, 0);
946 $box->pack_end (&new_button_box ($about), FALSE, FALSE, 0);
947 $window->signal_connect (delete_event => sub { Gtk2->main_quit });
948 $window->set_title ($xl::s{win_title});
949 $window->add ($box);
950 $window->show_all;
951 Gtk2->main;
952