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