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