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