Add measure units after spin buttons
[clawsker.git] / clawsker
1 #!/usr/bin/perl -w
2 #
3 # Clawsker :: A Claws Mail Tweaker
4 # Copyright 2007-2016 Ricardo Mones <ricardo@mones.org>
5 #
6 # This program is free software: you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation, either version 3 of the License, or
9 # (at your option) any later version.
10 #
11 # See COPYING file for license details.
12 # See AUTHORS file for a complete list of contributors.
13 #
14
15 binmode STDOUT, ":encoding(utf8)";
16
17 use 5.010_000;
18 use strict;
19 use utf8;
20 use version 0.77;
21 use Glib qw(TRUE FALSE);
22 use Gtk2;
23 use POSIX qw(setlocale);
24 use Locale::gettext;
25 use Encode;
26 use Digest::MD5 qw(md5_hex);
27 use Getopt::Long;
28
29 my $NAME = 'clawsker';
30 my $PREFIX = '@PREFIX@';
31 my $LIBDIR = '@LIBDIR@';
32 my $DATADIR = '@DATADIR@';
33 my $VERSION = '@VERSION@';
34 my $VERBOSE = FALSE;
35 my $READONLY = FALSE;
36 my $CLAWSV = undef;
37 my $main_window = undef;
38
39 my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG});
40 $locale = "C" unless defined($locale);
41 setlocale (LC_ALL, $locale);
42 bindtextdomain ($NAME, sprintf ('%s/share/locale', $PREFIX));
43 textdomain ($NAME);
44
45 my $SHOWHINTS = FALSE;
46 $SHOWHINTS = TRUE if ($Gtk2::VERSION >= 1.040 and Gtk2->CHECK_VERSION (2, 12, 0));
47
48 sub _ {
49     my $str = shift;
50     my %par = @_;
51     my $xla = gettext ($str);
52     if (scalar(keys(%par)) > 0) {
53         foreach my $key (keys %par) {
54             $xla =~ s/\{$key\}/$par{$key}/g;
55         }
56     }
57     return decode_utf8($xla);
58 }
59
60 # default messages
61 %xl::s = (
62     win_title => _('Claws Mail Hidden Preferences'),
63     about => _('About...'),
64     about_title => _('Clawsker :: A Claws Mail Tweaker'),
65     about_license => _('License:'),
66     about_version => _('Version:'),
67
68     tab_colours => _('Colours'),
69     tab_behaviour => _('Behaviour'),
70     tab_gui => _('GUI'),
71     tab_other => _('Other'),
72     tab_winpos => _('Windows'),
73     tab_accounts => _('Accounts'),
74     tab_plugins => _('Plugins'),
75     tab_info => _('Info'),
76
77     ab_frame => _('Addressbook'),
78     mem_frame => _('Memory'),
79     msgview_frame => _('Message View'),
80     log_frame => _('Log window'),
81     dnd_frame => _('Drag \'n\' drop'),
82     ssl_frame => _('Secure Sockets Layer'),
83     msgs_frame => _('Messages'),
84     swc_frame => _('Completion'),
85     stripes_frame => _('Coloured stripes'),
86     sbar_frame => _('Scroll bars'),
87     mlist_frame => _('Message List'),
88     mview_frame => _('Message View'),
89     compo_frame => _('Compose window'),
90     netm_frame => _('NetworkManager'),
91     diff_frame => _('Viewing patches'),
92     mpass_frame => _('Master passphrase'),
93     compose_frame => _('Compose window'),
94     qs_frame => _('Quick search'),
95
96     l_oth_use_dlg => _('Use detached address book edit dialogue'),
97     h_oth_use_dlg => _('If true use a separate dialogue to edit a person\'s details. Otherwise will use a form embedded in the address book\'s main window.'),
98     l_oth_max_use => _('Maximum memory for message cache'),
99     l_oth_max_use_units => _('kilobytes'),
100     h_oth_max_use => _('The maximum amount of memory to use to cache messages, in kilobytes.'),
101     l_oth_min_time => _('Minimun time for cache elements'),
102     l_oth_min_time_units => _('minutes'),
103     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.'),
104     l_oth_use_netm => _('Use NetworkManager'),
105     h_oth_use_netm => _('Use NetworkManager to switch offline automatically.'),
106     l_oth_mp_rounds => _('Rounds for PBKDF2 function'),
107     h_oth_mp_rounds => _('Specify the number of iterations the key derivation function will be applied on master passphrase computation. Does not modify currently stored passphrase, only master passphrases computed after changing this value are affected.'),
108
109     l_gui_b_unread => _('Show unread messages with bold font'),
110     h_gui_b_unread => _('Show unread messages in the Message List using a bold font.'),
111     l_gui_no_markup => _('Don\'t use markup'),
112     h_gui_no_markup => _('Don\'t use bold and italic text in Compose dialogue\'s account selector.'),
113     l_gui_dot_lines => _('Use dotted lines in tree view components'),
114     h_gui_dot_lines => _('Use the old dotted line look in the main window tree views (Folder, Message and other lists) instead of the modern lineless look.'),
115     l_gui_h_scroll => _('Enable horizontal scrollbar'),
116     h_gui_h_scroll => _('Enable the horizontal scrollbar in the Message List.'),
117     l_gui_swp_from => _('Display To column instead From column in Sent folder'),
118     h_gui_swp_from => _('Display the recipient\'s email address in a To column of the Sent folder instead of the originator\'s one in a From column.'),
119     l_gui_v_scroll => _('Folder List scrollbar behaviour'),
120     h_gui_v_scroll => _('Specify the policy of vertical scrollbar of Folder List: show always, automatic or hide always.'),
121     l_gui_v_scroll_show => _('Show always'),
122     l_gui_v_scroll_auto => _('Automatic'),
123     l_gui_v_scroll_hide => _('Hide always'),
124     l_gui_from_show => _('From column displays'),
125     h_gui_from_show => _('Selects the data displayed in the From column of the Message List: name, address or both.'),
126     l_gui_from_show_name => _('Name only'),
127     l_gui_from_show_addr => _('Address only'),
128     l_gui_from_show_both => _('Both name and address'),
129     l_gui_strip_off => _('Coloured lines contrast'),
130     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.'),
131     l_gui_cursor_v => _('Show cursor'),
132     h_gui_cursor_v => _('Display the cursor in the Message View.'),
133     l_gui_toolbar_d => _('Detachable toolbars'),
134     h_gui_toolbar_d => _('Show handles in the toolbars.'),
135     l_gui_strip_all => _('Use stripes in all tree view components'),
136     h_gui_strip_all => _('Enable alternately coloured lines in all tree view components.'),
137     l_gui_strip_sum => _('Use stripes in Folder List and Message List'),
138     h_gui_strip_sum => _('Enable alternately coloured lines in Message List and Folder List.'),
139     l_gui_two_line_v => _('2 lines per Message List item in 3-column layout'),
140     h_gui_two_line_v => _('Spread Message List information over two lines when using the three column mode.'),
141     l_gui_margin_co => _('Show margin'),
142     h_gui_margin_co => _('Shows a small margin in the Compose View.'),
143     l_gui_mview_date => _('Don\'t display localized date'),
144     h_gui_mview_date => _('Toggles localization of date format in Message View.'),
145     l_gui_zero_char => _('Zero replacement character'),
146     h_gui_zero_char => _('Replaces \'0\' with the given character in Folder List.'),
147     l_gui_type_any => _('Editable headers'),
148     h_gui_type_any => _('Allows to manually type any value in Compose Window header entries or just select from the available choices in the associated dropdown list.'),
149     l_gui_warn_send_multi => _('Warn when sending to more than'),
150     l_gui_warn_send_multi_units => _('recipients'),
151     h_gui_warn_send_multi => _('Show a warning dialogue when sending to more recipients than specified. Use 0 to disable this check.'),
152     l_gui_next_del => _('Select next message on delete'),
153     h_gui_next_del => _('When deleting a message, toggles between selecting the next one (newer message) or the previous one (older message).'),
154
155     l_beh_hover_t => _('Drag \'n\' drop hover timeout'),
156     l_beh_hover_t_units => _('milliseconds'),
157     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.'),
158     l_beh_dangerous => _('Don\'t confirm deletions (dangerous!)'),
159     h_beh_dangerous => _('Don\'t ask for confirmation before definitive deletion of emails.'),
160     l_beh_flowed => _('Respect format=flowed in messages'),
161     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.'),
162     l_beh_parts_rw => _('Allow writable temporary files'),
163     h_beh_parts_rw => _('Saves temporary files when opening attachment with write bit set.'),
164     l_beh_skip_ssl => _('Don\'t check SSL certificates'),
165     h_beh_skip_ssl => _('Disables the verification of SSL certificates.'),
166     l_beh_up_step => _('Progress bar update step every'),
167     l_beh_up_step_units => _('items'),
168     h_beh_up_step => _('Update stepping in progress bars.'),
169     l_beh_thread_a => _('Maximum age when threading by subject'),
170     l_beh_thread_a_units => _('days'),
171     h_beh_thread_a => _('Number of days to include a message in a thread when using "Thread using subject in addition to standard headers".'),
172     l_beh_unsafe_ssl => _('Allow unsafe SSL certificates'),
173     h_beh_unsafe_ssl => _('Allows Claws Mail to remember multiple SSL certificates for a given server/port.'),
174     l_beh_use_utf8 => _('Force UTF-8 for broken mails'),
175     h_beh_use_utf8 => _('Use UTF-8 encoding for broken mails instead of current locale.'),
176     l_beh_warn_dnd => _('Warn on drag \'n\' drop'),
177     h_beh_warn_dnd => _('Display a confirmation dialogue on drag \'n\' drop of folders.'),
178     l_beh_out_ascii => _('Outgoing messages fallback to ASCII'),
179     h_beh_out_ascii => _('If allowed by content, ASCII will be used to encode outgoing messages, otherwise the user-defined encoding is always enforced.'),
180     l_beh_pp_unsel => _('Primary paste unselects selection'),
181     h_beh_pp_unsel => _('Controls how pasting using middle-click changes the selected text and insertion point.'),
182     l_beh_inline_at => _('Show inline attachments'),
183     h_beh_inline_at => _('Allows to hide inline attachments already shown in mail structure view.'),
184     l_beh_addr_swc => _('Address search in compose window matches any'),
185     h_beh_addr_swc => _('On Tab-key completion, address text will match any part of the string or only from the start.'),
186     l_beh_fold_swc => _('Folder search in folder selector matches any'),
187     h_beh_fold_swc => _('On folder name completion text will match any part of the string or only from the start.'),
188     l_beh_rewrite_ff => _('Rewrite first \'From\' using QP encoding'),
189     h_beh_rewrite_ff => _('Workaround some servers which convert first \'From\' to \'>From\' by using Quoted-Printable transfer encoding instead of 7bit/8bit encoding.'),
190
191     l_col_emphasis => _('X-Mailer header'),
192     h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
193     l_col_log_err => _('Error messages'),
194     h_col_log_err => _('Colour for error messages in log window.'),
195     l_col_log_in => _('Server messages'),
196     h_col_log_in => _('Colour for messages received from servers in log window.'),
197     l_col_log_msg => _('Standard messages'),
198     h_col_log_msg => _('Colour for messages in log window.'),
199     l_col_log_out => _('Client messages'),
200     h_col_log_out => _('Colour for messages sent to servers in log window.'),
201     l_col_log_warn => _('Warning messages'),
202     h_col_log_warn => _('Colour for warning messages in log window.'),
203
204     l_col_tags_bg => _('Tags background'),
205     h_col_tags_bg => _('Background colour for tags in message view.'),
206     l_col_tags_text => _('Tags text'),
207     h_col_tags_text => _('Text colour for tags in message view.'),
208
209     l_col_default_header_bg => _('Default headers background'),
210     h_col_default_header_bg => _('Background colour for default headers in compose window.'),
211     l_col_default_header_text => _('Default headers text'),
212     h_col_default_header_text => _('Text colour for default headers in compose window.'),
213
214     l_col_qs_active_bg => _('Active quick search background'),
215     h_col_qs_active_bg => _('Background colour for active quick search.'),
216     l_col_qs_active_text => _('Active quick search text'),
217     h_col_qs_active_text => _('Text colour for active quick search.'),
218     l_col_qs_error_bg => _('Quick search error background'),
219     h_col_qs_error_bg => _('Background colour for quick search error.'),
220     l_col_qs_error_text => _('Quick search error text'),
221     h_col_qs_error_text => _('Text colour for quick search error.'),
222
223     l_col_diff_add => _('Added lines'),
224     h_col_diff_add => _('Colour for added lines in patches.'),
225     l_col_diff_del => _('Deleted lines'),
226     h_col_diff_del => _('Colour for deleted lines in patches.'),
227     l_col_diff_hunk => _('Hunk lines'),
228     h_col_diff_hunk => _('Colour for hunk headers in patches.'),
229
230     l_win_x => _('X position'),
231     h_win_x => _('X coordinate for window\'s top-left corner.'),
232     l_win_y => _('Y position'),
233     h_win_y => _('Y coordinate for window\'s top-left corner.'),
234     l_win_w => _('Width'),
235     h_win_w => _('Window\'s width in pixels.'),
236     l_win_h => _('Height'),
237     h_win_h => _('Window\'s height in pixels.'),
238
239     l_win_main_mx => _('Maximized'),
240     h_win_main_mx => _('Changes window maximized status.'),
241     l_win_main_fs => _('Full-screen'),
242     h_win_main_fs => _('Changes full screen status.'),
243
244     l_acc_gtls_set => _('Use custom GnuTLS priority'),
245     h_acc_gtls_set => _('Enables using user provided GnuTLS priority string.'),
246     l_acc_gtls_pri => _('GnuTLS priority'),
247     h_acc_gtls_pri => _('Value to use as GnuTLS priority string if custom priority check is enabled. Otherwise this value is ignored.'),
248
249     l_plu_gpg_alimit => _('Autocompletion limit'),
250     h_plu_gpg_alimit => _('Limits the number of addresses obtained from keyring through autocompletion. Use 0 to get all matches.'),
251     l_plu_lav_burl => _('Base URL'),
252     h_plu_lav_burl => _('This is the URL where avatar requests are sent. You can use the one of your own libravatar server, if available.'),
253     l_plu_prl_flvb => _('Log level'),
254     h_plu_prl_flvb => _('Verbosity level of log, accumulative.'),
255     l_plu_prl_none => _('None'),
256     l_plu_prl_manual => _('Manual'),
257     l_plu_prl_action => _('Actions'),
258     l_plu_prl_match => _('Matches'),
259 );
260
261 # data and metadata of resource files
262 my $CONFIGDATA;
263 my $CONFIGMETA;
264 my $ACCOUNTDATA;
265 my $ACCOUNTMETA;
266 # all preferences read by load_preferences
267 my %PREFS = ();
268 my %ACPREFS = ();
269 my %PLPREFS = ();
270 # values of all preferences handled by clawsker
271 my %HPVALUE = ();
272 my %ACHPVALUE = ();
273 my %PLHPVALUE = ();
274 # default config dir and file name
275 my $ALTCONFIGDIR = FALSE;
276 my $CONFIGDIR = $ENV{HOME} . '/.claws-mail/';
277 my $CONFIGRC = 'clawsrc';
278 my $ACCOUNTRC = 'accountrc';
279 # supported and available plugins lists
280 my @PLUGINS = qw(AttRemover GPG ManageSieve Libravatar PerlPlugin);
281 my @AVPLUGINS = ();
282 # loaded icons
283 my @APPICONS = ();
284
285 # index constants for preference arrays
286 use constant NAME  => 0; # the name on the rc file
287 use constant LABEL => 1; # the label on the GUI
288 use constant DESC  => 2; # the description for the hint/help
289 use constant TYPE  => 3; # data type: bool, int, float, string, color
290 use constant CMVER => 4; # lowest[,highest] Claws Mail version(s) the feature exists
291 use constant CMDEF => 5; # default value for the preference in Claws Mail
292 use constant PLUGIN => 6; # plugin section (only in plugin preferences)
293
294 # constants for GUI spacing
295 use constant HBOX_PAD => 5;
296 use constant FRAME_SPC => 2;
297 use constant PAGE_SPC => 5;
298
299 # version functions
300
301 sub version_greater_or_equal {
302     my ($version, $refvers) = @_;
303     return TRUE if (length($version) == 0 and length($refvers) >= 0);
304     return FALSE if (length($version) >= 0 and length($refvers) == 0);
305     return TRUE if (version->parse($version) >= version->parse($refvers));
306     return FALSE;
307 }
308
309 sub get_claws_version {
310     $_ = qx/which claws-mail/;
311     chomp;
312     return "" unless ($_); # not installed
313     my $res = "";
314     $_ = qx/$_ -v/;
315     chomp;
316     my @fver = split (/ /);
317     die "Invalid version string" unless ($fver[2] eq "version");
318     my @ver = split (/\./, $fver[3]);
319     $res .= "$ver[0].";
320     $res .= "$ver[1].";
321     if ($ver[2] =~ /(\d+)git(\d+)/) {
322         $res .= "$1.$2";
323     }
324     else {
325         $res .= "$ver[2].0";
326     }
327     return $res;
328 }
329
330 # data handlers and auxiliar functions
331
332 sub handle_bool_value {
333     my ($widget, $event, $dataref) = @_;
334     $$dataref = ($widget->get_active ())? '1': '0';
335 }
336
337 sub handle_int_value {
338     my ($widget, $event, $dataref) = @_;
339     $_ = $widget->get_text ();
340     s/^\s+//;
341     s/\s+$//;
342     if (/^[0-9]+$/) {
343         $$dataref = $_;
344         $widget->set_text ($_);
345     }
346     else {
347         $widget->set_text ($$dataref);
348     }
349 }
350
351 sub handle_string_value {
352     my ($widget, $event, $dataref) = @_;
353     $$dataref = $widget->get_text ();
354 }
355
356 sub handle_nchar_value {
357     my ($widget, $event, $dataref, $minlen, $maxlen) = @_;
358     $_ = substr ($widget->get_text (), 0, $maxlen);
359     $widget->set_text ($_);
360     $$dataref = $_;
361 }
362
363 sub gdk_color_from_str {
364     my ($str) = @_;
365     my ($rr, $gg, $bb) = (0, 0 ,0);
366     $_ = uc ($str);
367     if (/\#([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])/) {
368         $rr = hex($1) * 256;
369         $gg = hex($2) * 256;
370         $bb = hex($3) * 256;
371     }
372     my $color = Gtk2::Gdk::Color->new ($rr, $gg, $bb);
373     return $color;
374 }
375
376 sub str_from_gdk_color {
377     my ($color) = @_;
378     my $rr = $color->red / 256;
379     my $gg = $color->green / 256;
380     my $bb = $color->blue / 256;
381     my $str = sprintf ("#%.2x%.2x%.2x", $rr, $gg, $bb);
382     return $str;
383 }
384
385 sub handle_color_value {
386     my ($widget, $event, $dataref) = @_;
387     my $newcol = $widget->get_color;
388     $$dataref = str_from_gdk_color ($newcol);
389 }
390
391 sub handle_selection_value {
392     my ($widget, $event, $dataref) = @_;
393     $$dataref = $widget->get_active;
394 }
395
396 sub get_rc_filename {
397     return $CONFIGDIR . $CONFIGRC;
398 }
399
400 sub get_ac_rc_filename {
401     return $CONFIGDIR . $ACCOUNTRC;
402 }
403
404 sub set_rc_filename {
405     my ($fullname) = @_;
406     my @parts = split ('/', $fullname);
407     $CONFIGRC = $parts[$#parts];
408     $parts[$#parts] = '';
409     $CONFIGDIR = join ('/', @parts);
410 }
411
412 sub log_message {
413     my ($mesg, $fatal) = @_;
414     if (defined($fatal) && $fatal eq 'die') {
415         die "$NAME: $mesg\n";
416     }
417     if ($VERBOSE) {
418         print "$NAME: $mesg\n";
419     }
420 }
421
422 sub error_dialog {
423     my ($emsg) = @_;
424     my $markup = "<span weight=\"bold\" size=\"large\">" . $emsg . "</span>";
425     my $errordlg = Gtk2::MessageDialog->new_with_markup ($main_window, 'modal', 'error', 'cancel', $markup);
426     $errordlg->set_title (_('Clawsker error'));
427     $errordlg->run;
428     $errordlg->destroy;
429 }
430
431 sub claws_is_running {
432     my $emsg = _('Error: seems Claws Mail is currently running, close it first.');
433     log_message ($emsg);
434     error_dialog ($emsg);
435     return FALSE;
436 }
437
438 sub check_claws_not_running {
439     return TRUE if $READONLY;
440     my $tmpdir = (defined $ENV{TMPDIR})? $ENV{TMPDIR}: '/tmp';
441     $tmpdir = '/tmp' if ($tmpdir eq '');
442     my $lockdir = "$tmpdir/claws-mail-$<";
443     -d $lockdir and do {
444         $_ = $CONFIGDIR;
445         s/\/$//;
446         my $socket = "$lockdir/" . md5_hex ($_);
447         -S $socket and return claws_is_running ();
448     };
449     return TRUE;
450 }
451
452 sub check_rc_file {
453     my ($rcfile) = @_;
454     (defined($rcfile) && -f $rcfile) or do {
455         my $emsg = _('Error: resource file for Claws Mail was not found.');
456         log_message ($emsg);
457         error_dialog ($emsg);
458         return FALSE;
459     };
460     return TRUE;
461 }
462
463 sub set_widget_hint {
464     if ($SHOWHINTS) {
465         my ($wdgt, $hint) = @_;
466         $wdgt->set_tooltip_text ($hint);
467         $wdgt->set_has_tooltip (TRUE);
468     }
469 }
470
471 sub set_widget_sens {
472     my ($wdgt, $versions) = @_;
473     my @ver = split(/,/, $versions);
474     if ($#ver == 1) {
475       $wdgt->set_sensitive (
476         version_greater_or_equal ($CLAWSV, $ver[0])
477         and version_greater_or_equal ($ver[1], $CLAWSV)
478       );
479     } else {
480         $wdgt->set_sensitive (version_greater_or_equal ($CLAWSV, $ver[0]));
481     }
482 }
483
484 # graphic element creation
485
486 sub new_hbox_spaced_pack {
487     my $hbox = Gtk2::HBox->new (FALSE);
488     foreach (@_) {
489         $hbox->pack_start ($_, FALSE, FALSE, HBOX_PAD);
490     }
491     return $hbox;
492 }
493
494 sub new_check_button_for($$$) {
495     my ($hash, $key, $vhash) = @_;
496     my $name = $$hash{$key}[NAME];
497     my $label = $$hash{$key}[LABEL];
498     #
499     my $cb = Gtk2::CheckButton->new ($label);
500     my $value = $$vhash{$name};
501     $value //= $$hash{$key}[CMDEF];
502     $cb->set_active ($value eq '1');
503     $cb->signal_connect (clicked => sub {
504             my ($w, $e) = @_;
505             handle_bool_value ($w, $e, \$$vhash{$name});
506         });
507     set_widget_hint ($cb, $$hash{$key}[DESC]);
508     set_widget_sens ($cb, $$hash{$key}[CMVER]);
509     #
510     return new_hbox_spaced_pack ($cb);
511 }
512
513 sub new_text_box_for_int($$$) {
514     my ($hash, $key, $vhash) = @_;
515     my $name = $$hash{$key}[NAME];
516     my $label = $$hash{$key}[LABEL];
517     my @type = split (/,/, $$hash{$key}[TYPE]);
518     push (@type, 0), push (@type, 10000) unless ($#type > 0);
519     #
520     my $gunits = undef;
521     if (ref $label eq 'ARRAY') {
522         $gunits = Gtk2::Label->new ($label->[1]);
523         $label = $label->[0];
524     }
525     my $glabel = Gtk2::Label->new ($label);
526     my $pagei = int (($type[2] - $type[1]) / 10);
527     my $gentry = Gtk2::SpinButton->new_with_range ($type[1], $type[2], $pagei);
528     my $value = $$vhash{$name};
529     $value //= $$hash{$key}[CMDEF];
530     $gentry->set_numeric (TRUE);
531     $gentry->set_value ($value);
532     $gentry->signal_connect('value-changed' => sub {
533             my ($w, $e) = @_;
534             handle_int_value ($w, $e, \$$vhash{$name});
535         });
536     set_widget_hint ($gentry, $$hash{$key}[DESC]);
537     set_widget_sens ($gentry, $$hash{$key}[CMVER]);
538     $glabel->set_sensitive ($gentry->sensitive);
539     $gunits->set_sensitive ($gentry->sensitive) if ($gunits);
540     #
541     return new_hbox_spaced_pack ($glabel, $gentry, $gunits) if ($gunits);
542     return new_hbox_spaced_pack ($glabel, $gentry);
543 }
544
545 sub new_text_box_for_nchar($$$) {
546     my ($hash, $key, $vhash) = @_;
547     my $name = $$hash{$key}[NAME];
548     my $label = $$hash{$key}[LABEL];
549     my @type = split (/,/, $$hash{$key}[TYPE]); # char,minlen,maxlen,width
550     my $glabel = Gtk2::Label->new ($label);
551     my $gentry = Gtk2::Entry->new ();
552     $gentry->set_max_length($type[2]) if defined ($type[2]);
553     my $width = $type[3];
554     $width //= $type[2];
555     $gentry->set_width_chars(int ($width) + 2) if defined ($width);
556     my $value = $$vhash{$name};
557     $value //= $$hash{$key}[CMDEF];
558     $gentry->set_text ($value);
559     $gentry->signal_connect('key-release-event' => sub {
560             my ($w, $e) = @_;
561             handle_nchar_value ($w, $e, \$$vhash{$name}, $type[1], $type[2]);
562         });
563     set_widget_hint ($gentry, $$hash{$key}[DESC]);
564     set_widget_sens ($gentry, $$hash{$key}[CMVER]);
565     $glabel->set_sensitive ($gentry->sensitive);
566     #
567     return new_hbox_spaced_pack ($glabel, $gentry);
568 }
569
570 sub new_color_button_for($$$) {
571     my ($hash, $key, $vhash) = @_;
572     my $name = $$hash{$key}[NAME];
573     my $label = $$hash{$key}[LABEL];
574     #
575     my $value = $$vhash{$name};
576     $value //= $$hash{$key}[CMDEF];
577     my $col = gdk_color_from_str ($value);
578     my $glabel = Gtk2::Label->new ($label);
579     my $button = Gtk2::ColorButton->new_with_color ($col);
580     $button->set_title ($label);
581     $button->set_relief ('none');
582     $button->signal_connect ('color-set' => sub {
583             my ($w, $e) = @_;
584             handle_color_value ($w, $e, \$$vhash{$name});
585         });
586     set_widget_hint ($button, $$hash{$key}[DESC]);
587     set_widget_sens ($button, $$hash{$key}[CMVER]);
588     $glabel->set_sensitive ($button->sensitive);
589     #
590     return new_hbox_spaced_pack ($button, $glabel);
591 }
592
593 sub new_selection_box_for($$$) {
594     my ($hash, $key, $vhash) = @_;
595     my $name = $$hash{$key}[NAME];
596     my $label = $$hash{$key}[LABEL];
597     #
598     my $glabel = Gtk2::Label->new ($label);
599     my $combo = Gtk2::ComboBox->new_text;
600     my @options = split (';', $$hash{$key}[TYPE]);
601     foreach my $opt (@options) {
602         my ($index, $textkey) = split ('=', $opt);
603         $combo->insert_text ($index, $xl::s{$textkey});
604     }
605     $combo->signal_connect ('changed' => sub {
606             my ($w, $e) = @_;
607             handle_selection_value ($w, $e, \$$vhash{$name});
608         });
609     my $value = $$vhash{$name};
610     $value //= $$hash{$key}[CMDEF];
611     $combo->set_active ($value);
612     set_widget_hint ($combo, $$hash{$key}[DESC]);
613     set_widget_sens ($combo, $$hash{$key}[CMVER]);
614     $glabel->set_sensitive ($combo->sensitive);
615     #
616     return new_hbox_spaced_pack ($glabel, $combo);
617 }
618
619 # more graphic helpers
620
621 sub new_hbox_pack {
622     my $hbox = Gtk2::HBox->new (FALSE);
623     $hbox->set_border_width (PAGE_SPC);
624     foreach (@_) {
625         $hbox->pack_start ($_, FALSE, FALSE, 0);
626     }
627     return $hbox;
628 }
629
630 sub new_hbox_pack_compact {
631     my $hbox = Gtk2::HBox->new (FALSE);
632     $hbox->set_border_width (0);
633     foreach (@_) {
634         $hbox->pack_start ($_, FALSE, FALSE, 0);
635     }
636     return $hbox;
637 }
638
639 sub new_vbox_pack {
640     my $vbox = Gtk2::VBox->new (FALSE, 5);
641     $vbox->set_border_width (PAGE_SPC);
642     foreach (@_) {
643         $vbox->pack_start ($_, FALSE, FALSE, 0);
644     }
645     return $vbox;
646 }
647
648 sub new_vbox_pack_compact {
649     my $vbox = Gtk2::VBox->new (FALSE, 0);
650     $vbox->set_border_width (0);
651     foreach (@_) {
652         $vbox->pack_start ($_, FALSE, FALSE, 0);
653     }
654     return $vbox;
655 }
656
657 sub new_subpage_frame {
658     my ($box, $title, $notpacked) = @_;
659     my $frame = Gtk2::Frame->new ($title);
660     $frame->add ($box);
661     return new_vbox_pack ($frame) unless defined ($notpacked);
662     return $frame;
663 }
664
665 # preference maps and corresponding page creation subs
666
667 %pr::oth = ( # other preferences
668     use_dlg => [
669         'addressbook_use_editaddress_dialog',
670         $xl::s{l_oth_use_dlg},
671         $xl::s{h_oth_use_dlg},
672         'bool',
673         '2.7.0',
674         '0',
675     ],
676     max_use => [
677         'cache_max_mem_usage',
678         [ $xl::s{l_oth_max_use}, $xl::s{l_oth_max_use_units} ],
679         $xl::s{h_oth_max_use},
680         'int,0,262144', # 0 Kb - 256 Mb
681         '0.7.8.36',
682         '4096',
683     ],
684     min_time => [
685         'cache_min_keep_time',
686         [ $xl::s{l_oth_min_time}, $xl::s{l_oth_min_time_units} ],
687         $xl::s{h_oth_min_time},
688         'int,0,120', # 0 minutes - 2 hours
689         '0.7.8.36',
690         '15',
691     ],
692     use_netm => [
693         'use_networkmanager',
694         $xl::s{l_oth_use_netm},
695         $xl::s{h_oth_use_netm},
696         'bool',
697         '3.3.1',
698         '1',
699     ],
700     mp_rounds => [
701         'master_passphrase_pbkdf2_rounds',
702         $xl::s{l_oth_mp_rounds},
703         $xl::s{h_oth_mp_rounds},
704         'int,50000,1000000',
705         '3.13.2.110',
706         '50000',
707     ],
708 );
709
710 sub new_other_page() {
711     return new_vbox_pack (
712                new_subpage_frame (
713                    new_vbox_pack (
714                        new_check_button_for(\%pr::oth, 'use_dlg', \%HPVALUE)),
715                    $xl::s{ab_frame}, 'not-packed'),
716                new_subpage_frame (
717                    new_vbox_pack (
718                        new_text_box_for_int(\%pr::oth, 'max_use', \%HPVALUE),
719                        new_text_box_for_int(\%pr::oth, 'min_time', \%HPVALUE)),
720                    $xl::s{mem_frame}, 'not-packed'),
721                new_subpage_frame (
722                    new_vbox_pack (
723                        new_check_button_for(\%pr::oth, 'use_netm', \%HPVALUE)),
724                    $xl::s{netm_frame}, 'not-packed'),
725                new_subpage_frame (
726                    new_vbox_pack (
727                        new_text_box_for_int(\%pr::oth, 'mp_rounds', \%HPVALUE)),
728                    $xl::s{mpass_frame}, 'not-packed')
729            );
730 }
731
732 %pr::gui = ( # gui bells and whistles
733     b_unread => [
734         'bold_unread',
735         $xl::s{l_gui_b_unread},
736         $xl::s{h_gui_b_unread},
737         'bool',
738         '0.5.3',
739         '1',
740     ],
741     no_markup => [
742         'compose_no_markup',
743         $xl::s{l_gui_no_markup},
744         $xl::s{h_gui_no_markup},
745         'bool',
746         '2.1.0.16',
747         '0',
748     ],
749     dot_lines => [
750         'enable_dotted_lines',
751         $xl::s{l_gui_dot_lines},
752         $xl::s{h_gui_dot_lines},
753         'bool',
754         '2.4.0.115,3.7.10.44',
755         '0',
756     ],
757     h_scroll => [
758         'enable_hscrollbar',
759         $xl::s{l_gui_h_scroll},
760         $xl::s{h_gui_h_scroll},
761         'bool',
762         '0.8.6.18',
763         '1',
764     ],
765     swp_from => [
766         'enable_swap_from',
767         $xl::s{l_gui_swp_from},
768         $xl::s{h_gui_swp_from},
769         'bool',
770         '1.9.13.40',
771         '0',
772     ],
773     v_scroll => [
774         'folderview_vscrollbar_policy',
775         $xl::s{l_gui_v_scroll},
776         $xl::s{h_gui_v_scroll},
777         '0=l_gui_v_scroll_show;1=l_gui_v_scroll_auto;2=l_gui_v_scroll_hide',
778         '0.7.8.14',
779         '0',
780     ],
781     from_show => [
782         'summary_from_show',
783         $xl::s{l_gui_from_show},
784         $xl::s{h_gui_from_show},
785         '0=l_gui_from_show_name;1=l_gui_from_show_addr;2=l_gui_from_show_both',
786         '3.7.10',
787         '0',
788     ],
789     strip_off => [
790         'stripes_color_offset',
791         $xl::s{l_gui_strip_off},
792         $xl::s{h_gui_strip_off},
793         'int,0,40000', # no idea what this number means
794         '2.4.0.186',
795         '4000',
796     ],
797     cursor_v => [
798         'textview_cursor_visible',
799         $xl::s{l_gui_cursor_v},
800         $xl::s{h_gui_cursor_v},
801         'bool',
802         '0.0.0',
803         '0',
804     ],
805     toolbar_d => [
806         'toolbar_detachable',
807         $xl::s{l_gui_toolbar_d},
808         $xl::s{h_gui_toolbar_d},
809         'bool',
810         '0.0.0',
811         '0',
812     ],
813     strip_all => [
814         'use_stripes_everywhere',
815         $xl::s{l_gui_strip_all},
816         $xl::s{h_gui_strip_all},
817         'bool',
818         '0.0.0',
819         '1',
820     ],
821     strip_sum => [
822         'use_stripes_in_summaries',
823         $xl::s{l_gui_strip_sum},
824         $xl::s{h_gui_strip_sum},
825         'bool',
826         '0.0.0',
827         '1',
828     ],
829     two_linev => [
830         'two_line_vertical',
831         $xl::s{l_gui_two_line_v},
832         $xl::s{h_gui_two_line_v},
833         'bool',
834         '3.4.0.7',
835         '0',
836     ],
837     margin_co => [
838         'show_compose_margin',
839         $xl::s{l_gui_margin_co},
840         $xl::s{h_gui_margin_co},
841         'bool',
842         '3.7.6.7',
843         '0',
844     ],
845     mview_date => [
846         'msgview_date_format',
847         $xl::s{l_gui_mview_date},
848         $xl::s{h_gui_mview_date},
849         'bool',
850         '3.7.8.42',
851         '0',
852     ],
853     zero_char => [
854         'zero_replacement_char',
855         $xl::s{l_gui_zero_char},
856         $xl::s{h_gui_zero_char},
857         'char,1,1',
858         '2.8.1.77',
859         '0',
860     ],
861     type_any => [
862         'type_any_header',
863         $xl::s{l_gui_type_any},
864         $xl::s{h_gui_type_any},
865         'bool',
866         '3.12.0.44',
867         '0',
868     ],
869     warn_send_multi => [
870         'warn_sending_many_recipients_num',
871         [ $xl::s{l_gui_warn_send_multi}, $xl::s{l_gui_warn_send_multi_units} ],
872         $xl::s{h_gui_warn_send_multi},
873         'int,0,1000',
874         '3.14.1.125',
875         '0',
876     ],
877     next_del => [
878         'next_on_delete',
879         $xl::s{l_gui_next_del},
880         $xl::s{h_gui_next_del},
881         'bool',
882         '3.13.0.5',
883         '0',
884     ],
885 );
886
887 sub new_gui_page() {
888     my $gf = Gtk2::VBox->new (FALSE, 5);
889     $gf->set_border_width (PAGE_SPC);
890
891     my $cb_dot_lines = new_check_button_for (\%pr::gui, 'dot_lines', \%HPVALUE);
892     my $cb_toolbar_d = new_check_button_for (\%pr::gui, 'toolbar_d', \%HPVALUE);
893     my $tb_zero_char = new_text_box_for_nchar (\%pr::gui, 'zero_char', \%HPVALUE);
894
895     $gf->pack_start (new_subpage_frame (
896                          new_vbox_pack (
897                              new_check_button_for (\%pr::gui, 'strip_all', \%HPVALUE),
898                              new_check_button_for (\%pr::gui, 'strip_sum', \%HPVALUE),
899                              new_text_box_for_int (\%pr::gui, 'strip_off', \%HPVALUE)),
900                          $xl::s{stripes_frame}, 'not-packed'),
901                      FALSE, FALSE, FRAME_SPC);
902     $gf->pack_start (new_subpage_frame (
903                          new_vbox_pack (
904                              new_check_button_for (\%pr::gui, 'b_unread', \%HPVALUE),
905                              new_check_button_for (\%pr::gui, 'swp_from', \%HPVALUE),
906                              new_check_button_for (\%pr::gui, 'two_linev', \%HPVALUE),
907                              new_check_button_for (\%pr::gui, 'next_del', \%HPVALUE),
908                              new_selection_box_for (\%pr::gui, 'from_show', \%HPVALUE)),
909                          $xl::s{mlist_frame}, 'not-packed'),
910                      FALSE, FALSE, FRAME_SPC);
911     $gf->pack_start (new_subpage_frame (
912                          new_hbox_pack (
913                              new_check_button_for (\%pr::gui, 'cursor_v', \%HPVALUE),
914                              new_check_button_for (\%pr::gui, 'mview_date', \%HPVALUE)),
915                          $xl::s{mview_frame}, 'not-packed'),
916                      FALSE, FALSE, FRAME_SPC);
917     $gf->pack_start (new_subpage_frame (
918                          new_vbox_pack (
919                              new_hbox_pack_compact (
920                                  new_check_button_for (\%pr::gui, 'no_markup', \%HPVALUE),
921                                  new_check_button_for (\%pr::gui, 'margin_co', \%HPVALUE),
922                                  new_check_button_for (\%pr::gui, 'type_any', \%HPVALUE)),
923                              new_text_box_for_int (\%pr::gui, 'warn_send_multi', \%HPVALUE)),
924                          $xl::s{compo_frame}, 'not-packed'),
925                      FALSE, FALSE, FRAME_SPC);
926     $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
927     $gf->pack_start ($cb_toolbar_d, FALSE, FALSE, 0);
928     $gf->pack_start (new_subpage_frame (
929                          new_vbox_pack (
930                              new_check_button_for (\%pr::gui, 'h_scroll', \%HPVALUE),
931                              new_selection_box_for (\%pr::gui, 'v_scroll', \%HPVALUE)),
932                          $xl::s{sbar_frame}, 'not-packed'),
933                      FALSE, FALSE, FRAME_SPC);
934     $gf->pack_start ($tb_zero_char, FALSE, FALSE, 0);
935
936     return $gf;
937 }
938
939 %pr::beh = ( # tweak some behaviour
940     hover_t => [
941         'hover_timeout',
942         [ $xl::s{l_beh_hover_t}, $xl::s{l_beh_hover_t_units} ],
943         $xl::s{h_beh_hover_t},
944         'int,100,3000', # 0.1 seconds - 3 seconds
945         '0.0.0',
946         '500',
947     ],
948     dangerous => [
949         'live_dangerously',
950         $xl::s{l_beh_dangerous},
951         $xl::s{h_beh_dangerous},
952         'bool',
953         '0.0.0',
954         '0',
955     ],
956     flowed => [
957         'respect_flowed_format',
958         $xl::s{l_beh_flowed},
959         $xl::s{h_beh_flowed},
960         'bool',
961         '0.0.0',
962         '0',
963     ],
964     parts_rw => [
965         'save_parts_readwrite',
966         $xl::s{l_beh_parts_rw},
967         $xl::s{h_beh_parts_rw},
968         'bool',
969         '0.0.0',
970         '0',
971     ],
972     skip_ssl => [
973         'skip_ssl_cert_check',
974         $xl::s{l_beh_skip_ssl},
975         $xl::s{h_beh_skip_ssl},
976         'bool',
977         '0.0.0',
978         '0',
979     ],
980     up_step => [
981         'statusbar_update_step',
982         [ $xl::s{l_beh_up_step}, $xl::s{l_beh_up_step_units} ],
983         $xl::s{h_beh_up_step},
984         'int,1,200', # 1 item - 200 items
985         '0.0.0',
986         '10',
987     ],
988     thread_a => [
989         'thread_by_subject_max_age',
990         [ $xl::s{l_beh_thread_a}, $xl::s{l_beh_thread_a_units} ],
991         $xl::s{h_beh_thread_a},
992         'int,1,30', # 1 day - 30 days
993         '0.0.0',
994         '10',
995     ],
996     unsafe_ssl => [
997         'unsafe_ssl_certs',
998         $xl::s{l_beh_unsafe_ssl},
999         $xl::s{h_beh_unsafe_ssl},
1000         'bool',
1001         '0.0.0',
1002         '0',
1003     ],
1004     use_utf8 => [
1005         'utf8_instead_of_locale_for_broken_mail',
1006         $xl::s{l_beh_use_utf8},
1007         $xl::s{h_beh_use_utf8},
1008         'bool',
1009         '1.9.14.49',
1010         '0',
1011     ],
1012     warn_dnd => [
1013         'warn_dnd',
1014         $xl::s{l_beh_warn_dnd},
1015         $xl::s{h_beh_warn_dnd},
1016         'bool',
1017         '0.0.0',
1018         '1',
1019     ],
1020     out_ascii => [
1021         'outgoing_fallback_to_ascii',
1022         $xl::s{l_beh_out_ascii},
1023         $xl::s{h_beh_out_ascii},
1024         'bool',
1025         '3.4.0.37',
1026         '1',
1027     ],
1028     pp_unsel => [
1029         'primary_paste_unselects',
1030         $xl::s{l_beh_pp_unsel},
1031         $xl::s{h_beh_pp_unsel},
1032         'bool',
1033         '3.6.1.35',
1034         '0',
1035     ],
1036     inline_at => [
1037         'show_inline_attachments',
1038         $xl::s{l_beh_inline_at},
1039         $xl::s{h_beh_inline_at},
1040         'bool',
1041         '3.7.8.48',
1042         '1',
1043     ],
1044     addr_swc => [
1045         'address_search_wildcard',
1046         $xl::s{l_beh_addr_swc},
1047         $xl::s{h_beh_addr_swc},
1048         'bool',
1049         '3.9.3.18',
1050         '0',
1051     ],
1052     fold_swc => [
1053         'folder_search_wildcard',
1054         $xl::s{l_beh_fold_swc},
1055         $xl::s{h_beh_fold_swc},
1056         'bool',
1057         '3.9.3.18',
1058         '0',
1059     ],
1060     rewrite_ff => [
1061         'rewrite_first_from',
1062         $xl::s{l_beh_rewrite_ff},
1063         $xl::s{h_beh_rewrite_ff},
1064         'bool',
1065         '3.14.0.94',
1066         '0',
1067     ],
1068 );
1069
1070 sub new_behaviour_page() {
1071     my $bf = Gtk2::VBox->new (FALSE, 5);
1072     $bf->set_border_width (PAGE_SPC);
1073
1074     my $tb_up_step = new_text_box_for_int (\%pr::beh, 'up_step', \%HPVALUE);
1075     my $tb_thread_a = new_text_box_for_int (\%pr::beh, 'thread_a', \%HPVALUE);
1076
1077     $bf->pack_start (new_subpage_frame (
1078                          new_vbox_pack (
1079                              new_text_box_for_int (\%pr::beh, 'hover_t', \%HPVALUE),
1080                              new_check_button_for (\%pr::beh, 'warn_dnd', \%HPVALUE)),
1081                          $xl::s{dnd_frame}, 'not-packed'),
1082                      FALSE, FALSE, FRAME_SPC);
1083     $bf->pack_start (new_subpage_frame (
1084                          new_hbox_pack (
1085                              new_check_button_for (\%pr::beh, 'skip_ssl', \%HPVALUE),
1086                              new_check_button_for (\%pr::beh, 'unsafe_ssl', \%HPVALUE)),
1087                          $xl::s{ssl_frame}, 'not-packed'),
1088                      FALSE, FALSE, FRAME_SPC);
1089     $bf->pack_start ($tb_up_step, FALSE, FALSE, 0);
1090     $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0);
1091     $bf->pack_start (new_subpage_frame (
1092                          new_vbox_pack (
1093                              new_check_button_for (\%pr::beh, 'flowed', \%HPVALUE),
1094                              new_check_button_for (\%pr::beh, 'parts_rw', \%HPVALUE),
1095                              new_check_button_for (\%pr::beh, 'use_utf8', \%HPVALUE),
1096                              new_check_button_for (\%pr::beh, 'dangerous', \%HPVALUE),
1097                              new_check_button_for (\%pr::beh, 'out_ascii', \%HPVALUE),
1098                              new_check_button_for (\%pr::beh, 'pp_unsel', \%HPVALUE),
1099                              new_check_button_for (\%pr::beh, 'inline_at', \%HPVALUE),
1100                              new_check_button_for (\%pr::beh, 'rewrite_ff', \%HPVALUE)),
1101                          $xl::s{msgs_frame}, 'not-packed'),
1102                      FALSE, FALSE, FRAME_SPC);
1103     $bf->pack_start (new_subpage_frame (
1104                          new_vbox_pack (
1105                              new_check_button_for (\%pr::beh, 'addr_swc', \%HPVALUE),
1106                              new_check_button_for (\%pr::beh, 'fold_swc', \%HPVALUE)),
1107                          $xl::s{swc_frame}, 'not-packed'),
1108                      FALSE, FALSE, FRAME_SPC);
1109     return $bf;
1110 }
1111
1112 %pr::col = ( # a variety of colours
1113     emphasis => [
1114         'emphasis_color',
1115         $xl::s{l_col_emphasis},
1116         $xl::s{h_col_emphasis},
1117         'color',
1118         '0.0.0',
1119         '#0000cf',
1120     ],
1121     log_err => [
1122         'log_error_color',
1123         $xl::s{l_col_log_err},
1124         $xl::s{h_col_log_err},
1125         'color',
1126         '0.0.0',
1127         '#af0000',
1128     ],
1129     log_in => [
1130         'log_in_color',
1131         $xl::s{l_col_log_in},
1132         $xl::s{h_col_log_in},
1133         'color',
1134         '0.0.0',
1135         '#000000',
1136     ],
1137     log_msg => [
1138         'log_msg_color',
1139         $xl::s{l_col_log_msg},
1140         $xl::s{h_col_log_msg},
1141         'color',
1142         '0.0.0',
1143         '#00af00',
1144     ],
1145     log_out => [
1146         'log_out_color',
1147         $xl::s{l_col_log_out},
1148         $xl::s{h_col_log_out},
1149         'color',
1150         '0.0.0',
1151         '#0000ef',
1152     ],
1153     log_warn => [
1154         'log_warn_color',
1155         $xl::s{l_col_log_warn},
1156         $xl::s{h_col_log_warn},
1157         'color',
1158         '0.0.0',
1159         '#af0000',
1160     ],
1161     diff_add => [
1162         'diff_added_color',
1163         $xl::s{l_col_diff_add},
1164         $xl::s{h_col_diff_add},
1165         'color',
1166         '3.8.0.54',
1167         '#008b8b',
1168     ],
1169     diff_del => [
1170         'diff_deleted_color',
1171         $xl::s{l_col_diff_del},
1172         $xl::s{h_col_diff_del},
1173         'color',
1174         '3.8.0.54',
1175         '#6a5acd',
1176     ],
1177     diff_hunk => [
1178         'diff_hunk_color',
1179         $xl::s{l_col_diff_hunk},
1180         $xl::s{h_col_diff_hunk},
1181         'color',
1182         '3.8.0.54',
1183         '#a52a2a',
1184     ],
1185     tags_bg => [
1186         'tags_bgcolor',
1187         $xl::s{l_col_tags_bg},
1188         $xl::s{h_col_tags_bg},
1189         'color',
1190         '3.14.1.31',
1191         '#f5f6be',
1192     ],
1193     tags_text => [
1194         'tags_color',
1195         $xl::s{l_col_tags_text},
1196         $xl::s{h_col_tags_text},
1197         'color',
1198         '3.14.1.31',
1199         '#000000',
1200     ],
1201     default_header_bg => [
1202         'default_header_bgcolor',
1203         $xl::s{l_col_default_header_bg},
1204         $xl::s{h_col_default_header_bg},
1205         'color',
1206         '3.14.1.31',
1207         '#f5f6be',
1208     ],
1209     default_header_text => [
1210         'default_header_color',
1211         $xl::s{l_col_default_header_text},
1212         $xl::s{h_col_default_header_text},
1213         'color',
1214         '3.14.1.31',
1215         '#000000',
1216     ],
1217     qs_active_bg => [
1218         'qs_active_bgcolor',
1219         $xl::s{l_col_qs_active_bg},
1220         $xl::s{h_col_qs_active_bg},
1221         'color',
1222         '3.14.1.31',
1223         '#f5f6be',
1224     ],
1225     qs_active_text => [
1226         'qs_active_color',
1227         $xl::s{l_col_qs_active_text},
1228         $xl::s{h_col_qs_active_text},
1229         'color',
1230         '3.14.1.31',
1231         '#000000',
1232     ],
1233     qs_error_bg => [
1234         '',
1235         $xl::s{l_col_qs_error_bg},
1236         $xl::s{h_col_qs_error_bg},
1237         'qs_error_bgcolor',
1238         '3.14.1.31',
1239         '#ff7070',
1240     ],
1241     qs_error_text => [
1242         '',
1243         $xl::s{l_col_qs_error_text},
1244         $xl::s{h_col_qs_error_text},
1245         'qs_error_color',
1246         '3.14.1.31',
1247         '#000000',
1248     ],
1249 );
1250
1251 sub new_colours_page() {
1252     return new_vbox_pack (
1253                new_subpage_frame (
1254                    new_vbox_pack (
1255                        new_color_button_for (\%pr::col, 'emphasis', \%HPVALUE),
1256                        new_hbox_pack_compact (
1257                            new_color_button_for (\%pr::col, 'tags_text', \%HPVALUE),
1258                            new_color_button_for (\%pr::col, 'tags_bg', \%HPVALUE))),
1259                    $xl::s{msgview_frame}, 'not-packed'),
1260                new_subpage_frame (
1261                    new_hbox_pack (
1262                        new_vbox_pack_compact (
1263                            new_color_button_for (\%pr::col, 'log_err', \%HPVALUE),
1264                            new_color_button_for (\%pr::col, 'log_warn', \%HPVALUE),
1265                            new_color_button_for (\%pr::col, 'log_msg', \%HPVALUE)),
1266                        new_vbox_pack_compact (
1267                            new_color_button_for (\%pr::col, 'log_in', \%HPVALUE),
1268                            new_color_button_for (\%pr::col, 'log_out', \%HPVALUE))),
1269                    $xl::s{log_frame}, 'not-packed'),
1270                new_subpage_frame (
1271                    new_vbox_pack (
1272                        new_color_button_for (\%pr::col, 'diff_add', \%HPVALUE),
1273                        new_color_button_for (\%pr::col, 'diff_del', \%HPVALUE),
1274                        new_color_button_for (\%pr::col, 'diff_hunk', \%HPVALUE)),
1275                    $xl::s{diff_frame}, 'not-packed'),
1276                new_subpage_frame (
1277                    new_hbox_pack (
1278                        new_color_button_for (\%pr::col, 'default_header_text', \%HPVALUE),
1279                        new_color_button_for (\%pr::col, 'default_header_bg', \%HPVALUE)),
1280                    $xl::s{compose_frame}, 'not-packed'),
1281                new_subpage_frame (
1282                    new_hbox_pack (
1283                        new_vbox_pack_compact (
1284                            new_color_button_for (\%pr::col, 'qs_active_text', \%HPVALUE),
1285                            new_color_button_for (\%pr::col, 'qs_error_text', \%HPVALUE)),
1286                        new_vbox_pack_compact (
1287                            new_color_button_for (\%pr::col, 'qs_active_bg', \%HPVALUE),
1288                            new_color_button_for (\%pr::col, 'qs_error_bg', \%HPVALUE))),
1289                    $xl::s{qs_frame}, 'not-packed')
1290            );
1291 }
1292
1293 %pr::win = ( # tweak window positions and/or sizes
1294     main_x => [
1295         'mainwin_x',
1296         $xl::s{l_win_x},
1297         $xl::s{h_win_x},
1298         'int,0,3000', # 0 pixels - 3000 pixels
1299         '0.0.0',
1300         '16',
1301     ],
1302     main_y => [
1303         'mainwin_y',
1304         $xl::s{l_win_y},
1305         $xl::s{h_win_y},
1306         'int,0,3000', # 0 pixels - 3000 pixels
1307         '0.0.0',
1308         '16',
1309     ],
1310     main_w => [
1311         'mainwin_width',
1312         $xl::s{l_win_w},
1313         $xl::s{h_win_w},
1314         'int,0,3000', # 0 pixels - 3000 pixels
1315         '0.0.0',
1316         '779',
1317     ],
1318     main_h => [
1319         'mainwin_height',
1320         $xl::s{l_win_h},
1321         $xl::s{h_win_h},
1322         'int,0,3000', # 0 pixels - 3000 pixels
1323         '0.0.0',
1324         '568',
1325     ],
1326     main_mx => [
1327         'mainwin_maximised',
1328         $xl::s{l_win_main_mx},
1329         $xl::s{h_win_main_mx},
1330         'bool',
1331         '0.0.0',
1332         '0',
1333     ],
1334     main_fs => [
1335         'mainwin_fullscreen',
1336         $xl::s{l_win_main_fs},
1337         $xl::s{h_win_main_fs},
1338         'bool',
1339         '0.0.0',
1340         '0',
1341     ],
1342     msgs_x => [
1343         'main_messagewin_x',
1344         $xl::s{l_win_x},
1345         $xl::s{h_win_x},
1346         'int,0,3000', # 0 pixels - 3000 pixels
1347         '0.0.0',
1348         '256',
1349     ],
1350     msgs_y => [
1351         'main_messagewin_y',
1352         $xl::s{l_win_y},
1353         $xl::s{h_win_y},
1354         'int,0,3000', # 0 pixels - 3000 pixels
1355         '0.0.0',
1356         '210',
1357     ],
1358     msgs_w => [
1359         'messagewin_width',
1360         $xl::s{l_win_w},
1361         $xl::s{h_win_w},
1362         'int,0,3000', # 0 pixels - 3000 pixels
1363         '0.0.0',
1364         '600',
1365     ],
1366     msgs_h => [
1367         'messagewin_height',
1368         $xl::s{l_win_h},
1369         $xl::s{h_win_h},
1370         'int,0,3000', # 0 pixels - 3000 pixels
1371         '0.0.0',
1372         '540',
1373     ],
1374     send_w => [
1375         'sendwin_width',
1376         $xl::s{l_win_w},
1377         $xl::s{h_win_w},
1378         'int,0,3000', # 0 pixels - 3000 pixels
1379         '0.0.0',
1380         '460',
1381     ],
1382     send_h => [
1383         'sendwin_height',
1384         $xl::s{l_win_h},
1385         $xl::s{h_win_h},
1386         'int,0,3000', # 0 pixels - 3000 pixels
1387         '0.0.0',
1388         '-1',
1389     ],
1390     recv_w => [
1391         'receivewin_width',
1392         $xl::s{l_win_w},
1393         $xl::s{h_win_w},
1394         'int,0,3000', # 0 pixels - 3000 pixels
1395         '0.0.0',
1396         '460',
1397     ],
1398     recv_h => [
1399         'receivewin_height',
1400         $xl::s{l_win_h},
1401         $xl::s{h_win_h},
1402         'int,0,3000', # 0 pixels - 3000 pixels
1403         '0.0.0',
1404         '-1',
1405     ],
1406     fold_x => [
1407         'folderwin_x',
1408         $xl::s{l_win_x},
1409         $xl::s{h_win_x},
1410         'int,0,3000', # 0 pixels - 3000 pixels
1411         '0.0.0',
1412         '16',
1413     ],
1414     fold_y => [
1415         'folderwin_y',
1416         $xl::s{l_win_y},
1417         $xl::s{h_win_y},
1418         'int,0,3000', # 0 pixels - 3000 pixels
1419         '0.0.0',
1420         '16',
1421     ],
1422     fold_w => [
1423         'folderitemwin_width',
1424         $xl::s{l_win_w},
1425         $xl::s{h_win_w},
1426         'int,0,3000', # 0 pixels - 3000 pixels
1427         '0.0.0',
1428         '500',
1429     ],
1430     fold_h => [
1431         'folderitemwin_height',
1432         $xl::s{l_win_h},
1433         $xl::s{h_win_h},
1434         'int,0,3000', # 0 pixels - 3000 pixels
1435         '0.0.0',
1436         '-1',
1437     ],
1438     fsel_w => [
1439         'folderselwin_width',
1440         $xl::s{l_win_w},
1441         $xl::s{h_win_w},
1442         'int,0,3000', # 0 pixels - 3000 pixels
1443         '0.0.0',
1444         '300',
1445     ],
1446     fsel_h => [
1447         'folderselwin_height',
1448         $xl::s{l_win_h},
1449         $xl::s{h_win_h},
1450         'int,0,3000', # 0 pixels - 3000 pixels
1451         '0.0.0',
1452         '-1',
1453     ],
1454     sour_w => [
1455         'sourcewin_width',
1456         $xl::s{l_win_w},
1457         $xl::s{h_win_w},
1458         'int,0,3000', # 0 pixels - 3000 pixels
1459         '0.0.0',
1460         '600',
1461     ],
1462     sour_h => [
1463         'sourcewin_height',
1464         $xl::s{l_win_h},
1465         $xl::s{h_win_h},
1466         'int,0,3000', # 0 pixels - 3000 pixels
1467         '0.0.0',
1468         '500',
1469     ],
1470     addr_w => [
1471         'addressbookwin_width',
1472         $xl::s{l_win_w},
1473         $xl::s{h_win_w},
1474         'int,0,3000', # 0 pixels - 3000 pixels
1475         '0.0.0',
1476         '520',
1477     ],
1478     addr_h => [
1479         'addressbookwin_height',
1480         $xl::s{l_win_h},
1481         $xl::s{h_win_h},
1482         'int,0,3000', # 0 pixels - 3000 pixels
1483         '0.0.0',
1484         '-1',
1485     ],
1486     adep_w => [
1487         'addressbookeditpersonwin_width',
1488         $xl::s{l_win_w},
1489         $xl::s{h_win_w},
1490         'int,0,3000', # 0 pixels - 3000 pixels
1491         '0.0.0',
1492         '640',
1493     ],
1494     adep_h => [
1495         'addressbookeditpersonwin_height',
1496         $xl::s{l_win_h},
1497         $xl::s{h_win_h},
1498         'int,0,3000', # 0 pixels - 3000 pixels
1499         '0.0.0',
1500         '320',
1501     ],
1502     adeg_w => [
1503         'addressbookeditgroupwin_width',
1504         $xl::s{l_win_w},
1505         $xl::s{h_win_w},
1506         'int,0,3000', # 0 pixels - 3000 pixels
1507         '0.0.0',
1508         '580',
1509     ],
1510     adeg_h => [
1511         'addressbookeditgroupwin_height',
1512         $xl::s{l_win_h},
1513         $xl::s{h_win_h},
1514         'int,0,3000', # 0 pixels - 3000 pixels
1515         '0.0.0',
1516         '340',
1517     ],
1518     adda_w => [
1519         'addressaddwin_width',
1520         $xl::s{l_win_w},
1521         $xl::s{h_win_w},
1522         'int,0,3000', # 0 pixels - 3000 pixels
1523         '0.0.0',
1524         '300',
1525     ],
1526     adda_h => [
1527         'addressaddwin_height',
1528         $xl::s{l_win_h},
1529         $xl::s{h_win_h},
1530         'int,0,3000', # 0 pixels - 3000 pixels
1531         '0.0.0',
1532         '-1',
1533     ],
1534     addf_w => [
1535         'addressbook_folderselwin_width',
1536         $xl::s{l_win_w},
1537         $xl::s{h_win_w},
1538         'int,0,3000', # 0 pixels - 3000 pixels
1539         '0.0.0',
1540         '300',
1541     ],
1542     addf_h => [
1543         'addressbook_folderselwin_height',
1544         $xl::s{l_win_h},
1545         $xl::s{h_win_h},
1546         'int,0,3000', # 0 pixels - 3000 pixels
1547         '0.0.0',
1548         '-1',
1549     ],
1550     acce_w => [
1551         'editaccountwin_width',
1552         $xl::s{l_win_w},
1553         $xl::s{h_win_w},
1554         'int,0,3000', # 0 pixels - 3000 pixels
1555         '0.0.0',
1556         '500',
1557     ],
1558     acce_h => [
1559         'editaccountwin_height',
1560         $xl::s{l_win_h},
1561         $xl::s{h_win_h},
1562         'int,0,3000', # 0 pixels - 3000 pixels
1563         '0.0.0',
1564         '-1',
1565     ],
1566     acco_w => [
1567         'accountswin_width',
1568         $xl::s{l_win_w},
1569         $xl::s{h_win_w},
1570         'int,0,3000', # 0 pixels - 3000 pixels
1571         '0.0.0',
1572         '500',
1573     ],
1574     acco_h => [
1575         'accountswin_height',
1576         $xl::s{l_win_h},
1577         $xl::s{h_win_h},
1578         'int,0,3000', # 0 pixels - 3000 pixels
1579         '0.0.0',
1580         '-1',
1581     ],
1582     filt_w => [
1583         'filteringwin_width',
1584         $xl::s{l_win_w},
1585         $xl::s{h_win_w},
1586         'int,0,3000', # 0 pixels - 3000 pixels
1587         '0.0.0',
1588         '500',
1589     ],
1590     filt_h => [
1591         'filteringwin_height',
1592         $xl::s{l_win_h},
1593         $xl::s{h_win_h},
1594         'int,0,3000', # 0 pixels - 3000 pixels
1595         '0.0.0',
1596         '-1',
1597     ],
1598     fila_w => [
1599         'filteringactionwin_width',
1600         $xl::s{l_win_w},
1601         $xl::s{h_win_w},
1602         'int,0,3000', # 0 pixels - 3000 pixels
1603         '0.0.0',
1604         '490',
1605     ],
1606     fila_h => [
1607         'filteringactionwin_height',
1608         $xl::s{l_win_h},
1609         $xl::s{h_win_h},
1610         'int,0,3000', # 0 pixels - 3000 pixels
1611         '0.0.0',
1612         '-1',
1613     ],
1614     fild_w => [
1615         'filtering_debugwin_width',
1616         $xl::s{l_win_w},
1617         $xl::s{h_win_w},
1618         'int,0,3000', # 0 pixels - 3000 pixels
1619         '0.0.0',
1620         '600',
1621     ],
1622     fild_h => [
1623         'filtering_debugwin_height',
1624         $xl::s{l_win_h},
1625         $xl::s{h_win_h},
1626         'int,0,3000', # 0 pixels - 3000 pixels
1627         '0.0.0',
1628         '-1',
1629     ],
1630     matc_w => [
1631         'matcherwin_width',
1632         $xl::s{l_win_w},
1633         $xl::s{h_win_w},
1634         'int,0,3000', # 0 pixels - 3000 pixels
1635         '0.0.0',
1636         '520',
1637     ],
1638     matc_h => [
1639         'matcherwin_height',
1640         $xl::s{l_win_h},
1641         $xl::s{h_win_h},
1642         'int,0,3000', # 0 pixels - 3000 pixels
1643         '0.0.0',
1644         '-1',
1645     ],
1646     pref_w => [
1647         'prefswin_width',
1648         $xl::s{l_win_w},
1649         $xl::s{h_win_w},
1650         'int,0,3000', # 0 pixels - 3000 pixels
1651         '0.0.0',
1652         '600',
1653     ],
1654     pref_h => [
1655         'prefswin_height',
1656         $xl::s{l_win_h},
1657         $xl::s{h_win_h},
1658         'int,0,3000', # 0 pixels - 3000 pixels
1659         '0.0.0',
1660         '-1',
1661     ],
1662     temp_w => [
1663         'templateswin_width',
1664         $xl::s{l_win_w},
1665         $xl::s{h_win_w},
1666         'int,0,3000', # 0 pixels - 3000 pixels
1667         '0.0.0',
1668         '480',
1669     ],
1670     temp_h => [
1671         'templateswin_height',
1672         $xl::s{l_win_h},
1673         $xl::s{h_win_h},
1674         'int,0,3000', # 0 pixels - 3000 pixels
1675         '0.0.0',
1676         '-1',
1677     ],
1678     acti_w => [
1679         'actionswin_width',
1680         $xl::s{l_win_w},
1681         $xl::s{h_win_w},
1682         'int,0,3000', # 0 pixels - 3000 pixels
1683         '0.0.0',
1684         '486',
1685     ],
1686     acti_h => [
1687         'actionswin_height',
1688         $xl::s{l_win_h},
1689         $xl::s{h_win_h},
1690         'int,0,3000', # 0 pixels - 3000 pixels
1691         '0.0.0',
1692         '-1',
1693     ],
1694     tags_w => [
1695         'tagswin_width',
1696         $xl::s{l_win_w},
1697         $xl::s{h_win_w},
1698         'int,0,3000', # 0 pixels - 3000 pixels
1699         '0.0.0',
1700         '486',
1701     ],
1702     tags_h => [
1703         'tagswin_height',
1704         $xl::s{l_win_h},
1705         $xl::s{h_win_h},
1706         'int,0,3000', # 0 pixels - 3000 pixels
1707         '0.0.0',
1708         '-1',
1709     ],
1710     plug_w => [
1711         'pluginswin_width',
1712         $xl::s{l_win_w},
1713         $xl::s{h_win_w},
1714         'int,0,3000', # 0 pixels - 3000 pixels
1715         '0.0.0',
1716         '-1',
1717     ],
1718     plug_h => [
1719         'pluginswin_height',
1720         $xl::s{l_win_h},
1721         $xl::s{h_win_h},
1722         'int,0,3000', # 0 pixels - 3000 pixels
1723         '0.0.0',
1724         '-1',
1725     ],
1726     logw_w => [
1727         'logwin_width',
1728         $xl::s{l_win_w},
1729         $xl::s{h_win_w},
1730         'int,0,3000', # 0 pixels - 3000 pixels
1731         '0.0.0',
1732         '520',
1733     ],
1734     logw_h => [
1735         'logwin_height',
1736         $xl::s{l_win_h},
1737         $xl::s{h_win_h},
1738         'int,0,3000', # 0 pixels - 3000 pixels
1739         '0.0.0',
1740         '-1',
1741     ],
1742     prin_w => [
1743         'print_previewwin_width',
1744         $xl::s{l_win_w},
1745         $xl::s{h_win_w},
1746         'int,0,3000', # 0 pixels - 3000 pixels
1747         '0.0.0',
1748         '600',
1749     ],
1750     prin_h => [
1751         'print_previewwin_height',
1752         $xl::s{l_win_h},
1753         $xl::s{h_win_h},
1754         'int,0,3000', # 0 pixels - 3000 pixels
1755         '0.0.0',
1756         '-1',
1757     ],
1758 );
1759
1760 sub new_winpos_subpage_main() {
1761     return new_subpage_frame (
1762                new_vbox_pack_compact (
1763                    new_vbox_pack (
1764                        new_text_box_for_int (\%pr::win, 'main_x', \%HPVALUE),
1765                        new_text_box_for_int (\%pr::win, 'main_y', \%HPVALUE)),
1766                    new_hbox_pack (
1767                        new_text_box_for_int (\%pr::win, 'main_w', \%HPVALUE),
1768                        new_text_box_for_int (\%pr::win, 'main_h', \%HPVALUE)),
1769                    new_vbox_pack (
1770                        new_check_button_for (\%pr::win, 'main_fs', \%HPVALUE),
1771                        new_check_button_for (\%pr::win, 'main_mx', \%HPVALUE))),
1772                _('Main window')
1773            );
1774 }
1775
1776 sub new_winpos_subpage_msgs() {
1777     return new_subpage_frame (
1778                new_vbox_pack_compact (
1779                    new_vbox_pack (
1780                        new_text_box_for_int (\%pr::win, 'msgs_x', \%HPVALUE),
1781                        new_text_box_for_int (\%pr::win, 'msgs_y', \%HPVALUE)),
1782                    new_hbox_pack (
1783                        new_text_box_for_int (\%pr::win, 'msgs_w', \%HPVALUE),
1784                        new_text_box_for_int (\%pr::win, 'msgs_h', \%HPVALUE))),
1785                _('Message window')
1786            );
1787 }
1788
1789 sub new_winpos_subpage_sendrecv() {
1790     return new_vbox_pack (
1791                new_subpage_frame (
1792                    new_hbox_pack (
1793                        new_text_box_for_int (\%pr::win, 'send_w', \%HPVALUE),
1794                        new_text_box_for_int (\%pr::win, 'send_h', \%HPVALUE)),
1795                    _('Send window'), 'not-packed'),
1796                new_subpage_frame (
1797                    new_hbox_pack (
1798                        new_text_box_for_int (\%pr::win, 'recv_w', \%HPVALUE),
1799                        new_text_box_for_int (\%pr::win, 'recv_h', \%HPVALUE)),
1800                    _('Receive window'), 'not-packed')
1801            );
1802 }
1803
1804 sub new_winpos_subpage_fold() {
1805     return new_vbox_pack (
1806                new_subpage_frame (
1807                    new_vbox_pack_compact (
1808                        new_vbox_pack (
1809                             new_text_box_for_int (\%pr::win, 'fold_x', \%HPVALUE),
1810                             new_text_box_for_int (\%pr::win, 'fold_y', \%HPVALUE)),
1811                        new_hbox_pack (
1812                            new_text_box_for_int (\%pr::win, 'fold_w', \%HPVALUE),
1813                            new_text_box_for_int (\%pr::win, 'fold_h', \%HPVALUE))),
1814                    _('Folder window'), 'not-packed'),
1815                new_subpage_frame (
1816                    new_hbox_pack (
1817                        new_text_box_for_int (\%pr::win, 'fsel_w', \%HPVALUE),
1818                        new_text_box_for_int (\%pr::win, 'fsel_h', \%HPVALUE)),
1819                    _('Folder selection window'), 'not-packed')
1820            );
1821 }
1822
1823 sub new_winpos_subpage_addrbook() {
1824     return new_vbox_pack (
1825                 new_subpage_frame (
1826                      new_hbox_pack (
1827                           new_text_box_for_int (\%pr::win, 'addr_w', \%HPVALUE),
1828                           new_text_box_for_int (\%pr::win, 'addr_h', \%HPVALUE)),
1829                      _('Addressbook main window'), 'not-packed'),
1830                 new_subpage_frame (
1831                      new_hbox_pack (
1832                           new_text_box_for_int (\%pr::win, 'adep_w', \%HPVALUE),
1833                           new_text_box_for_int (\%pr::win, 'adep_h', \%HPVALUE)),
1834                      _('Edit person window'), 'not-packed'),
1835                 new_subpage_frame (
1836                      new_hbox_pack (
1837                           new_text_box_for_int (\%pr::win, 'adeg_w', \%HPVALUE),
1838                           new_text_box_for_int (\%pr::win, 'adeg_h', \%HPVALUE)),
1839                      _('Edit group window'), 'not-packed'),
1840                 new_subpage_frame (
1841                      new_hbox_pack (
1842                           new_text_box_for_int (\%pr::win, 'adda_w', \%HPVALUE),
1843                           new_text_box_for_int (\%pr::win, 'adda_h', \%HPVALUE)),
1844                      _('Add address window'), 'not-packed'),
1845                 new_subpage_frame (
1846                      new_hbox_pack (
1847                           new_text_box_for_int (\%pr::win, 'addf_w', \%HPVALUE),
1848                           new_text_box_for_int (\%pr::win, 'addf_h', \%HPVALUE)),
1849                      _('Folder select window'), 'not-packed')
1850            );
1851 }
1852
1853 sub new_winpos_subpage_accounts() {
1854     return new_vbox_pack (
1855                 new_subpage_frame (
1856                      new_hbox_pack (
1857                           new_text_box_for_int (\%pr::win, 'acco_w', \%HPVALUE),
1858                           new_text_box_for_int (\%pr::win, 'acco_h', \%HPVALUE)),
1859                      _('Accounts window'), 'not-packed'),
1860                 new_subpage_frame (
1861                      new_hbox_pack (
1862                           new_text_box_for_int (\%pr::win, 'acce_w', \%HPVALUE),
1863                           new_text_box_for_int (\%pr::win, 'acce_h', \%HPVALUE)),
1864                      _('Edit account window'), 'not-packed')
1865            );
1866 }
1867
1868 sub new_winpos_subpage_filtering() {
1869     return new_vbox_pack (
1870                 new_subpage_frame (
1871                      new_hbox_pack (
1872                           new_text_box_for_int (\%pr::win, 'filt_w', \%HPVALUE),
1873                           new_text_box_for_int (\%pr::win, 'filt_h', \%HPVALUE)),
1874                      _('Filtering window'), 'not-packed'),
1875                 new_subpage_frame (
1876                      new_hbox_pack (
1877                           new_text_box_for_int (\%pr::win, 'fila_w', \%HPVALUE),
1878                           new_text_box_for_int (\%pr::win, 'fila_h', \%HPVALUE)),
1879                      _('Filtering actions window'), 'not-packed'),
1880                 new_subpage_frame (
1881                      new_hbox_pack (
1882                           new_text_box_for_int (\%pr::win, 'fild_w', \%HPVALUE),
1883                           new_text_box_for_int (\%pr::win, 'fild_h', \%HPVALUE)),
1884                      _('Filtering debug window'), 'not-packed'),
1885                 new_subpage_frame (
1886                      new_hbox_pack (
1887                           new_text_box_for_int (\%pr::win, 'matc_w', \%HPVALUE),
1888                           new_text_box_for_int (\%pr::win, 'matc_h', \%HPVALUE)),
1889                      _('Matcher window'), 'not-packed')
1890            );
1891 }
1892
1893 sub new_winpos_subpage_prefs() {
1894     return new_vbox_pack (
1895                 new_subpage_frame (
1896                      new_hbox_pack (
1897                           new_text_box_for_int (\%pr::win, 'pref_w', \%HPVALUE),
1898                           new_text_box_for_int (\%pr::win, 'pref_h', \%HPVALUE)),
1899                      _('Preferences window'), 'not-packed'),
1900                 new_subpage_frame (
1901                      new_hbox_pack (
1902                           new_text_box_for_int (\%pr::win, 'temp_w', \%HPVALUE),
1903                           new_text_box_for_int (\%pr::win, 'temp_h', \%HPVALUE)),
1904                      _('Templates window'), 'not-packed'),
1905                 new_subpage_frame (
1906                      new_hbox_pack (
1907                           new_text_box_for_int (\%pr::win, 'acti_w', \%HPVALUE),
1908                           new_text_box_for_int (\%pr::win, 'acti_h', \%HPVALUE)),
1909                      _('Actions window'), 'not-packed'),
1910                 new_subpage_frame (
1911                      new_hbox_pack (
1912                           new_text_box_for_int (\%pr::win, 'tags_w', \%HPVALUE),
1913                           new_text_box_for_int (\%pr::win, 'tags_h', \%HPVALUE)),
1914                      _('Tags window'), 'not-packed'),
1915                 new_subpage_frame (
1916                      new_hbox_pack (
1917                           new_text_box_for_int (\%pr::win, 'plug_w', \%HPVALUE),
1918                           new_text_box_for_int (\%pr::win, 'plug_h', \%HPVALUE)),
1919                      _('Plugins window'), 'not-packed')
1920            );
1921 }
1922
1923 sub new_winpos_subpage_misc() {
1924     return new_vbox_pack (
1925                 new_subpage_frame (
1926                      new_hbox_pack (
1927                           new_text_box_for_int (\%pr::win, 'logw_w', \%HPVALUE),
1928                           new_text_box_for_int (\%pr::win, 'logw_h', \%HPVALUE)),
1929                      _('Log window'), 'not-packed'),
1930                 new_subpage_frame (
1931                      new_hbox_pack (
1932                           new_text_box_for_int (\%pr::win, 'prin_w', \%HPVALUE),
1933                           new_text_box_for_int (\%pr::win, 'prin_h', \%HPVALUE)),
1934                      _('Print preview window'), 'not-packed'),
1935                 new_subpage_frame (
1936                      new_hbox_pack (
1937                           new_text_box_for_int (\%pr::win, 'sour_w', \%HPVALUE),
1938                           new_text_box_for_int (\%pr::win, 'sour_h', \%HPVALUE)),
1939                      _('View source window'), 'not-packed')
1940            );
1941 }
1942
1943 sub new_winpos_page() {
1944     my $winbook = Gtk2::Notebook->new;
1945     $winbook->set_tab_pos ('right');
1946     $winbook->append_page (new_winpos_subpage_main, _('Main'));
1947     $winbook->append_page (new_winpos_subpage_msgs, _('Message'));
1948     $winbook->append_page (new_winpos_subpage_sendrecv, _('Send/Receive'));
1949     $winbook->append_page (new_winpos_subpage_fold, _('Folder'));
1950     $winbook->append_page (new_winpos_subpage_addrbook, _('Addressbook'));
1951     $winbook->append_page (new_winpos_subpage_accounts, _('Accounts'));
1952     $winbook->append_page (new_winpos_subpage_filtering, _('Filtering'));
1953     $winbook->append_page (new_winpos_subpage_prefs, _('Preferences'));
1954     $winbook->append_page (new_winpos_subpage_misc, _('Other'));
1955     return $winbook;
1956 }
1957
1958 %pr::acc = ( # per account hidden preferences
1959     tls_set => [
1960         'gnutls_set_priority',
1961         $xl::s{l_acc_gtls_set},
1962         $xl::s{h_acc_gtls_set},
1963         'bool',
1964         '3.9.0.181',
1965         '0',
1966     ],
1967     tls_pri => [
1968         'gnutls_priority',
1969         $xl::s{l_acc_gtls_pri},
1970         $xl::s{h_acc_gtls_pri},
1971         'char,0,256,32',
1972         '3.9.0.181',
1973         '0',
1974     ],
1975 );
1976
1977 sub new_account_subpage($) {
1978     my ($akey) = @_;
1979     return new_vbox_pack (
1980                 new_subpage_frame (
1981                     new_vbox_pack (
1982                         new_check_button_for (\%pr::acc, 'tls_set', $ACHPVALUE{$akey}),
1983                         new_text_box_for_nchar (\%pr::acc, 'tls_pri', $ACHPVALUE{$akey})),
1984                     _('GnuTLS priority'), 'not-packed')
1985            );
1986 }
1987
1988 sub new_accounts_page() {
1989     my $accbook = Gtk2::Notebook->new;
1990     $accbook->set_tab_pos ('right');
1991     my @akeys = sort {
1992         $ACPREFS{$a}{'account_name'} cmp $ACPREFS{$b}{'account_name'}
1993     } keys %ACPREFS;
1994     foreach (@akeys) {
1995         my $name = $ACPREFS{$_}{'account_name'};
1996         my $isdef = ($ACPREFS{$_}{'is_default'} eq '1');
1997         my $page = new_account_subpage ($_);
1998         $accbook->append_page ($page, $isdef? '<b>' . $name . '</b>': $name);
1999         if ($isdef) {
2000             my $label = $accbook->get_tab_label ($page);
2001             $label->set_use_markup (TRUE);
2002         }
2003     }
2004     $accbook->set_scrollable (TRUE);
2005     return $accbook;
2006 }
2007
2008 %pr::plu = ( # plugins hidden preferences
2009     # att_remover
2010     arm_winw => [
2011         'win_width',
2012         $xl::s{l_win_w},
2013         $xl::s{h_win_w},
2014         'int,0,3000', # 0 pixels - 3000 pixels
2015         '3.9.0.74',
2016         '-1',
2017         'AttRemover',
2018     ],
2019     arm_winh => [
2020         'win_height',
2021         $xl::s{l_win_h},
2022         $xl::s{h_win_h},
2023         'int,0,3000', # 0 pixels - 3000 pixels
2024         '3.9.0.74',
2025         '-1',
2026         'AttRemover',
2027     ],
2028     # GPG
2029     gpg_alimit => [
2030         'autocompletion_limit',
2031         $xl::s{l_plu_gpg_alimit},
2032         $xl::s{h_plu_gpg_alimit},
2033         'int,0,100',
2034         '3.12.0.75',
2035         '0',
2036         'GPG',
2037     ],
2038     # managesieve
2039     msv_winw => [
2040         'manager_win_width',
2041         $xl::s{l_win_w},
2042         $xl::s{h_win_w},
2043         'int,0,3000', # 0 pixels - 3000 pixels
2044         '3.11.1.210',
2045         '-1',
2046         'ManageSieve',
2047     ],
2048     msv_winh => [
2049         'manager_win_height',
2050         $xl::s{l_win_h},
2051         $xl::s{h_win_h},
2052         'int,0,3000', # 0 pixels - 3000 pixels
2053         '3.11.1.210',
2054         '-1',
2055         'ManageSieve',
2056     ],
2057     # libravatar
2058     lav_burl => [
2059         'base_url',
2060         $xl::s{l_plu_lav_burl},
2061         $xl::s{h_plu_lav_burl},
2062         'char,0,1024,32',
2063         '3.9.3.32',
2064         'http://cdn.libravatar.org/avatar',
2065         'Libravatar',
2066     ],
2067     # perl
2068     prl_flvb => [
2069         'filter_log_verbosity',
2070         $xl::s{l_plu_prl_flvb},
2071         $xl::s{h_plu_prl_flvb},
2072         '0=l_plu_prl_none;1=l_plu_prl_manual;2=l_plu_prl_action;3=l_plu_prl_match',
2073         '3.9.0.75',
2074         '2',
2075         'PerlPlugin',
2076     ],
2077 );
2078
2079 sub new_plugins_page() {
2080     my %frame = ();
2081     $frame{'AttRemover'} =
2082                 new_subpage_frame (
2083                      new_hbox_pack (
2084                           new_text_box_for_int (\%pr::plu, 'arm_winw', $PLHPVALUE{'AttRemover'}),
2085                           new_text_box_for_int (\%pr::plu, 'arm_winh', $PLHPVALUE{'AttRemover'})),
2086                      _('Attachment remover'), 'not-packed');
2087     $frame{'GPG'} =
2088                 new_subpage_frame (
2089                      new_hbox_pack (
2090                           new_text_box_for_int (\%pr::plu, 'gpg_alimit', $PLHPVALUE{'GPG'})),
2091                      _('GPG'), 'not-packed');
2092     $frame{'ManageSieve'} =
2093                 new_subpage_frame (
2094                      new_hbox_pack (
2095                           new_text_box_for_int (\%pr::plu, 'msv_winw', $PLHPVALUE{'ManageSieve'}),
2096                           new_text_box_for_int (\%pr::plu, 'msv_winh', $PLHPVALUE{'ManageSieve'})),
2097                      _('Sieve manager'), 'not-packed');
2098     $frame{'Libravatar'} =
2099                 new_subpage_frame (
2100                      new_hbox_pack (
2101                           new_text_box_for_nchar (\%pr::plu, 'lav_burl', $PLHPVALUE{'Libravatar'})),
2102                      _('Libravatar'), 'not-packed');
2103     $frame{'PerlPlugin'} =
2104                 new_subpage_frame (
2105                      new_hbox_pack (
2106                           new_selection_box_for (\%pr::plu, 'prl_flvb', $PLHPVALUE{'PerlPlugin'})),
2107                      _('Perl'), 'not-packed');
2108     foreach (@PLUGINS) {
2109         $frame{$_}->set_sensitive (defined $PLHPVALUE{$_});
2110     }
2111     return new_vbox_pack (
2112                 $frame{'AttRemover'},
2113                 $frame{'GPG'},
2114                 $frame{'ManageSieve'},
2115                 $frame{'Libravatar'},
2116                 $frame{'PerlPlugin'});
2117 }
2118
2119 sub new_info_page() {
2120     my $t0 = Gtk2::Table->new (7, 2, FALSE);
2121     my $v = get_toolkit_versions ();
2122     my %labels = (
2123         'glib' => 'Perl-GLib',
2124         'glib-r' => _('GLib runtime'),
2125         'glib-b' => _('GLib built'),
2126         'gtk2' => 'Perl-GTK2',
2127         'gtk2-r' => _('GTK2 runtime'),
2128         'gtk2-b' => _('GTK2 built'),
2129     );
2130     my $row = 0;
2131     foreach (sort keys %$v) {
2132         if (defined $v->{$_}) {
2133             my $label = Gtk2::Label->new ($labels{$_});
2134             my $value = Gtk2::Label->new ('<b>' . $v->{$_} . '</b>');
2135             $label->set_alignment (0, 0.5);
2136             $value->set_alignment (0, 0.5);
2137             $value->set_use_markup (TRUE);
2138             $t0->attach ($label, 0, 1, $row, $row + 1, 'fill', 'shrink', 8, 6);
2139             $t0->attach ($value, 1, 2, $row, $row + 1, 'fill', 'shrink', 8, 6);
2140             ++$row;
2141         }
2142     }
2143     my $t1 = Gtk2::Table->new (2, 2, FALSE);
2144     my @lbl = map { $_->set_alignment (0, 0.5); $_ } (
2145         Gtk2::Label->new (_('Binary')),
2146         Gtk2::Label->new (_('Configuration'))
2147     );
2148     my $cfgv = $CONFIGDATA->{'Common'}{'config_version'};
2149     $cfgv //= '';
2150     my @val = map { $_->set_alignment (0, 0.5); $_->set_use_markup (TRUE); $_ } (
2151         Gtk2::Label->new ('<b>' . $CLAWSV . '</b>'),
2152         Gtk2::Label->new ('<b>' . $cfgv . '</b>')
2153     );
2154     for (my $i = 0; $i <= $#lbl; ++$i) {
2155         $t1->attach ($lbl[$i], 0, 1, $i, $i + 1, 'fill', 'shrink', 8, 6);
2156         $t1->attach ($val[$i], 1, 2, $i, $i + 1, 'fill', 'shrink', 8, 6);
2157     }
2158     return new_vbox_pack (
2159                 new_subpage_frame ($t0, _('Library versions'), 'not-packed'),
2160                 new_subpage_frame ($t1, _('Claws Mail versions'), 'not-packed'));
2161 }
2162
2163 # version info
2164 sub get_toolkit_versions {
2165     my %versions = ();
2166     $versions{'glib'} = $Glib::VERSION;
2167     # version info stuff appeared in 1.040
2168     if ($Glib::VERSION >= 1.040) {
2169         $versions{'glib-b'} = join('.', Glib->GET_VERSION_INFO);
2170         $versions{'glib-r'} = join('.',
2171             &Glib::major_version, &Glib::minor_version, &Glib::micro_version);
2172     }
2173     $versions{'gtk2'} = $Gtk2::VERSION;
2174     if ($Gtk2::VERSION >= 1.040) {
2175         $versions{'gtk2-b'} = join('.', Gtk2->GET_VERSION_INFO);
2176         $versions{'gtk2-r'} = join('.',
2177             &Gtk2::major_version, &Gtk2::minor_version, &Gtk2::micro_version);
2178     }
2179     return \%versions;
2180 }
2181
2182 sub print_version() {
2183     print $xl::s{about_title} . "\n";
2184     print $xl::s{about_version} . " $VERSION\n";
2185     my $v = get_toolkit_versions ();
2186     if ($v->{'glib-b'}) {
2187         print _("Perl-GLib version {glibv}, built for {glibb}, running with {glibr}.",
2188                 glibv => $v->{'glib'},
2189                 glibb => $v->{'glib-b'},
2190                 glibr => $v->{'glib-r'});
2191     } else {
2192         print _("Perl-GLib version {glibv}.", glibv => $v->{'glib'});
2193     }
2194     print "\n";
2195     if ($v->{'gtk2-b'}) {
2196         print _("Perl-GTK2 version {gtkv}, built for {gtkb}, running with {gtkr}.",
2197                 gtkv => $v->{'gtk2'},
2198                 gtkb => $v->{'gtk2-b'},
2199                 gtkr => $v->{'gtk2-r'});
2200     } else {
2201         print _("Perl-GTK2 version {gtkv}.", gtkv => $v->{'gtk2'});
2202     }
2203     print "\n";
2204     my $clawsver = ($CLAWSV eq "") ?
2205                 _("Claws Mail was not found!") :
2206                 _("Claws Mail returned version {cmv}.", cmv => $CLAWSV);
2207     print $clawsver . "\n";
2208 }
2209
2210 # the command line help
2211 sub print_help() {
2212     my $line = '-' x length ($xl::s{about_title});
2213     say $line;
2214     say $xl::s{about_title};
2215     say $line;
2216     my @help = (
2217         _("Syntax:"),
2218         _("  clawsker [options]"),
2219         _("Options:"),
2220         _("  -a|--alternate-config-dir <dir>  Uses <dir> as Claws Mail configuration."),
2221         _("  -b|--verbose                     More messages on standard output."),
2222         _("  -c|--clawsrc <file>              Uses <file> as full resource name."),
2223         _("  -h|--help                        Prints this help screen and exits."),
2224         _("  -r|--read-only                   Disables writing changes to disk."),
2225         _("  -v|--version                     Prints version information and exits.")
2226     );
2227     foreach (@help) { say $_ }
2228 }
2229
2230 sub parse_command_line {
2231     my $cont = TRUE;
2232     $CLAWSV = get_claws_version ();
2233     eval {
2234         GetOptions('h|help' => sub { print_help (); $cont = FALSE },
2235             'v|version' => sub { print_version (); $cont = FALSE },
2236             'b|verbose' => sub { $VERBOSE = TRUE },
2237             'r|read-only' => sub { $READONLY = TRUE },
2238             'u|use-claws-version=s' => \&opt_use_claws_version,
2239             'a|alternate-config-dir=s' => \&opt_alternate_config_dir,
2240             'c|clawsrc=s' => \&opt_clawsrc)
2241         or die _("try -h or --help for syntax.\n");
2242     };
2243     if ($@) {
2244         my $msg = _("Error in options: {msg}\n", msg => $@);
2245         if (defined $ENV{'DISPLAY'} and $ENV{'DISPLAY'} ne '') {
2246             eval { Gtk2->init };
2247             error_dialog ($msg) unless $@;
2248         }
2249         die $msg;
2250     }
2251     return $cont;
2252 }
2253
2254 sub opt_use_claws_version {
2255     my ($name, $value) = @_;
2256     die _("Error: {opt} requires a dotted numeric value argument\n", opt => $name)
2257         unless ($value =~ /^[\d\.]+$/);
2258     $CLAWSV = $value;
2259 }
2260
2261 sub opt_alternate_config_dir {
2262     my ($name, $value) = @_;
2263     die _("Error: '{dir}' is not a directory or does not exist\n", dir => $value)
2264         unless -d $value;
2265     $CONFIGDIR = $value;
2266     $CONFIGDIR .= "/" unless ($CONFIGDIR =~ /.*\/$/);
2267     $ALTCONFIGDIR = TRUE;
2268 }
2269
2270 sub opt_clawsrc {
2271     my ($name, $value) = @_;
2272     die _("Error: '{value}' is not a file or does not exist\n", value => $value)
2273         unless -f $value;
2274     set_rc_filename ($value);
2275 }
2276
2277 # update the hidden preferences status from loaded values
2278 sub init_hidden_preferences {
2279     foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth, \%pr::win) {
2280         foreach my $key (keys %$hash) {
2281             $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]};
2282         }
2283     }
2284     foreach my $akey (keys %ACPREFS) {
2285         foreach my $key (keys %pr::acc) {
2286             my $pname = $pr::acc{$key}[NAME];
2287             $ACHPVALUE{$akey}{$pname} = $ACPREFS{$akey}{$pname};
2288         }
2289     }
2290     foreach my $key (keys %pr::plu) {
2291         my $plugin = $pr::plu{$key}[PLUGIN];
2292         my $pname = $pr::plu{$key}[NAME];
2293         if (defined $PLPREFS{$plugin}) {
2294             $PLHPVALUE{$plugin}{$pname} = $PLPREFS{$plugin}{$pname};
2295         }
2296     }
2297     return TRUE;
2298 }
2299
2300 # generic load/save resource files
2301 sub load_resource {
2302     my $rc = shift;
2303     my %data = ();
2304     my %meta = ();
2305     my $line = 0;
2306     open (RCF, '<:encoding(utf8)', $rc)
2307         or die _("Error: opening '{file}' for reading", file => $rc) . ": $!\n";
2308     my $section = '_'; # default unnamed section
2309     while (<RCF>) {
2310         chomp;
2311         ++$line;
2312         next if (/^\s*$/);
2313         if (/^\[([^\]]+)\]$/) { # new section
2314             $section = $1;
2315             die _("Error: duplicate section '{sect}' in resource file '{file}'\n",
2316                 sect => $section, file => $rc) if ($data{$section});
2317             $data{$section} = {};
2318             $meta{$section}{'#'} = $line;
2319         }
2320         elsif (/^([0-9a-z_]+)=(.*)$/) { # key=value
2321             $data{$section}{$1} = $2;
2322             $meta{$section}{$1} = $line;
2323         }
2324         elsif (/^(.*)$/) { # lone value
2325             push (@{$data{$section}{'_'}}, $1);
2326         }
2327     }
2328     close (RCF);
2329     return (\%data, \%meta);
2330 }
2331
2332 sub save_resource {
2333     my ($rc, $data, $meta) = @_;
2334     open (RCF, '>:utf8', $rc)
2335         or die _("Error: opening '{file}' for writing", file => $rc) . ": $!\n";
2336     my @sections = keys %$data;
2337     if (defined $meta) {
2338         @sections = sort {
2339             $meta->{$a}{'#'} <=> $meta->{$b}{'#'}
2340         } @sections
2341     }
2342     foreach my $section (@sections) {
2343         say RCF "[$section]";
2344         if (ref ($data->{$section}{'_'}) eq 'ARRAY') {
2345             foreach my $val (@{$data->{$section}{'_'}}) {
2346                 say RCF $val;
2347             }
2348         } else {
2349             my @keys = keys %{$data->{$section}};
2350             if (defined $meta) {
2351                 @keys = sort {
2352                     $meta->{$section}{$a} <=> $meta->{$section}{$b}
2353                 } @keys
2354             }
2355             foreach my $key (@keys) {
2356                 my $val = $data->{$section}{$key};
2357                 say RCF "$key=$val";
2358             }
2359         }
2360         say RCF "";
2361     }
2362     close (RCF);
2363 }
2364
2365 # load current status from disc
2366 sub load_rc_preferences {
2367     my $rc = get_rc_filename ();
2368     log_message ("Loading preferences from $rc\n");
2369     return FALSE unless check_rc_file ($rc);
2370     ($CONFIGDATA, $CONFIGMETA) = load_resource ($rc);
2371     foreach (keys %{$CONFIGDATA->{'Common'}}) {
2372         $PREFS{$_} = $CONFIGDATA->{'Common'}{$_};
2373     }
2374     foreach my $plugin (@PLUGINS) {
2375         if (defined $CONFIGDATA->{$plugin}) {
2376             push (@AVPLUGINS, $plugin);
2377             foreach (keys %{$CONFIGDATA->{$plugin}}) {
2378                 $PLPREFS{$plugin}{$_} = $CONFIGDATA->{$plugin}{$_};
2379             }
2380         }
2381     }
2382     return TRUE;
2383 }
2384
2385 sub load_ac_preferences {
2386     my $rc = get_ac_rc_filename ();
2387     log_message ("Loading account preferences from $rc\n");
2388     return FALSE unless check_rc_file ($rc);
2389     ($ACCOUNTDATA, $ACCOUNTMETA) = load_resource ($rc);
2390     foreach my $asect (keys %$ACCOUNTDATA) {
2391         if ($asect =~ /^Account: (\d+)$/) {
2392             foreach (keys %{$ACCOUNTDATA->{$asect}}) {
2393                 $ACPREFS{$1}{$_} = $ACCOUNTDATA->{$asect}{$_};
2394             }
2395         }
2396     }
2397     return TRUE;
2398 }
2399
2400 sub load_preferences {
2401     return FALSE unless check_claws_not_running ();
2402     return (load_rc_preferences () and load_ac_preferences ());
2403 }
2404
2405 # save current preferences to disc
2406 sub save_preferences {
2407     my $rc = get_rc_filename ();
2408     log_message ("Saving preferences to $rc\n");
2409     return FALSE unless check_rc_file ($rc);
2410     return FALSE unless check_claws_not_running ();
2411     my $rcbak = "$rc.backup";
2412     rename ($rc, $rcbak);
2413     foreach (keys %PREFS) {
2414         if (defined $HPVALUE{$_}) {
2415             $CONFIGDATA->{'Common'}{$_} = $HPVALUE{$_};
2416         }
2417     }
2418     foreach my $plugin (@AVPLUGINS) {
2419         foreach (keys %{$CONFIGDATA->{$plugin}}) {
2420             if (defined $PLHPVALUE{$plugin}{$_}) {
2421                 $CONFIGDATA->{$plugin}{$_} = $PLHPVALUE{$plugin}{$_};
2422             }
2423         }
2424     }
2425     save_resource ($rc, $CONFIGDATA, $CONFIGMETA);
2426     return TRUE;
2427 }
2428
2429 sub save_ac_preferences {
2430     my $rc = get_ac_rc_filename ();
2431     log_message ("Saving account preferences to $rc\n");
2432     return FALSE unless check_rc_file ($rc);
2433     return FALSE unless check_claws_not_running ();
2434     my $rcbak = "$rc.backup";
2435     rename ($rc, $rcbak);
2436     foreach my $asect (keys %$ACCOUNTDATA) {
2437         if ($asect =~ /^Account: (\d+)$/) {
2438             foreach (keys %{$ACCOUNTDATA->{$asect}}) {
2439                 if (defined $ACHPVALUE{$1}{$_}) {
2440                     $ACCOUNTDATA->{$asect}{$_} = $ACHPVALUE{$1}{$_};
2441                 }
2442             }
2443         }
2444     }
2445     save_resource ($rc, $ACCOUNTDATA, $ACCOUNTMETA);
2446     return TRUE;
2447 }
2448
2449 # create notebook
2450 sub new_notebook {
2451     my $nb = Gtk2::Notebook->new;
2452     #
2453     $nb->append_page (new_behaviour_page (), $xl::s{tab_behaviour});
2454     $nb->append_page (new_colours_page (), $xl::s{tab_colours});
2455     $nb->append_page (new_gui_page (), $xl::s{tab_gui});
2456     $nb->append_page (new_other_page (), $xl::s{tab_other});
2457     $nb->append_page (new_winpos_page (), $xl::s{tab_winpos});
2458     $nb->append_page (new_accounts_page (), $xl::s{tab_accounts});
2459     $nb->append_page (new_plugins_page (), $xl::s{tab_plugins});
2460     $nb->append_page (new_info_page (), $xl::s{tab_info});
2461
2462     return $nb;
2463 }
2464
2465 # create an about dialog
2466 sub new_about_dialog {
2467     my ($parent) = @_;
2468     my $title = $xl::s{about_title};
2469     my $lic = $xl::s{about_license};
2470     my $vers = $xl::s{about_version} . " $VERSION";
2471     my $license =
2472 "This program is free software: you can redistribute it and/or modify
2473 it under the terms of the GNU General Public License as published by
2474 the Free Software Foundation, either version 3 of the License, or
2475 (at your option) any later version.
2476
2477 This program is distributed in the hope that it will be useful,
2478 but WITHOUT ANY WARRANTY; without even the implied warranty of
2479 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
2480 GNU General Public License for more details.
2481
2482 You should have received a copy of the GNU General Public License
2483 along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.";
2484     my $year = "2007-2016";
2485     my $holder = "Ricardo Mones &lt;ricardo\@mones.org&gt;";
2486     my $url = "http://www.claws-mail.org/clawsker.php";
2487
2488     my $dialog = Gtk2::MessageDialog->new_with_markup ($parent,
2489                     [qw/modal destroy-with-parent/],
2490                     'info', 'close',
2491                     "<span size=\"x-large\" weight=\"bold\">$title</span>\n"
2492                     . "<span size=\"large\">$vers</span>\n\n"
2493                     . "<span color=\"blue\" size=\"large\">$url</span>\n\n"
2494                     . "<span>Copyright $year by $holder</span>\n\n"
2495                     . "<span size=\"large\">$lic</span>\n\n"
2496                     . "<span size=\"small\">$license</span>");
2497     $dialog->set_title ($xl::s{about});
2498     if (Gtk2->CHECK_VERSION (2, 10, 0)) {
2499         my @icons = get_app_icons ();
2500         my $image = Gtk2::Image->new_from_pixbuf ($icons[-1]);
2501         $image->show ();
2502         $image->set_alignment (0, 0);
2503         $dialog->set_image ($image);
2504     }
2505     #
2506     return $dialog;
2507 }
2508
2509 # create buttons box
2510 sub new_button_box {
2511     my ($parent, $adlg) = @_;
2512     my $b_about = Gtk2::Button->new_from_stock ('gtk-about');
2513     my $b_exit = Gtk2::Button->new_from_stock ('gtk-quit');
2514     my $b_apply = Gtk2::Button->new_from_stock ('gtk-apply');
2515     # disable button until is really implemented
2516     # my $b_undo = Gtk2::Button->new_from_stock ('gtk-undo');
2517     my $hbox = Gtk2::HBox->new (FALSE, 5);
2518     # signal handlers
2519     $b_exit->signal_connect (clicked => sub { Gtk2->main_quit });
2520     $b_apply->set_sensitive (not $READONLY);
2521     $b_apply->signal_connect (clicked => sub {
2522         save_preferences ($parent);
2523         save_ac_preferences ($parent);
2524     });
2525     # $b_undo->signal_connect (clicked => sub { undo_current_changes });
2526     $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
2527     # package them
2528     $hbox->pack_end ($b_apply, FALSE, FALSE, 0);
2529     $hbox->pack_end ($b_exit, FALSE, FALSE, 0);
2530     # $hbox->pack_end ($b_undo, FALSE, FALSE, 0);
2531     $hbox->pack_start ($b_about, FALSE, FALSE, 0);
2532     #
2533     return $hbox;
2534 }
2535
2536 sub get_app_icons {
2537     return @APPICONS if (@APPICONS);
2538     my @names;
2539     if (-d $DATADIR) { # installed
2540         my $dir = $DATADIR . '/icons/hicolor';
2541         @names = map {
2542             join ('/', ($dir, $_ . 'x' . $_, 'apps', $NAME . '.png'))
2543         } (48, 64, 128);
2544     } else { # unpacked tarball or git clone
2545         @names = map {
2546             join ('/', ('./icons', $NAME . '-' . $_ . '.png'));
2547         } (48, 64, 128);
2548     }
2549     foreach (@names) {
2550         my $icon = undef;
2551         $icon = Gtk2::Gdk::Pixbuf->new_from_file($_) if (-f $_);
2552         push @APPICONS, $icon if ($icon);
2553     }
2554     return @APPICONS;
2555 }
2556
2557 # initialise
2558 exit unless parse_command_line ();
2559 Gtk2->init;
2560 $main_window = Gtk2::Window->new ('toplevel');
2561 exit unless load_preferences ();
2562 exit unless init_hidden_preferences ();
2563 # create main GUI
2564 my $box = Gtk2::VBox->new (FALSE, 5);
2565 $box->set_border_width(3);
2566 my $about = new_about_dialog ();
2567 $box->pack_start (new_notebook (), FALSE, FALSE, 0);
2568 $box->pack_end (new_button_box ($main_window, $about), FALSE, FALSE, 0);
2569 $main_window->signal_connect (delete_event => sub { Gtk2->main_quit });
2570 $main_window->set_title ($xl::s{win_title});
2571 $main_window->set_icon_list (get_app_icons ());
2572 $main_window->add ($box);
2573 $main_window->show_all;
2574 Gtk2->main;
2575