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