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