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