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