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