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