3 # Clawsker :: A Claws Mail Tweaker
4 # Copyright 2007-2016 Ricardo Mones <ricardo@mones.org>
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.
11 # See COPYING file for license details.
12 # See AUTHORS file for a complete list of contributors.
15 binmode STDOUT, ":encoding(utf8)";
20 use Glib qw(TRUE FALSE);
22 use POSIX qw(setlocale);
25 use Digest::MD5 qw(md5_hex);
28 my $NAME = 'clawsker';
29 my $PREFIX = '@PREFIX@';
30 my $LIBDIR = '@LIBDIR@';
31 my $DATADIR = '@DATADIR@';
32 my $VERSION = '@VERSION@';
36 my $main_window = undef;
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));
44 my $SHOWHINTS = FALSE;
45 $SHOWHINTS = TRUE if ($Gtk2::VERSION >= 1.040 and Gtk2->CHECK_VERSION (2, 12, 0));
50 my $xla = gettext ($str);
51 if (scalar(keys(%par)) > 0) {
52 foreach my $key (keys %par) {
53 $xla =~ s/\{$key\}/$par{$key}/g;
56 return decode_utf8($xla);
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:'),
67 tab_colours => _('Colours'),
68 tab_behaviour => _('Behaviour'),
70 tab_other => _('Other'),
71 tab_winpos => _('Windows'),
72 tab_accounts => _('Accounts'),
73 tab_plugins => _('Plugins'),
74 tab_info => _('Info'),
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'),
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.'),
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).'),
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.'),
178 l_col_emphasis => _('X-Mailer header'),
179 h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
180 l_col_log_err => _('Error messages'),
181 h_col_log_err => _('Colour for error messages in log window.'),
182 l_col_log_in => _('Server messages'),
183 h_col_log_in => _('Colour for messages received from servers in log window.'),
184 l_col_log_msg => _('Standard messages'),
185 h_col_log_msg => _('Colour for messages in log window.'),
186 l_col_log_out => _('Client messages'),
187 h_col_log_out => _('Colour for messages sent to servers in log window.'),
188 l_col_log_warn => _('Warnings'),
189 h_col_log_warn => _('Colour for warning messages in log window.'),
191 l_col_diff_add => _('Added lines'),
192 h_col_diff_add => _('Colour for added lines in patches.'),
193 l_col_diff_del => _('Deleted lines'),
194 h_col_diff_del => _('Colour for deleted lines in patches.'),
195 l_col_diff_hunk => _('Hunk lines'),
196 h_col_diff_hunk => _('Color for hunk headers in patches.'),
198 l_win_x => _('X position'),
199 h_win_x => _('X coordinate for window\'s top-left corner.'),
200 l_win_y => _('Y position'),
201 h_win_y => _('Y coordinate for window\'s top-left corner.'),
202 l_win_w => _('Width'),
203 h_win_w => _('Window\'s width in pixels.'),
204 l_win_h => _('Height'),
205 h_win_h => _('Window\'s height in pixels.'),
207 l_win_main_mx => _('Maximized'),
208 h_win_main_mx => _('Changes window maximized status.'),
209 l_win_main_fs => _('Full-screen'),
210 h_win_main_fs => _('Changes full screen status.'),
212 l_acc_gtls_set => _('Use custom GnuTLS priority'),
213 h_acc_gtls_set => _('Enables using user provided GnuTLS priority string.'),
214 l_acc_gtls_pri => _('GnuTLS priority'),
215 h_acc_gtls_pri => _('Value to use as GnuTLS priority string if custom priority check is enabled. Otherwise this value is ignored.'),
217 l_plu_gpg_alimit => _('Autocompletion limit'),
218 h_plu_gpg_alimit => _('Limits the number of addresses obtained from from keyring through autocompletion. Use 0 to get all matches.'),
219 l_plu_lav_burl => _('Base URL'),
220 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.'),
221 l_plu_prl_flvb => _('Log level'),
222 h_plu_prl_flvb => _('Verbosity level of log, acumulative.'),
223 l_plu_prl_none => _('None'),
224 l_plu_prl_manual => _('Manual'),
225 l_plu_prl_action => _('Actions'),
226 l_plu_prl_match => _('Matches'),
228 e_error => _('Error: '),
229 e_noclawsrc => _('resource file for Claws Mail was not found.'),
230 e_running => _('seems Claws Mail is currently running, close it first.'),
233 # data and metadata of resource files
238 # all preferences read by load_preferences
242 # values of all preferences handled by clawsker
246 # default config dir and file name
247 my $ALTCONFIGDIR = FALSE;
248 my $CONFIGDIR = $ENV{HOME} . '/.claws-mail/';
249 my $CONFIGRC = 'clawsrc';
250 my $ACCOUNTRC = 'accountrc';
251 # supported and available plugins lists
252 my @PLUGINS = qw(AttRemover GPG ManageSieve Libravatar PerlPlugin);
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)
264 # constants for GUI spacing
265 use constant HBOX_SPC => 5;
266 use constant FRAME_SPC => 2;
267 use constant PAGE_SPC => 5;
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');
279 while (($idx <= $#refvers)
280 and (int ($version[$idx]) == int ($refvers[$idx]))) {
283 return TRUE if (($idx > $#refvers)
284 or (int ($version[$idx]) >= int ($refvers[$idx])));
288 sub get_claws_version {
289 $_ = qx/which claws-mail/;
291 return "" unless ($_); # not installed
295 my @fver = split (/ /);
296 die "Invalid version string" unless ($fver[2] eq "version");
297 my @ver = split (/\./, $fver[3]);
300 if ($ver[2] =~ /(\d+)git(\d+)/) {
309 # data handlers and auxiliar functions
311 sub handle_bool_value {
312 my ($widget, $event, $dataref) = @_;
313 $$dataref = ($widget->get_active ())? '1': '0';
316 sub handle_int_value {
317 my ($widget, $event, $dataref) = @_;
318 $_ = $widget->get_text ();
323 $widget->set_text ($_);
326 $widget->set_text ($$dataref);
330 sub handle_string_value {
331 my ($widget, $event, $dataref) = @_;
332 $$dataref = $widget->get_text ();
335 sub handle_nchar_value {
336 my ($widget, $event, $dataref, $minlen, $maxlen) = @_;
337 $_ = substr ($widget->get_text (), 0, $maxlen);
338 $widget->set_text ($_);
342 sub gdk_color_from_str {
344 my ($rr, $gg, $bb) = (0, 0 ,0);
346 if (/\#([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])/) {
351 my $color = Gtk2::Gdk::Color->new ($rr, $gg, $bb);
355 sub str_from_gdk_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);
364 sub handle_color_value {
365 my ($widget, $event, $dataref) = @_;
366 my $newcol = $widget->get_color;
367 $$dataref = str_from_gdk_color ($newcol);
370 sub handle_selection_value {
371 my ($widget, $event, $dataref) = @_;
372 $$dataref = $widget->get_active;
375 sub get_rc_filename {
376 return $CONFIGDIR . $CONFIGRC;
379 sub get_ac_rc_filename {
380 return $CONFIGDIR . $ACCOUNTRC;
383 sub set_rc_filename {
385 my @parts = split ('/', $fullname);
386 $CONFIGRC = $parts[$#parts];
387 $parts[$#parts] = '';
388 $CONFIGDIR = join ('/', @parts);
392 my ($mesg, $fatal) = @_;
393 if (defined($fatal) && $fatal eq 'die') {
394 die "$NAME: $mesg\n";
397 print "$NAME: $mesg\n";
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'));
410 sub claws_is_running {
411 my $emsg = "$xl::s{e_error}$xl::s{e_running}";
413 error_dialog ($emsg);
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-$<";
425 my $socket = "$lockdir/" . md5_hex ($_);
426 -S $socket and return claws_is_running ();
433 (defined($rcfile) && -f $rcfile) or do {
434 my $emsg = "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
436 error_dialog ($emsg);
442 sub set_widget_hint {
444 my ($wdgt, $hint) = @_;
445 $wdgt->set_tooltip_text ($hint);
446 $wdgt->set_has_tooltip (TRUE);
450 sub set_widget_sens {
451 my ($wdgt, $versions) = @_;
452 my @ver = split(/,/, $versions);
454 $wdgt->set_sensitive (
455 version_greater_or_equal ($CLAWSV, $ver[0])
456 and version_greater_or_equal ($ver[1], $CLAWSV)
459 $wdgt->set_sensitive (version_greater_or_equal ($CLAWSV, $ver[0]));
463 # graphic element creation
465 sub new_check_button_for($$$) {
466 my ($hash, $key, $vhash) = @_;
467 my $name = $$hash{$key}[NAME];
468 my $label = $$hash{$key}[LABEL];
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 {
477 handle_bool_value ($w, $e, \$$vhash{$name});
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);
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);
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 {
503 handle_int_value ($w, $e, \$$vhash{$name});
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);
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];
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 {
531 handle_nchar_value ($w, $e, \$$vhash{$name}, $type[1], $type[2]);
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);
542 sub new_color_button_for($$$) {
543 my ($hash, $key, $vhash) = @_;
544 my $name = $$hash{$key}[NAME];
545 my $label = $$hash{$key}[LABEL];
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 {
557 handle_color_value ($w, $e, \$$vhash{$name});
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);
568 sub new_selection_box_for($$$) {
569 my ($hash, $key, $vhash) = @_;
570 my $name = $$hash{$key}[NAME];
571 my $label = $$hash{$key}[LABEL];
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});
581 $combo->signal_connect ('changed' => sub {
583 handle_selection_value ($w, $e, \$$vhash{$name});
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);
597 # more graphic helpers
600 my $hbox = Gtk2::HBox->new (FALSE, 5);
601 $hbox->set_border_width (PAGE_SPC);
602 foreach my $item (@_) {
603 $hbox->pack_start ($item, FALSE, FALSE, 0);
609 my $vbox = Gtk2::VBox->new (FALSE, 5);
610 $vbox->set_border_width (PAGE_SPC);
611 foreach my $item (@_) {
612 $vbox->pack_start ($item, FALSE, FALSE, 0);
617 sub new_vbox_pack_compact {
618 my $vbox = Gtk2::VBox->new (FALSE, 0);
619 $vbox->set_border_width (0);
620 foreach my $item (@_) {
621 $vbox->pack_start ($item, FALSE, FALSE, 0);
626 sub new_subpage_frame {
627 my ($box, $title, $notpacked) = @_;
628 my $frame = Gtk2::Frame->new ($title);
630 return new_vbox_pack ($frame) unless defined ($notpacked);
634 # preference maps and corresponding page creation subs
636 %pr::oth = ( # other preferences
638 'addressbook_use_editaddress_dialog',
639 $xl::s{l_oth_use_dlg},
640 $xl::s{h_oth_use_dlg},
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
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
662 'use_networkmanager',
663 $xl::s{l_oth_use_netm},
664 $xl::s{h_oth_use_netm},
670 'master_passphrase_pbkdf2_rounds',
671 $xl::s{l_oth_mp_rounds},
672 $xl::s{h_oth_mp_rounds},
679 sub new_other_page() {
680 return new_vbox_pack (
683 new_check_button_for(\%pr::oth, 'use_dlg', \%HPVALUE)),
684 $xl::s{ab_frame}, 'not-packed'),
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'),
692 new_check_button_for(\%pr::oth, 'use_netm', \%HPVALUE)),
693 $xl::s{netm_frame}, 'not-packed'),
696 new_text_box_for_int(\%pr::oth, 'mp_rounds', \%HPVALUE)),
697 $xl::s{mpass_frame}, 'not-packed')
701 %pr::gui = ( # gui bells and whistles
704 $xl::s{l_gui_b_unread},
705 $xl::s{h_gui_b_unread},
712 $xl::s{l_gui_no_markup},
713 $xl::s{h_gui_no_markup},
719 'enable_dotted_lines',
720 $xl::s{l_gui_dot_lines},
721 $xl::s{h_gui_dot_lines},
728 $xl::s{l_gui_h_scroll},
729 $xl::s{h_gui_h_scroll},
736 $xl::s{l_gui_swp_from},
737 $xl::s{h_gui_swp_from},
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',
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',
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
767 'textview_cursor_visible',
768 $xl::s{l_gui_cursor_v},
769 $xl::s{h_gui_cursor_v},
775 'toolbar_detachable',
776 $xl::s{l_gui_toolbar_d},
777 $xl::s{h_gui_toolbar_d},
783 'use_stripes_everywhere',
784 $xl::s{l_gui_strip_all},
785 $xl::s{h_gui_strip_all},
791 'use_stripes_in_summaries',
792 $xl::s{l_gui_strip_sum},
793 $xl::s{h_gui_strip_sum},
800 $xl::s{l_gui_two_line_v},
801 $xl::s{h_gui_two_line_v},
807 'show_compose_margin',
808 $xl::s{l_gui_margin_co},
809 $xl::s{h_gui_margin_co},
815 'msgview_date_format',
816 $xl::s{l_gui_mview_date},
817 $xl::s{h_gui_mview_date},
823 'zero_replacement_char',
824 $xl::s{l_gui_zero_char},
825 $xl::s{h_gui_zero_char},
832 $xl::s{l_gui_type_any},
833 $xl::s{h_gui_type_any},
840 $xl::s{l_gui_next_del},
841 $xl::s{h_gui_next_del},
849 my $gf = Gtk2::VBox->new (FALSE, 5);
850 $gf->set_border_width (PAGE_SPC);
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);
856 $gf->pack_start (new_subpage_frame (
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 (
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 (
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 (
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 (
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);
898 %pr::beh = ( # tweak some behaviour
901 $xl::s{l_beh_hover_t},
902 $xl::s{h_beh_hover_t},
903 'int,100,3000', # 0.1 seconds - 3 seconds
909 $xl::s{l_beh_dangerous},
910 $xl::s{h_beh_dangerous},
916 'respect_flowed_format',
917 $xl::s{l_beh_flowed},
918 $xl::s{h_beh_flowed},
924 'save_parts_readwrite',
925 $xl::s{l_beh_parts_rw},
926 $xl::s{h_beh_parts_rw},
932 'skip_ssl_cert_check',
933 $xl::s{l_beh_skip_ssl},
934 $xl::s{h_beh_skip_ssl},
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
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
957 $xl::s{l_beh_unsafe_ssl},
958 $xl::s{h_beh_unsafe_ssl},
964 'utf8_instead_of_locale_for_broken_mail',
965 $xl::s{l_beh_use_utf8},
966 $xl::s{h_beh_use_utf8},
973 $xl::s{l_beh_warn_dnd},
974 $xl::s{h_beh_warn_dnd},
980 'outgoing_fallback_to_ascii',
981 $xl::s{l_beh_out_ascii},
982 $xl::s{h_beh_out_ascii},
988 'primary_paste_unselects',
989 $xl::s{l_beh_pp_unsel},
990 $xl::s{h_beh_pp_unsel},
996 'show_inline_attachments',
997 $xl::s{l_beh_inline_at},
998 $xl::s{h_beh_inline_at},
1004 'address_search_wildcard',
1005 $xl::s{l_beh_addr_swc},
1006 $xl::s{h_beh_addr_swc},
1012 'folder_search_wildcard',
1013 $xl::s{l_beh_fold_swc},
1014 $xl::s{h_beh_fold_swc},
1021 sub new_behaviour_page() {
1022 my $bf = Gtk2::VBox->new (FALSE, 5);
1023 $bf->set_border_width (PAGE_SPC);
1025 my $tb_up_step = new_text_box_for_int (\%pr::beh, 'up_step', \%HPVALUE);
1026 my $tb_thread_a = new_text_box_for_int (\%pr::beh, 'thread_a', \%HPVALUE);
1028 $bf->pack_start (new_subpage_frame (
1030 new_text_box_for_int (\%pr::beh, 'hover_t', \%HPVALUE),
1031 new_check_button_for (\%pr::beh, 'warn_dnd', \%HPVALUE)),
1032 $xl::s{dnd_frame}, 'not-packed'),
1033 FALSE, FALSE, FRAME_SPC);
1034 $bf->pack_start (new_subpage_frame (
1036 new_check_button_for (\%pr::beh, 'skip_ssl', \%HPVALUE),
1037 new_check_button_for (\%pr::beh, 'unsafe_ssl', \%HPVALUE)),
1038 $xl::s{ssl_frame}, 'not-packed'),
1039 FALSE, FALSE, FRAME_SPC);
1040 $bf->pack_start ($tb_up_step, FALSE, FALSE, 0);
1041 $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0);
1042 $bf->pack_start (new_subpage_frame (
1044 new_check_button_for (\%pr::beh, 'flowed', \%HPVALUE),
1045 new_check_button_for (\%pr::beh, 'parts_rw', \%HPVALUE),
1046 new_check_button_for (\%pr::beh, 'use_utf8', \%HPVALUE),
1047 new_check_button_for (\%pr::beh, 'dangerous', \%HPVALUE),
1048 new_check_button_for (\%pr::beh, 'out_ascii', \%HPVALUE),
1049 new_check_button_for (\%pr::beh, 'pp_unsel', \%HPVALUE),
1050 new_check_button_for (\%pr::beh, 'inline_at', \%HPVALUE)),
1051 $xl::s{msgs_frame}, 'not-packed'),
1052 FALSE, FALSE, FRAME_SPC);
1053 $bf->pack_start (new_subpage_frame (
1055 new_check_button_for (\%pr::beh, 'addr_swc', \%HPVALUE),
1056 new_check_button_for (\%pr::beh, 'fold_swc', \%HPVALUE)),
1057 $xl::s{swc_frame}, 'not-packed'),
1058 FALSE, FALSE, FRAME_SPC);
1062 %pr::col = ( # a variety of colours
1065 $xl::s{l_col_emphasis},
1066 $xl::s{h_col_emphasis},
1073 $xl::s{l_col_log_err},
1074 $xl::s{h_col_log_err},
1081 $xl::s{l_col_log_in},
1082 $xl::s{h_col_log_in},
1089 $xl::s{l_col_log_msg},
1090 $xl::s{h_col_log_msg},
1097 $xl::s{l_col_log_out},
1098 $xl::s{h_col_log_out},
1105 $xl::s{l_col_log_warn},
1106 $xl::s{h_col_log_warn},
1113 $xl::s{l_col_diff_add},
1114 $xl::s{h_col_diff_add},
1120 'diff_deleted_color',
1121 $xl::s{l_col_diff_del},
1122 $xl::s{h_col_diff_del},
1129 $xl::s{l_col_diff_hunk},
1130 $xl::s{h_col_diff_hunk},
1137 sub new_colours_page() {
1138 return new_vbox_pack (
1141 new_color_button_for (\%pr::col, 'emphasis', \%HPVALUE)),
1142 $xl::s{msgview_frame}, 'not-packed'),
1145 new_color_button_for (\%pr::col, 'log_err', \%HPVALUE),
1146 new_color_button_for (\%pr::col, 'log_in', \%HPVALUE),
1147 new_color_button_for (\%pr::col, 'log_msg', \%HPVALUE),
1148 new_color_button_for (\%pr::col, 'log_out', \%HPVALUE),
1149 new_color_button_for (\%pr::col, 'log_warn', \%HPVALUE)),
1150 $xl::s{log_frame}, 'not-packed'),
1153 new_color_button_for (\%pr::col, 'diff_add', \%HPVALUE),
1154 new_color_button_for (\%pr::col, 'diff_del', \%HPVALUE),
1155 new_color_button_for (\%pr::col, 'diff_hunk', \%HPVALUE)),
1156 $xl::s{diff_frame}, 'not-packed')
1160 %pr::win = ( # tweak window positions and/or sizes
1165 'int,0,3000', # 0 pixels - 3000 pixels
1173 'int,0,3000', # 0 pixels - 3000 pixels
1181 'int,0,3000', # 0 pixels - 3000 pixels
1189 'int,0,3000', # 0 pixels - 3000 pixels
1194 'mainwin_maximised',
1195 $xl::s{l_win_main_mx},
1196 $xl::s{h_win_main_mx},
1202 'mainwin_fullscreen',
1203 $xl::s{l_win_main_fs},
1204 $xl::s{h_win_main_fs},
1210 'main_messagewin_x',
1213 'int,0,3000', # 0 pixels - 3000 pixels
1218 'main_messagewin_y',
1221 'int,0,3000', # 0 pixels - 3000 pixels
1229 'int,0,3000', # 0 pixels - 3000 pixels
1234 'messagewin_height',
1237 'int,0,3000', # 0 pixels - 3000 pixels
1245 'int,0,3000', # 0 pixels - 3000 pixels
1253 'int,0,3000', # 0 pixels - 3000 pixels
1261 'int,0,3000', # 0 pixels - 3000 pixels
1266 'receivewin_height',
1269 'int,0,3000', # 0 pixels - 3000 pixels
1277 'int,0,3000', # 0 pixels - 3000 pixels
1285 'int,0,3000', # 0 pixels - 3000 pixels
1290 'folderitemwin_width',
1293 'int,0,3000', # 0 pixels - 3000 pixels
1298 'folderitemwin_height',
1301 'int,0,3000', # 0 pixels - 3000 pixels
1306 'folderselwin_width',
1309 'int,0,3000', # 0 pixels - 3000 pixels
1314 'folderselwin_height',
1317 'int,0,3000', # 0 pixels - 3000 pixels
1325 'int,0,3000', # 0 pixels - 3000 pixels
1333 'int,0,3000', # 0 pixels - 3000 pixels
1338 'addressbookwin_width',
1341 'int,0,3000', # 0 pixels - 3000 pixels
1346 'addressbookwin_height',
1349 'int,0,3000', # 0 pixels - 3000 pixels
1354 'addressbookeditpersonwin_width',
1357 'int,0,3000', # 0 pixels - 3000 pixels
1362 'addressbookeditpersonwin_height',
1365 'int,0,3000', # 0 pixels - 3000 pixels
1370 'addressbookeditgroupwin_width',
1373 'int,0,3000', # 0 pixels - 3000 pixels
1378 'addressbookeditgroupwin_height',
1381 'int,0,3000', # 0 pixels - 3000 pixels
1386 'addressaddwin_width',
1389 'int,0,3000', # 0 pixels - 3000 pixels
1394 'addressaddwin_height',
1397 'int,0,3000', # 0 pixels - 3000 pixels
1402 'addressbook_folderselwin_width',
1405 'int,0,3000', # 0 pixels - 3000 pixels
1410 'addressbook_folderselwin_height',
1413 'int,0,3000', # 0 pixels - 3000 pixels
1418 'editaccountwin_width',
1421 'int,0,3000', # 0 pixels - 3000 pixels
1426 'editaccountwin_height',
1429 'int,0,3000', # 0 pixels - 3000 pixels
1434 'accountswin_width',
1437 'int,0,3000', # 0 pixels - 3000 pixels
1442 'accountswin_height',
1445 'int,0,3000', # 0 pixels - 3000 pixels
1450 'filteringwin_width',
1453 'int,0,3000', # 0 pixels - 3000 pixels
1458 'filteringwin_height',
1461 'int,0,3000', # 0 pixels - 3000 pixels
1466 'filteringactionwin_width',
1469 'int,0,3000', # 0 pixels - 3000 pixels
1474 'filteringactionwin_height',
1477 'int,0,3000', # 0 pixels - 3000 pixels
1482 'filtering_debugwin_width',
1485 'int,0,3000', # 0 pixels - 3000 pixels
1490 'filtering_debugwin_height',
1493 'int,0,3000', # 0 pixels - 3000 pixels
1501 'int,0,3000', # 0 pixels - 3000 pixels
1506 'matcherwin_height',
1509 'int,0,3000', # 0 pixels - 3000 pixels
1517 'int,0,3000', # 0 pixels - 3000 pixels
1525 'int,0,3000', # 0 pixels - 3000 pixels
1530 'templateswin_width',
1533 'int,0,3000', # 0 pixels - 3000 pixels
1538 'templateswin_height',
1541 'int,0,3000', # 0 pixels - 3000 pixels
1549 'int,0,3000', # 0 pixels - 3000 pixels
1554 'actionswin_height',
1557 'int,0,3000', # 0 pixels - 3000 pixels
1565 'int,0,3000', # 0 pixels - 3000 pixels
1573 'int,0,3000', # 0 pixels - 3000 pixels
1581 'int,0,3000', # 0 pixels - 3000 pixels
1586 'pluginswin_height',
1589 'int,0,3000', # 0 pixels - 3000 pixels
1597 'int,0,3000', # 0 pixels - 3000 pixels
1605 'int,0,3000', # 0 pixels - 3000 pixels
1610 'print_previewwin_width',
1613 'int,0,3000', # 0 pixels - 3000 pixels
1618 'print_previewwin_height',
1621 'int,0,3000', # 0 pixels - 3000 pixels
1627 sub new_winpos_subpage_main() {
1628 return new_subpage_frame (
1629 new_vbox_pack_compact (
1631 new_text_box_for_int (\%pr::win, 'main_x', \%HPVALUE),
1632 new_text_box_for_int (\%pr::win, 'main_y', \%HPVALUE)),
1634 new_text_box_for_int (\%pr::win, 'main_w', \%HPVALUE),
1635 new_text_box_for_int (\%pr::win, 'main_h', \%HPVALUE)),
1637 new_check_button_for (\%pr::win, 'main_fs', \%HPVALUE),
1638 new_check_button_for (\%pr::win, 'main_mx', \%HPVALUE))),
1643 sub new_winpos_subpage_msgs() {
1644 return new_subpage_frame (
1645 new_vbox_pack_compact (
1647 new_text_box_for_int (\%pr::win, 'msgs_x', \%HPVALUE),
1648 new_text_box_for_int (\%pr::win, 'msgs_y', \%HPVALUE)),
1650 new_text_box_for_int (\%pr::win, 'msgs_w', \%HPVALUE),
1651 new_text_box_for_int (\%pr::win, 'msgs_h', \%HPVALUE))),
1656 sub new_winpos_subpage_sendrecv() {
1657 return new_vbox_pack (
1660 new_text_box_for_int (\%pr::win, 'send_w', \%HPVALUE),
1661 new_text_box_for_int (\%pr::win, 'send_h', \%HPVALUE)),
1662 _('Send window'), 'not-packed'),
1665 new_text_box_for_int (\%pr::win, 'recv_w', \%HPVALUE),
1666 new_text_box_for_int (\%pr::win, 'recv_h', \%HPVALUE)),
1667 _('Receive window'), 'not-packed')
1671 sub new_winpos_subpage_fold() {
1672 return new_vbox_pack (
1674 new_vbox_pack_compact (
1676 new_text_box_for_int (\%pr::win, 'fold_x', \%HPVALUE),
1677 new_text_box_for_int (\%pr::win, 'fold_y', \%HPVALUE)),
1679 new_text_box_for_int (\%pr::win, 'fold_w', \%HPVALUE),
1680 new_text_box_for_int (\%pr::win, 'fold_h', \%HPVALUE))),
1681 _('Folder window'), 'not-packed'),
1684 new_text_box_for_int (\%pr::win, 'fsel_w', \%HPVALUE),
1685 new_text_box_for_int (\%pr::win, 'fsel_h', \%HPVALUE)),
1686 _('Folder selection window'), 'not-packed')
1690 sub new_winpos_subpage_addrbook() {
1691 return new_vbox_pack (
1694 new_text_box_for_int (\%pr::win, 'addr_w', \%HPVALUE),
1695 new_text_box_for_int (\%pr::win, 'addr_h', \%HPVALUE)),
1696 _('Addressbook main window'), 'not-packed'),
1699 new_text_box_for_int (\%pr::win, 'adep_w', \%HPVALUE),
1700 new_text_box_for_int (\%pr::win, 'adep_h', \%HPVALUE)),
1701 _('Edit person window'), 'not-packed'),
1704 new_text_box_for_int (\%pr::win, 'adeg_w', \%HPVALUE),
1705 new_text_box_for_int (\%pr::win, 'adeg_h', \%HPVALUE)),
1706 _('Edit group window'), 'not-packed'),
1709 new_text_box_for_int (\%pr::win, 'adda_w', \%HPVALUE),
1710 new_text_box_for_int (\%pr::win, 'adda_h', \%HPVALUE)),
1711 _('Add address window'), 'not-packed'),
1714 new_text_box_for_int (\%pr::win, 'addf_w', \%HPVALUE),
1715 new_text_box_for_int (\%pr::win, 'addf_h', \%HPVALUE)),
1716 _('Folder select window'), 'not-packed')
1720 sub new_winpos_subpage_accounts() {
1721 return new_vbox_pack (
1724 new_text_box_for_int (\%pr::win, 'acco_w', \%HPVALUE),
1725 new_text_box_for_int (\%pr::win, 'acco_h', \%HPVALUE)),
1726 _('Accounts window'), 'not-packed'),
1729 new_text_box_for_int (\%pr::win, 'acce_w', \%HPVALUE),
1730 new_text_box_for_int (\%pr::win, 'acce_h', \%HPVALUE)),
1731 _('Edit account window'), 'not-packed')
1735 sub new_winpos_subpage_filtering() {
1736 return new_vbox_pack (
1739 new_text_box_for_int (\%pr::win, 'filt_w', \%HPVALUE),
1740 new_text_box_for_int (\%pr::win, 'filt_h', \%HPVALUE)),
1741 _('Filtering window'), 'not-packed'),
1744 new_text_box_for_int (\%pr::win, 'fila_w', \%HPVALUE),
1745 new_text_box_for_int (\%pr::win, 'fila_h', \%HPVALUE)),
1746 _('Filtering actions window'), 'not-packed'),
1749 new_text_box_for_int (\%pr::win, 'fild_w', \%HPVALUE),
1750 new_text_box_for_int (\%pr::win, 'fild_h', \%HPVALUE)),
1751 _('Filtering debug window'), 'not-packed'),
1754 new_text_box_for_int (\%pr::win, 'matc_w', \%HPVALUE),
1755 new_text_box_for_int (\%pr::win, 'matc_h', \%HPVALUE)),
1756 _('Matcher window'), 'not-packed')
1760 sub new_winpos_subpage_prefs() {
1761 return new_vbox_pack (
1764 new_text_box_for_int (\%pr::win, 'pref_w', \%HPVALUE),
1765 new_text_box_for_int (\%pr::win, 'pref_h', \%HPVALUE)),
1766 _('Preferences window'), 'not-packed'),
1769 new_text_box_for_int (\%pr::win, 'temp_w', \%HPVALUE),
1770 new_text_box_for_int (\%pr::win, 'temp_h', \%HPVALUE)),
1771 _('Templates window'), 'not-packed'),
1774 new_text_box_for_int (\%pr::win, 'acti_w', \%HPVALUE),
1775 new_text_box_for_int (\%pr::win, 'acti_h', \%HPVALUE)),
1776 _('Actions window'), 'not-packed'),
1779 new_text_box_for_int (\%pr::win, 'tags_w', \%HPVALUE),
1780 new_text_box_for_int (\%pr::win, 'tags_h', \%HPVALUE)),
1781 _('Tags window'), 'not-packed'),
1784 new_text_box_for_int (\%pr::win, 'plug_w', \%HPVALUE),
1785 new_text_box_for_int (\%pr::win, 'plug_h', \%HPVALUE)),
1786 _('Plugins window'), 'not-packed')
1790 sub new_winpos_subpage_misc() {
1791 return new_vbox_pack (
1794 new_text_box_for_int (\%pr::win, 'logw_w', \%HPVALUE),
1795 new_text_box_for_int (\%pr::win, 'logw_h', \%HPVALUE)),
1796 _('Log window'), 'not-packed'),
1799 new_text_box_for_int (\%pr::win, 'prin_w', \%HPVALUE),
1800 new_text_box_for_int (\%pr::win, 'prin_h', \%HPVALUE)),
1801 _('Print preview window'), 'not-packed'),
1804 new_text_box_for_int (\%pr::win, 'sour_w', \%HPVALUE),
1805 new_text_box_for_int (\%pr::win, 'sour_h', \%HPVALUE)),
1806 _('View source window'), 'not-packed')
1810 sub new_winpos_page() {
1811 my $winbook = Gtk2::Notebook->new;
1812 $winbook->set_tab_pos ('right');
1813 $winbook->append_page (new_winpos_subpage_main, _('Main'));
1814 $winbook->append_page (new_winpos_subpage_msgs, _('Message'));
1815 $winbook->append_page (new_winpos_subpage_sendrecv, _('Send/Receive'));
1816 $winbook->append_page (new_winpos_subpage_fold, _('Folder'));
1817 $winbook->append_page (new_winpos_subpage_addrbook, _('Addressbook'));
1818 $winbook->append_page (new_winpos_subpage_accounts, _('Accounts'));
1819 $winbook->append_page (new_winpos_subpage_filtering, _('Filtering'));
1820 $winbook->append_page (new_winpos_subpage_prefs, _('Preferences'));
1821 $winbook->append_page (new_winpos_subpage_misc, _('Other'));
1825 %pr::acc = ( # per account hidden preferences
1827 'gnutls_set_priority',
1828 $xl::s{l_acc_gtls_set},
1829 $xl::s{h_acc_gtls_set},
1836 $xl::s{l_acc_gtls_pri},
1837 $xl::s{h_acc_gtls_pri},
1844 sub new_account_subpage($) {
1846 return new_vbox_pack (
1849 new_check_button_for (\%pr::acc, 'tls_set', $ACHPVALUE{$akey}),
1850 new_text_box_for_nchar (\%pr::acc, 'tls_pri', $ACHPVALUE{$akey})),
1851 _('GnuTLS priority'), 'not-packed')
1855 sub new_accounts_page() {
1856 my $accbook = Gtk2::Notebook->new;
1857 $accbook->set_tab_pos ('right');
1859 $ACPREFS{$a}{'account_name'} cmp $ACPREFS{$b}{'account_name'}
1862 my $name = $ACPREFS{$_}{'account_name'};
1863 my $isdef = ($ACPREFS{$_}{'is_default'} eq '1');
1864 my $page = new_account_subpage ($_);
1865 $accbook->append_page ($page, $isdef? '<b>' . $name . '</b>': $name);
1867 my $label = $accbook->get_tab_label ($page);
1868 $label->set_use_markup (TRUE);
1874 %pr::plu = ( # plugins hidden preferences
1880 'int,0,3000', # 0 pixels - 3000 pixels
1889 'int,0,3000', # 0 pixels - 3000 pixels
1896 'autocompletion_limit',
1897 $xl::s{l_plu_gpg_alimit},
1898 $xl::s{h_plu_gpg_alimit},
1906 'manager_win_width',
1909 'int,0,3000', # 0 pixels - 3000 pixels
1915 'manager_win_height',
1918 'int,0,3000', # 0 pixels - 3000 pixels
1926 $xl::s{l_plu_lav_burl},
1927 $xl::s{h_plu_lav_burl},
1930 'http://cdn.libravatar.org/avatar',
1935 'filter_log_verbosity',
1936 $xl::s{l_plu_prl_flvb},
1937 $xl::s{h_plu_prl_flvb},
1938 '0=l_plu_prl_none;1=l_plu_prl_manual;2=l_plu_prl_action;3=l_plu_prl_match',
1945 sub new_plugins_page() {
1947 $frame{'AttRemover'} =
1950 new_text_box_for_int (\%pr::plu, 'arm_winw', $PLHPVALUE{'AttRemover'}),
1951 new_text_box_for_int (\%pr::plu, 'arm_winh', $PLHPVALUE{'AttRemover'})),
1952 _('Attachment remover'), 'not-packed');
1956 new_text_box_for_int (\%pr::plu, 'gpg_alimit', $PLHPVALUE{'GPG'})),
1957 _('GPG'), 'not-packed');
1958 $frame{'ManageSieve'} =
1961 new_text_box_for_int (\%pr::plu, 'msv_winw', $PLHPVALUE{'ManageSieve'}),
1962 new_text_box_for_int (\%pr::plu, 'msv_winh', $PLHPVALUE{'ManageSieve'})),
1963 _('Sieve manager'), 'not-packed');
1964 $frame{'Libravatar'} =
1967 new_text_box_for_nchar (\%pr::plu, 'lav_burl', $PLHPVALUE{'Libravatar'})),
1968 _('Libravatar'), 'not-packed');
1969 $frame{'PerlPlugin'} =
1972 new_selection_box_for (\%pr::plu, 'prl_flvb', $PLHPVALUE{'PerlPlugin'})),
1973 _('Perl'), 'not-packed');
1974 foreach (@PLUGINS) {
1975 $frame{$_}->set_sensitive (defined $PLHPVALUE{$_});
1977 return new_vbox_pack (
1978 $frame{'AttRemover'},
1980 $frame{'ManageSieve'},
1981 $frame{'Libravatar'},
1982 $frame{'PerlPlugin'});
1985 sub new_info_page() {
1986 my $t0 = Gtk2::Table->new (7, 2, FALSE);
1987 my $v = get_toolkit_versions ();
1989 'glib' => 'Perl-GLib',
1990 'glib-r' => _('GLib runtime'),
1991 'glib-b' => _('GLib built'),
1992 'gtk2' => 'Perl-GTK2',
1993 'gtk2-r' => _('GTK2 runtime'),
1994 'gtk2-b' => _('GTK2 built'),
1997 foreach (sort keys %$v) {
1998 if (defined $v->{$_}) {
1999 my $label = Gtk2::Label->new ($labels{$_});
2000 my $value = Gtk2::Label->new ('<b>' . $v->{$_} . '</b>');
2001 $label->set_alignment (0, 0.5);
2002 $value->set_alignment (0, 0.5);
2003 $value->set_use_markup (TRUE);
2004 $t0->attach ($label, 0, 1, $row, $row + 1, 'fill', 'shrink', 8, 6);
2005 $t0->attach ($value, 1, 2, $row, $row + 1, 'fill', 'shrink', 8, 6);
2009 my $t1 = Gtk2::Table->new (2, 2, FALSE);
2010 my @lbl = map { $_->set_alignment (0, 0.5); $_ } (
2011 Gtk2::Label->new (_('Binary')),
2012 Gtk2::Label->new (_('Configuration'))
2014 my $cfgv = $CONFIGDATA->{'Common'}{'config_version'};
2016 my @val = map { $_->set_alignment (0, 0.5); $_->set_use_markup (TRUE); $_ } (
2017 Gtk2::Label->new ('<b>' . $CLAWSV . '</b>'),
2018 Gtk2::Label->new ('<b>' . $cfgv . '</b>')
2020 for (my $i = 0; $i <= $#lbl; ++$i) {
2021 $t1->attach ($lbl[$i], 0, 1, $i, $i + 1, 'fill', 'shrink', 8, 6);
2022 $t1->attach ($val[$i], 1, 2, $i, $i + 1, 'fill', 'shrink', 8, 6);
2024 return new_vbox_pack (
2025 new_subpage_frame ($t0, _('Library versions'), 'not-packed'),
2026 new_subpage_frame ($t1, _('Claws Mail versions'), 'not-packed'));
2030 sub get_toolkit_versions {
2032 $versions{'glib'} = $Glib::VERSION;
2033 # version info stuff appeared in 1.040
2034 if ($Glib::VERSION >= 1.040) {
2035 $versions{'glib-b'} = join('.', Glib->GET_VERSION_INFO);
2036 $versions{'glib-r'} = join('.',
2037 &Glib::major_version, &Glib::minor_version, &Glib::micro_version);
2039 $versions{'gtk2'} = $Gtk2::VERSION;
2040 if ($Gtk2::VERSION >= 1.040) {
2041 $versions{'gtk2-b'} = join('.', Gtk2->GET_VERSION_INFO);
2042 $versions{'gtk2-r'} = join('.',
2043 &Gtk2::major_version, &Gtk2::minor_version, &Gtk2::micro_version);
2048 sub print_version() {
2049 print $xl::s{about_title} . "\n";
2050 print $xl::s{about_version} . " $VERSION\n";
2051 my $v = get_toolkit_versions ();
2052 print "Perl-GLib " . $v->{'glib'};
2053 print _(", built for ") . $v->{'glib-b'}
2054 . _(", running with ") . $v->{'glib-r'} if $v->{'glib-b'};
2056 print "Perl-GTK2 " . $v->{'gtk2'};
2057 print _(", built for ") . $v->{'gtk2-b'}
2058 . _(", running with ") . $v->{'gtk2-r'} if $v->{'gtk2-b'};
2060 my $clawsver = ($CLAWSV eq "") ?
2061 _("was not found!") :
2062 (_("returned version ") . $CLAWSV);
2063 print "Claws Mail " . $clawsver . "\n";
2066 # the command line help
2068 my $line = '-' x length ($xl::s{about_title}) . "\n";
2070 print $xl::s{about_title} . "\n";
2072 print _("Syntax:\n");
2073 print _(" clawsker [options]\n");
2074 print _("Options:\n");
2075 print _(" -a|--alternate-config-dir <dir> Uses <dir> as Claws Mail config dir.\n");
2076 print _(" -b|--verbose More messages on standard output.\n");
2077 print _(" -c|--clawsrc <file> Uses <file> as full resource name.\n");
2078 print _(" -h|--help Prints this help screen.\n");
2079 print _(" -r|--read-only Disables writing changes to disk.\n");
2080 print _(" -v|--version Prints version infos.\n");
2083 sub parse_command_line {
2085 $CLAWSV = get_claws_version ();
2087 GetOptions('h|help' => sub { print_help (); $cont = FALSE },
2088 'v|version' => sub { print_version (); $cont = FALSE },
2089 'b|verbose' => sub { $VERBOSE = TRUE },
2090 'r|read-only' => sub { $READONLY = TRUE },
2091 'u|use-claws-version=s' => \&opt_use_claws_version,
2092 'a|alternate-config-dir=s' => \&opt_alternate_config_dir,
2093 'c|clawsrc=s' => \&opt_clawsrc)
2094 or die _("try -h or --help for syntax.\n");
2097 my $msg = _("Error in options: {msg}\n", msg => $@);
2098 if (defined $ENV{'DISPLAY'} and $ENV{'DISPLAY'} ne '') {
2099 eval { Gtk2->init };
2100 error_dialog ($msg) unless $@;
2107 sub opt_use_claws_version {
2108 my ($name, $value) = @_;
2109 die _("Error: {opt} requires a dotted numeric value argument\n", opt => $name)
2110 unless ($value =~ /^[\d\.]+$/);
2114 sub opt_alternate_config_dir {
2115 my ($name, $value) = @_;
2116 die _("Error: '{dir}' is not a directory or does not exist\n", dir => $value)
2118 $CONFIGDIR = $value;
2119 $CONFIGDIR .= "/" unless ($CONFIGDIR =~ /.*\/$/);
2120 $ALTCONFIGDIR = TRUE;
2124 my ($name, $value) = @_;
2125 die _("Error: '{value}' is not a file or does not exist\n", value => $value)
2127 set_rc_filename ($value);
2130 # update the hidden preferences status from loaded values
2131 sub init_hidden_preferences {
2132 foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth, \%pr::win) {
2133 foreach my $key (keys %$hash) {
2134 $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]};
2140 sub init_ac_hidden_preferences {
2141 foreach my $akey (keys %ACPREFS) {
2142 foreach my $key (keys %pr::acc) {
2143 my $pname = $pr::acc{$key}[NAME];
2144 $ACHPVALUE{$akey}{$pname} = $ACPREFS{$akey}{$pname};
2150 sub init_plu_hidden_preferences {
2151 foreach my $key (keys %pr::plu) {
2152 my $plugin = $pr::plu{$key}[PLUGIN];
2153 my $pname = $pr::plu{$key}[NAME];
2154 if (defined $PLPREFS{$plugin}) {
2155 $PLHPVALUE{$plugin}{$pname} = $PLPREFS{$plugin}{$pname};
2161 # generic load/save resource files
2167 open (RCF, '<:encoding(utf8)', $rc)
2168 or die _("Error: opening '{file}' for reading", file => $rc) . ": $!";
2169 my $section = '_'; # default unnamed section
2174 if (/^\[([^\]]+)\]$/) { # new section
2176 die _("Error: duplicate section '{sect}' in resource file '{file}'\n",
2177 sect => $section, file => $rc) if ($data{$section});
2178 $data{$section} = {};
2179 $meta{$section}{'#'} = $line;
2181 elsif (/^([0-9a-z_]+)=(.*)$/) { # key=value
2182 $data{$section}{$1} = $2;
2183 $meta{$section}{$1} = $line;
2185 elsif (/^(.*)$/) { # lone value
2186 push (@{$data{$section}{'_'}}, $1);
2190 return (\%data, \%meta);
2194 my ($rc, $data, $meta) = @_;
2195 open (RCF, '>:utf8', $rc)
2196 or die _("Error: opening '{file}' for writing", file => $rc) . ": $!";
2197 my @sections = keys %$data;
2198 if (defined $meta) {
2200 $meta->{$a}{'#'} <=> $meta->{$b}{'#'}
2203 foreach my $section (@sections) {
2204 say RCF "[$section]";
2205 if (ref ($data->{$section}{'_'}) eq 'ARRAY') {
2206 foreach my $val (@{$data->{$section}{'_'}}) {
2210 my @keys = keys %{$data->{$section}};
2211 if (defined $meta) {
2213 $meta->{$section}{$a} <=> $meta->{$section}{$b}
2216 foreach my $key (@keys) {
2217 my $val = $data->{$section}{$key};
2218 say RCF "$key=$val";
2226 # load current status from disc
2227 sub load_preferences {
2228 my $rc = get_rc_filename ();
2229 log_message ("Loading preferences from $rc\n");
2230 return FALSE unless check_rc_file ($rc);
2231 return FALSE unless check_claws_not_running ();
2232 ($CONFIGDATA, $CONFIGMETA) = load_resource ($rc);
2233 foreach (keys %{$CONFIGDATA->{'Common'}}) {
2234 $PREFS{$_} = $CONFIGDATA->{'Common'}{$_};
2236 foreach my $plugin (@PLUGINS) {
2237 if (defined $CONFIGDATA->{$plugin}) {
2238 push (@AVPLUGINS, $plugin);
2239 foreach (keys %{$CONFIGDATA->{$plugin}}) {
2240 $PLPREFS{$plugin}{$_} = $CONFIGDATA->{$plugin}{$_};
2247 sub load_ac_preferences {
2248 my $rc = get_ac_rc_filename ();
2249 log_message ("Loading account preferences from $rc\n");
2250 return FALSE unless check_rc_file ($rc);
2251 return FALSE unless check_claws_not_running ();
2252 ($ACCOUNTDATA, $ACCOUNTMETA) = load_resource ($rc);
2253 foreach my $asect (keys %$ACCOUNTDATA) {
2254 if ($asect =~ /^Account: (\d+)$/) {
2255 foreach (keys %{$ACCOUNTDATA->{$asect}}) {
2256 $ACPREFS{$1}{$_} = $ACCOUNTDATA->{$asect}{$_};
2263 # save current preferences to disc
2264 sub save_preferences {
2265 my $rc = get_rc_filename ();
2266 log_message ("Saving preferences to $rc\n");
2267 return FALSE unless check_rc_file ($rc);
2268 return FALSE unless check_claws_not_running ();
2269 my $rcbak = "$rc.backup";
2270 rename ($rc, $rcbak);
2271 foreach (keys %PREFS) {
2272 if (defined $HPVALUE{$_}) {
2273 $CONFIGDATA->{'Common'}{$_} = $HPVALUE{$_};
2276 foreach my $plugin (@AVPLUGINS) {
2277 foreach (keys %{$CONFIGDATA->{$plugin}}) {
2278 if (defined $PLHPVALUE{$plugin}{$_}) {
2279 $CONFIGDATA->{$plugin}{$_} = $PLHPVALUE{$plugin}{$_};
2283 save_resource ($rc, $CONFIGDATA, $CONFIGMETA);
2287 sub save_ac_preferences {
2288 my $rc = get_ac_rc_filename ();
2289 log_message ("Saving account preferences to $rc\n");
2290 return FALSE unless check_rc_file ($rc);
2291 return FALSE unless check_claws_not_running ();
2292 my $rcbak = "$rc.backup";
2293 rename ($rc, $rcbak);
2294 foreach my $asect (keys %$ACCOUNTDATA) {
2295 if ($asect =~ /^Account: (\d+)$/) {
2296 foreach (keys %{$ACCOUNTDATA->{$asect}}) {
2297 if (defined $ACHPVALUE{$1}{$_}) {
2298 $ACCOUNTDATA->{$asect}{$_} = $ACHPVALUE{$1}{$_};
2303 save_resource ($rc, $ACCOUNTDATA, $ACCOUNTMETA);
2309 my $nb = Gtk2::Notebook->new;
2311 $nb->append_page (new_behaviour_page (), $xl::s{tab_behaviour});
2312 $nb->append_page (new_colours_page (), $xl::s{tab_colours});
2313 $nb->append_page (new_gui_page (), $xl::s{tab_gui});
2314 $nb->append_page (new_other_page (), $xl::s{tab_other});
2315 $nb->append_page (new_winpos_page (), $xl::s{tab_winpos});
2316 $nb->append_page (new_accounts_page (), $xl::s{tab_accounts});
2317 $nb->append_page (new_plugins_page (), $xl::s{tab_plugins});
2318 $nb->append_page (new_info_page (), $xl::s{tab_info});
2323 # create an about dialog
2324 sub new_about_dialog {
2326 my $title = $xl::s{about_title};
2327 my $lic = $xl::s{about_license};
2328 my $vers = $xl::s{about_version} . " $VERSION";
2330 "This program is free software: you can redistribute it and/or modify
2331 it under the terms of the GNU General Public License as published by
2332 the Free Software Foundation, either version 3 of the License, or
2333 (at your option) any later version.
2335 This program is distributed in the hope that it will be useful,
2336 but WITHOUT ANY WARRANTY; without even the implied warranty of
2337 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2338 GNU General Public License for more details.
2340 You should have received a copy of the GNU General Public License
2341 along with this program. If not, see <http://www.gnu.org/licenses/>.";
2342 my $year = "2007-2016";
2343 my $holder = "Ricardo Mones <ricardo\@mones.org>";
2344 my $url = "http://www.claws-mail.org/clawsker.php";
2346 my $dialog = Gtk2::MessageDialog->new_with_markup ($parent,
2347 [qw/modal destroy-with-parent/],
2349 "<span size=\"x-large\" weight=\"bold\">$title</span>\n"
2350 . "<span size=\"large\">$vers</span>\n\n"
2351 . "<span color=\"blue\" size=\"large\">$url</span>\n\n"
2352 . "<span>Copyright $year by $holder</span>\n\n"
2353 . "<span size=\"large\">$lic</span>\n\n"
2354 . "<span size=\"small\">$license</span>");
2355 $dialog->set_title ($xl::s{about});
2360 # create buttons box
2361 sub new_button_box {
2362 my ($parent, $adlg) = @_;
2363 my $b_about = Gtk2::Button->new_from_stock ('gtk-about');
2364 my $b_exit = Gtk2::Button->new_from_stock ('gtk-quit');
2365 my $b_apply = Gtk2::Button->new_from_stock ('gtk-apply');
2366 # disable button until is really implemented
2367 # my $b_undo = Gtk2::Button->new_from_stock ('gtk-undo');
2368 my $hbox = Gtk2::HBox->new (FALSE, 5);
2370 $b_exit->signal_connect (clicked => sub { Gtk2->main_quit });
2371 $b_apply->set_sensitive (not $READONLY);
2372 $b_apply->signal_connect (clicked => sub {
2373 save_preferences ($parent);
2374 save_ac_preferences ($parent);
2376 # $b_undo->signal_connect (clicked => sub { undo_current_changes });
2377 $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
2379 $hbox->pack_end ($b_apply, FALSE, FALSE, 0);
2380 $hbox->pack_end ($b_exit, FALSE, FALSE, 0);
2381 # $hbox->pack_end ($b_undo, FALSE, FALSE, 0);
2382 $hbox->pack_start ($b_about, FALSE, FALSE, 0);
2388 my $dir = $DATADIR . '/icons/hicolor';
2390 join ('/', ($dir, , $_ . 'x' . $_, 'apps', $NAME . '.png'))
2395 $icon = Gtk2::Gdk::Pixbuf->new_from_file($_) if (-f $_);
2396 push @icons, $icon if ($icon);
2402 exit unless parse_command_line ();
2404 $main_window = Gtk2::Window->new ('toplevel');
2405 exit unless load_preferences ();
2406 exit unless load_ac_preferences ();
2407 exit unless init_hidden_preferences ();
2408 exit unless init_ac_hidden_preferences ();
2409 exit unless init_plu_hidden_preferences ();
2411 my $box = Gtk2::VBox->new (FALSE, 5);
2412 $box->set_border_width(3);
2413 my $about = new_about_dialog ();
2414 $box->pack_start (new_notebook (), FALSE, FALSE, 0);
2415 $box->pack_end (new_button_box ($main_window, $about), FALSE, FALSE, 0);
2416 $main_window->signal_connect (delete_event => sub { Gtk2->main_quit });
2417 $main_window->set_title ($xl::s{win_title});
2418 $main_window->set_icon_list (get_app_icons ());
2419 $main_window->add ($box);
2420 $main_window->show_all;