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