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