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