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