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