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