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