3 # Clawsker :: A Claws Mail Tweaker
4 # Copyright 2007-2018 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)";
21 use Glib qw(TRUE FALSE);
23 use POSIX qw(setlocale);
26 use Digest::MD5 qw(md5_hex);
29 my $NAME = 'clawsker';
30 my $PREFIX = '@PREFIX@';
31 my $LIBDIR = '@LIBDIR@';
32 my $DATADIR = '@DATADIR@';
33 my $VERSION = '@VERSION@';
37 my $main_window = undef;
39 my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG});
40 $locale = "C" unless defined($locale);
41 setlocale (LC_ALL, $locale);
42 bindtextdomain ($NAME, sprintf ('%s/share/locale', $PREFIX));
48 my $xla = gettext ($str);
49 if (scalar(keys(%par)) > 0) {
50 foreach my $key (keys %par) {
51 $xla =~ s/\{$key\}/$par{$key}/g;
54 return decode_utf8($xla);
59 win_title => _('Claws Mail Hidden Preferences'),
60 about_title => _('Clawsker :: A Claws Mail Tweaker'),
61 about_web_label => _("Visit Clawsker's web page"),
63 error_title => _('Clawsker error'),
65 exit_title => _('Clawsker warning'),
66 exit_fact => _('There are unapplied modifications.'),
67 exit_question => _('Do you really want to quit?'),
69 tab_colours => _('Colours'),
70 tab_behaviour => _('Behaviour'),
72 tab_other => _('Other'),
73 tab_winpos => _('Windows'),
74 tab_accounts => _('Accounts'),
75 tab_plugins => _('Plugins'),
76 tab_hotkeys => _('Hotkeys'),
77 tab_info => _('Info'),
79 dnd_frame => _('Drag \'n\' drop'),
80 ssl_frame => _('Secure Sockets Layer'),
81 msgs_frame => _('Messages'),
82 swc_frame => _('Completion'),
84 l_oth_use_dlg => _('Use detached address book edit dialogue'),
85 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.'),
86 l_oth_max_use => _('Maximum memory for message cache'),
87 l_oth_max_use_units => _('kilobytes'),
88 h_oth_max_use => _('The maximum amount of memory to use to cache messages, in kilobytes.'),
89 l_oth_min_time => _('Minimun time for cache elements'),
90 l_oth_min_time_units => _('minutes'),
91 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.'),
92 l_oth_use_netm => _('Use NetworkManager'),
93 h_oth_use_netm => _('Use NetworkManager to switch offline automatically.'),
94 l_oth_mp_rounds => _('Rounds for PBKDF2 function'),
95 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.'),
97 l_gui_b_unread => _('Show unread messages with bold font'),
98 h_gui_b_unread => _('Show unread messages in the Message List using a bold font.'),
99 l_gui_no_markup => _('Don\'t use markup'),
100 h_gui_no_markup => _('Don\'t use bold and italic text in Compose dialogue\'s account selector.'),
101 l_gui_dot_lines => _('Use dotted lines in tree view components'),
102 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.'),
103 l_gui_h_scroll => _('Enable horizontal scrollbar'),
104 h_gui_h_scroll => _('Enable the horizontal scrollbar in the Message List.'),
105 l_gui_swp_from => _('Display To column instead From column in Sent folder'),
106 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.'),
107 l_gui_v_scroll => _('Folder List scrollbar behaviour'),
108 h_gui_v_scroll => _('Specify the policy of vertical scrollbar of Folder List: show always, automatic or hide always.'),
109 l_gui_v_scroll_show => _('Show always'),
110 l_gui_v_scroll_auto => _('Automatic'),
111 l_gui_v_scroll_hide => _('Hide always'),
112 l_gui_from_show => _('From column displays'),
113 h_gui_from_show => _('Selects the data displayed in the From column of the Message List: name, address or both.'),
114 l_gui_from_show_name => _('Name only'),
115 l_gui_from_show_addr => _('Address only'),
116 l_gui_from_show_both => _('Both name and address'),
117 l_gui_strip_off => _('Coloured lines contrast'),
118 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.'),
119 l_gui_cursor_v => _('Show cursor'),
120 h_gui_cursor_v => _('Display the cursor in the Message View.'),
121 l_gui_toolbar_d => _('Detachable toolbars'),
122 h_gui_toolbar_d => _('Show handles in the toolbars.'),
123 l_gui_strip_all => _('Use stripes in all tree view components'),
124 h_gui_strip_all => _('Enable alternately coloured lines in all tree view components.'),
125 l_gui_strip_sum => _('Use stripes in Folder List and Message List'),
126 h_gui_strip_sum => _('Enable alternately coloured lines in Message List and Folder List.'),
127 l_gui_two_line_v => _('2 lines per Message List item in 3-column layout'),
128 h_gui_two_line_v => _('Spread Message List information over two lines when using the three column mode.'),
129 l_gui_margin_co => _('Show margin'),
130 h_gui_margin_co => _('Shows a small margin in the Compose View.'),
131 l_gui_mview_date => _('Don\'t display localized date'),
132 h_gui_mview_date => _('Toggles localization of date format in Message View.'),
133 l_gui_zero_char => _('Zero replacement character'),
134 h_gui_zero_char => _('Replaces \'0\' with the given character in Folder List.'),
135 l_gui_type_any => _('Editable headers'),
136 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.'),
137 l_gui_warn_send_multi => _('Warn when sending to more than'),
138 l_gui_warn_send_multi_units => _('recipients'),
139 h_gui_warn_send_multi => _('Show a warning dialogue when sending to more recipients than specified. Use 0 to disable this check.'),
140 l_gui_next_del => _('Select next message on delete'),
141 h_gui_next_del => _('When deleting a message, toggles between selecting the next one (newer message) or the previous one (older message).'),
143 l_beh_hover_t => _('Drag \'n\' drop hover timeout'),
144 l_beh_hover_t_units => _('milliseconds'),
145 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.'),
146 l_beh_dangerous => _('Don\'t confirm deletions (dangerous!)'),
147 h_beh_dangerous => _('Don\'t ask for confirmation before definitive deletion of emails.'),
148 l_beh_flowed => _('Respect format=flowed in messages'),
149 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.'),
150 l_beh_parts_rw => _('Allow writable temporary files'),
151 h_beh_parts_rw => _('Saves temporary files when opening attachment with write bit set.'),
152 l_beh_skip_ssl => _('Don\'t check SSL certificates'),
153 h_beh_skip_ssl => _('Disables the verification of SSL certificates.'),
154 l_beh_up_step => _('Progress bar update step every'),
155 l_beh_up_step_units => _('items'),
156 h_beh_up_step => _('Update stepping in progress bars.'),
157 l_beh_thread_a => _('Maximum age when threading by subject'),
158 l_beh_thread_a_units => _('days'),
159 h_beh_thread_a => _('Number of days to include a message in a thread when using "Thread using subject in addition to standard headers".'),
160 l_beh_unsafe_ssl => _('Allow unsafe SSL certificates'),
161 h_beh_unsafe_ssl => _('Allows Claws Mail to remember multiple SSL certificates for a given server/port.'),
162 l_beh_use_utf8 => _('Force UTF-8 for broken mails'),
163 h_beh_use_utf8 => _('Use UTF-8 encoding for broken mails instead of current locale.'),
164 l_beh_warn_dnd => _('Warn on drag \'n\' drop'),
165 h_beh_warn_dnd => _('Display a confirmation dialogue on drag \'n\' drop of folders.'),
166 l_beh_out_ascii => _('Outgoing messages fallback to ASCII'),
167 h_beh_out_ascii => _('If allowed by content, ASCII will be used to encode outgoing messages, otherwise the user-defined encoding is always enforced.'),
168 l_beh_pp_unsel => _('Primary paste unselects selection'),
169 h_beh_pp_unsel => _('Controls how pasting using middle-click changes the selected text and insertion point.'),
170 l_beh_inline_at => _('Show inline attachments'),
171 h_beh_inline_at => _('Allows to hide inline attachments already shown in mail structure view.'),
172 l_beh_addr_swc => _('Address search in compose window matches any'),
173 h_beh_addr_swc => _('On Tab-key completion, address text will match any part of the string or only from the start.'),
174 l_beh_fold_swc => _('Folder search in folder selector matches any'),
175 h_beh_fold_swc => _('On folder name completion text will match any part of the string or only from the start.'),
176 l_beh_rewrite_ff => _('Rewrite first \'From\' using QP encoding'),
177 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 l_col_emphasis => _('X-Mailer header'),
180 h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
181 l_col_log_err => _('Error messages'),
182 h_col_log_err => _('Colour for error messages in log window.'),
183 l_col_log_in => _('Server messages'),
184 h_col_log_in => _('Colour for messages received from servers in log window.'),
185 l_col_log_msg => _('Standard messages'),
186 h_col_log_msg => _('Colour for messages in log window.'),
187 l_col_log_out => _('Client messages'),
188 h_col_log_out => _('Colour for messages sent to servers in log window.'),
189 l_col_log_warn => _('Warning messages'),
190 h_col_log_warn => _('Colour for warning messages in log window.'),
192 l_col_tags_bg => _('Tags background'),
193 h_col_tags_bg => _('Background colour for tags in message view.'),
194 l_col_tags_text => _('Tags text'),
195 h_col_tags_text => _('Text colour for tags in message view.'),
197 l_col_default_header_bg => _('Default headers background'),
198 h_col_default_header_bg => _('Background colour for default headers in compose window.'),
199 l_col_default_header_text => _('Default headers text'),
200 h_col_default_header_text => _('Text colour for default headers in compose window.'),
202 l_col_qs_active_bg => _('Active quick search background'),
203 h_col_qs_active_bg => _('Background colour for active quick search.'),
204 l_col_qs_active_text => _('Active quick search text'),
205 h_col_qs_active_text => _('Text colour for active quick search.'),
206 l_col_qs_error_bg => _('Quick search error background'),
207 h_col_qs_error_bg => _('Background colour for quick search error.'),
208 l_col_qs_error_text => _('Quick search error text'),
209 h_col_qs_error_text => _('Text colour for quick search error.'),
211 l_col_diff_add => _('Added lines'),
212 h_col_diff_add => _('Colour for added lines in patches.'),
213 l_col_diff_del => _('Deleted lines'),
214 h_col_diff_del => _('Colour for deleted lines in patches.'),
215 l_col_diff_hunk => _('Hunk lines'),
216 h_col_diff_hunk => _('Colour for hunk headers in patches.'),
218 l_win_x => _('X position'),
219 h_win_x => _('X coordinate for window\'s top-left corner.'),
220 l_win_y => _('Y position'),
221 h_win_y => _('Y coordinate for window\'s top-left corner.'),
222 l_win_w => _('Width'),
223 h_win_w => _('Window\'s width in pixels.'),
224 l_win_h => _('Height'),
225 h_win_h => _('Window\'s height in pixels.'),
227 l_win_main_mx => _('Maximized'),
228 h_win_main_mx => _('Changes window maximized status.'),
229 l_win_main_fs => _('Full-screen'),
230 h_win_main_fs => _('Changes full screen status.'),
232 l_acc_gtls_set => _('Use custom GnuTLS priority'),
233 h_acc_gtls_set => _('Enables using user provided GnuTLS priority string.'),
234 l_acc_gtls_pri => _('GnuTLS priority'),
235 h_acc_gtls_pri => _('Value to use as GnuTLS priority string if custom priority check is enabled. Otherwise this value is ignored.'),
237 l_plu_gpg_alimit => _('Autocompletion limit'),
238 h_plu_gpg_alimit => _('Limits the number of addresses obtained from keyring through autocompletion. Use 0 to get all matches.'),
239 l_plu_lav_burl => _('Base URL'),
240 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.'),
241 l_plu_prl_flvb => _('Log level'),
242 h_plu_prl_flvb => _('Verbosity level of log, accumulative.'),
243 l_plu_prl_none => _('None'),
244 l_plu_prl_manual => _('Manual'),
245 l_plu_prl_action => _('Actions'),
246 l_plu_prl_match => _('Matches'),
249 # data and metadata of resource files
254 # all preferences read by load_preferences
258 # values of all preferences handled by clawsker
262 # default config dir and file name
263 my $ALTCONFIGDIR = FALSE;
264 my $CONFIGDIR = $ENV{HOME} . '/.claws-mail/';
265 my $CONFIGRC = 'clawsrc';
266 my $ACCOUNTRC = 'accountrc';
267 # supported and available plugins lists
268 my @PLUGINS = qw(AttRemover GPG ManageSieve Libravatar PerlPlugin);
270 # loaded hotkeys from load_menurc
272 # current tree selection
279 # index constants for preference arrays
280 use constant NAME => 0; # the name on the rc file
281 use constant LABEL => 1; # the label on the GUI
282 use constant DESC => 2; # the description for the hint/help
283 use constant TYPE => 3; # data type: bool, int, float, string, color
284 use constant CMVER => 4; # lowest[,highest] Claws Mail version(s) the feature exists
285 use constant CMDEF => 5; # default value for the preference in Claws Mail
286 use constant PLUGIN => 6; # plugin section (only in plugin preferences)
288 # constants for GUI spacing
289 use constant HBOX_PAD => 5;
290 use constant FRAME_SPC => 2;
291 use constant PAGE_SPC => 5;
293 # for data references indexing
294 use constant VALUE => 0;
295 use constant IVALUE => 1;
299 sub version_greater_or_equal {
300 my ($version, $refvers) = @_;
301 return TRUE if (length($version) == 0 and length($refvers) >= 0);
302 return FALSE if (length($version) >= 0 and length($refvers) == 0);
303 return TRUE if (version->parse($version) >= version->parse($refvers));
307 sub get_claws_version {
308 $_ = qx/which claws-mail/;
310 return "" unless ($_); # not installed
314 my @fver = split (/ /);
315 die "Invalid version string" unless ($fver[2] eq "version");
316 my @ver = split (/\./, $fver[3]);
319 if ($ver[2] =~ /(\d+)git(\d+)/) {
328 # data handlers and auxiliar functions
330 sub handle_bool_value {
331 my ($widget, $event, $dataref) = @_;
332 $$dataref->[VALUE] = ($widget->get_active ())? '1': '0';
333 $MODIFIED += $$dataref->[VALUE] != $$dataref->[IVALUE]? 1: -1
334 if defined $$dataref->[IVALUE];
337 sub handle_int_value {
338 my ($widget, $event, $dataref) = @_;
339 $_ = $widget->get_text ();
343 $$dataref->[VALUE] = $_;
344 $widget->set_text ($_);
347 $widget->set_text ($$dataref->[VALUE]);
349 $MODIFIED += $$dataref->[VALUE] != $$dataref->[IVALUE]? 1: -1
350 if defined $$dataref->[IVALUE];
353 sub handle_string_value {
354 my ($widget, $event, $dataref) = @_;
355 $$dataref->[VALUE] = $widget->get_text ();
356 $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
357 if defined $$dataref->[IVALUE];
360 sub handle_nchar_value {
361 my ($widget, $event, $dataref, $minlen, $maxlen) = @_;
362 $_ = substr ($widget->get_text (), 0, $maxlen);
363 $widget->set_text ($_);
364 $$dataref->[VALUE] = $_;
365 $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
366 if defined $$dataref->[IVALUE];
369 sub gdk_rgba_from_str {
371 my ($rr, $gg, $bb) = (0, 0 ,0);
373 if (/\#([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])/) {
378 my $color = Gtk3::Gdk::RGBA->new ($rr, $gg, $bb, 1.0);
382 sub str_from_gdk_rgba {
384 my $rr = $color->red * 256;
385 my $gg = $color->green * 256;
386 my $bb = $color->blue * 256;
387 my $str = sprintf ("#%.2x%.2x%.2x", $rr, $gg, $bb);
391 sub handle_color_value {
392 my ($widget, $event, $dataref) = @_;
393 my $newcol = $widget->get_rgba;
394 $$dataref->[VALUE] = str_from_gdk_rgba ($newcol);
395 $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
396 if defined $$dataref->[IVALUE];
399 sub handle_selection_value {
400 my ($widget, $event, $dataref) = @_;
401 $$dataref->[VALUE] = $widget->get_active;
402 $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
403 if defined $$dataref->[IVALUE];
406 sub get_rc_filename {
407 return $CONFIGDIR . $CONFIGRC;
410 sub get_ac_rc_filename {
411 return $CONFIGDIR . $ACCOUNTRC;
414 sub get_menurc_filename {
415 return $CONFIGDIR . "menurc";
418 sub set_rc_filename {
420 my @parts = split ('/', $fullname);
421 $CONFIGRC = $parts[$#parts];
422 $parts[$#parts] = '';
423 $CONFIGDIR = join ('/', @parts);
427 my ($mesg, $fatal) = @_;
428 if (defined($fatal) && $fatal eq 'die') {
429 die "$NAME: $mesg\n";
432 print "$NAME: $mesg\n";
437 my ($parent, $title, $markup, $type, $buttons) = @_;
438 my $flags = [qw/modal destroy-with-parent/];
439 my $dialog = Gtk3::Dialog->new_with_buttons (
440 $title, $parent, $flags, @$buttons
442 my $label = Gtk3::Label->new;
443 $label->set_markup ($markup);
445 if ($type eq 'error') {
446 $icon = Gtk3::Image->new_from_icon_name('dialog-error', 'GTK_ICON_SIZE_DIALOG');
447 } elsif ($type eq 'warning') {
448 $icon = Gtk3::Image->new_from_icon_name('dialog-warning', 'GTK_ICON_SIZE_DIALOG');
449 } elsif ($type eq 'question') {
450 $icon = Gtk3::Image->new_from_icon_name('dialog-question', 'GTK_ICON_SIZE_DIALOG');
452 my $hbox = Gtk3::Box->new ('horizontal', 5);
453 $hbox->pack_start ($icon, FALSE, FALSE, 5) if defined $icon;
454 $hbox->pack_start ($label, FALSE, FALSE, 5);
455 my $dialogbox = $dialog->get_content_area;
456 $dialogbox->add ($hbox);
457 $dialogbox->show_all;
463 my $markup = "<span weight=\"bold\" size=\"large\">" . $emsg . "</span>";
464 my $errordlg = message_dialog (
465 $main_window, $xl::s{error_title}, $markup, 'error', [ 'gtk-cancel', 0 ]
471 sub claws_is_running {
472 my $emsg = _('Error: seems Claws Mail is currently running, close it first.');
474 error_dialog ($emsg);
478 sub check_claws_not_running {
479 return TRUE if $READONLY;
480 my $tmpdir = (defined $ENV{TMPDIR})? $ENV{TMPDIR}: '/tmp';
481 $tmpdir = '/tmp' if ($tmpdir eq '');
482 my $lockdir = "$tmpdir/claws-mail-$<";
486 my $socket = "$lockdir/" . md5_hex ($_);
487 -S $socket and return claws_is_running ();
494 (defined($rcfile) && -f $rcfile) or do {
495 my $emsg = _('Error: resource file for Claws Mail was not found.');
497 error_dialog ($emsg);
503 sub set_widget_hint {
504 my ($wdgt, $hint) = @_;
505 $wdgt->set_tooltip_text ($hint);
506 $wdgt->set_has_tooltip (TRUE);
509 sub set_widget_sens {
510 my ($wdgt, $versions) = @_;
511 my @ver = split(/,/, $versions);
513 $wdgt->set_sensitive (
514 version_greater_or_equal ($CLAWSV, $ver[0])
515 and version_greater_or_equal ($ver[1], $CLAWSV)
518 $wdgt->set_sensitive (version_greater_or_equal ($CLAWSV, $ver[0]));
522 # graphic element creation
524 sub new_hbox_spaced_pack {
525 my $hbox = Gtk3::HBox->new (FALSE);
527 $hbox->pack_start ($_, FALSE, FALSE, HBOX_PAD);
532 sub new_check_button_for($$$) {
533 my ($hash, $key, $vhash) = @_;
534 my $name = $$hash{$key}[NAME];
535 my $label = $$hash{$key}[LABEL];
537 my $cb = Gtk3::CheckButton->new ($label);
538 my $value = $$vhash{$name}[VALUE];
539 $value //= $$hash{$key}[CMDEF];
540 $cb->set_active ($value eq '1');
541 $cb->signal_connect (clicked => sub {
543 handle_bool_value ($w, $e, \$$vhash{$name});
545 set_widget_hint ($cb, $$hash{$key}[DESC]);
546 set_widget_sens ($cb, $$hash{$key}[CMVER]);
548 return new_hbox_spaced_pack ($cb);
551 sub new_text_box_for_int($$$) {
552 my ($hash, $key, $vhash) = @_;
553 my $name = $$hash{$key}[NAME];
554 my $label = $$hash{$key}[LABEL];
555 my @type = split (/,/, $$hash{$key}[TYPE]);
556 push (@type, 0), push (@type, 10000) unless ($#type > 0);
559 if (ref $label eq 'ARRAY') {
560 $gunits = Gtk3::Label->new ($label->[1]);
561 $label = $label->[0];
563 my $glabel = Gtk3::Label->new ($label);
564 my $pagei = int (($type[2] - $type[1]) / 10);
565 my $gentry = Gtk3::SpinButton->new_with_range ($type[1], $type[2], $pagei);
566 my $value = $$vhash{$name}[VALUE];
567 $value //= $$hash{$key}[CMDEF];
568 $gentry->set_numeric (TRUE);
569 $gentry->set_value ($value);
570 $gentry->signal_connect('value-changed' => sub {
572 handle_int_value ($w, $e, \$$vhash{$name});
574 set_widget_hint ($gentry, $$hash{$key}[DESC]);
575 set_widget_sens ($gentry, $$hash{$key}[CMVER]);
576 $glabel->set_sensitive ($gentry->get_sensitive);
577 $gunits->set_sensitive ($gentry->get_sensitive) if ($gunits);
579 return new_hbox_spaced_pack ($glabel, $gentry, $gunits) if ($gunits);
580 return new_hbox_spaced_pack ($glabel, $gentry);
583 sub new_text_box_for_nchar($$$) {
584 my ($hash, $key, $vhash) = @_;
585 my $name = $$hash{$key}[NAME];
586 my $label = $$hash{$key}[LABEL];
587 my @type = split (/,/, $$hash{$key}[TYPE]); # char,minlen,maxlen,width
588 my $glabel = Gtk3::Label->new ($label);
589 my $gentry = Gtk3::Entry->new ();
590 $gentry->set_max_length($type[2]) if defined ($type[2]);
591 my $width = $type[3];
593 $gentry->set_width_chars(int ($width) + 2) if defined ($width);
594 my $value = $$vhash{$name}[VALUE];
595 $value //= $$hash{$key}[CMDEF];
596 $gentry->set_text ($value);
597 $gentry->signal_connect('key-release-event' => sub {
599 handle_nchar_value ($w, $e, \$$vhash{$name}, $type[1], $type[2]);
601 set_widget_hint ($gentry, $$hash{$key}[DESC]);
602 set_widget_sens ($gentry, $$hash{$key}[CMVER]);
603 $glabel->set_sensitive ($gentry->get_sensitive);
605 return new_hbox_spaced_pack ($glabel, $gentry);
608 sub new_color_button_for($$$) {
609 my ($hash, $key, $vhash) = @_;
610 my $name = $$hash{$key}[NAME];
611 my $label = $$hash{$key}[LABEL];
613 my $value = $$vhash{$name}[VALUE];
614 $value //= $$hash{$key}[CMDEF];
615 my $col = gdk_rgba_from_str ($value);
616 my $glabel = Gtk3::Label->new ($label);
617 my $button = Gtk3::ColorButton->new_with_rgba ($col);
618 $button->set_title ($label);
619 $button->set_relief ('none');
620 $button->signal_connect ('color-set' => sub {
622 handle_color_value ($w, $e, \$$vhash{$name});
624 set_widget_hint ($button, $$hash{$key}[DESC]);
625 set_widget_sens ($button, $$hash{$key}[CMVER]);
626 $glabel->set_sensitive ($button->get_sensitive);
628 return new_hbox_spaced_pack ($button, $glabel);
631 sub new_selection_box_for($$$) {
632 my ($hash, $key, $vhash) = @_;
633 my $name = $$hash{$key}[NAME];
634 my $label = $$hash{$key}[LABEL];
636 my $glabel = Gtk3::Label->new ($label);
637 my $combo = Gtk3::ComboBoxText->new;
638 my @options = split (';', $$hash{$key}[TYPE]);
639 foreach my $opt (@options) {
640 my ($index, $textkey) = split ('=', $opt);
641 $combo->insert (-1, $index, $xl::s{$textkey});
643 $combo->signal_connect ('changed' => sub {
645 handle_selection_value ($w, $e, \$$vhash{$name});
647 my $value = $$vhash{$name}[VALUE];
648 $value //= $$hash{$key}[CMDEF];
649 $combo->set_active ($value);
650 set_widget_hint ($combo, $$hash{$key}[DESC]);
651 set_widget_sens ($combo, $$hash{$key}[CMVER]);
652 $glabel->set_sensitive ($combo->get_sensitive);
654 return new_hbox_spaced_pack ($glabel, $combo);
657 # more graphic helpers
660 my $hbox = Gtk3::HBox->new (FALSE);
661 $hbox->set_border_width (PAGE_SPC);
663 $hbox->pack_start ($_, FALSE, FALSE, 0);
668 sub new_hbox_pack_compact {
669 my $hbox = Gtk3::HBox->new (FALSE);
670 $hbox->set_border_width (0);
672 $hbox->pack_start ($_, FALSE, FALSE, 0);
678 my $vbox = Gtk3::VBox->new (FALSE, 5);
679 $vbox->set_border_width (PAGE_SPC);
681 $vbox->pack_start ($_, FALSE, FALSE, 0);
686 sub new_vbox_pack_compact {
687 my $vbox = Gtk3::VBox->new (FALSE, 0);
688 $vbox->set_border_width (0);
690 $vbox->pack_start ($_, FALSE, FALSE, 0);
695 sub new_subpage_frame {
696 my ($box, $title, $notpacked) = @_;
697 my $frame = Gtk3::Frame->new ($title);
699 return new_vbox_pack ($frame) unless defined ($notpacked);
704 my ($border_w, $row_s, $col_s) = @_;
708 my $grid = Gtk3::Grid->new;
709 $grid->set_border_width ($border_w);
710 $grid->set_row_spacing ($row_s);
711 $grid->set_column_spacing ($col_s);
718 my $label = Gtk3::Label->new ($text);
719 $label->set_alignment (0, 0.5);
726 my $label = Gtk3::Label->new ('<b>' . $text . '</b>');
727 $label->set_use_markup (TRUE);
728 $label->set_alignment (0, 0.5);
733 my ($width, $height, $widget) = @_;
734 my $grid = new_grid ();
735 $grid->set_column_homogeneous (TRUE);
736 for (my $i = 0; $i < $width; ++$i) {
737 for (my $j = 0; $j < $height; ++$j) {
738 my $wid = $widget->[$j]->[$i];
739 next unless defined $wid;
740 my $ww = (($i + 1 < $width) and (defined $widget->[$j]->[$i + 1]))
744 $grid->attach ($wid, $i, $j, $ww, 1);
745 } else { # not a widget
746 if ('--' eq $wid) { # a separator
747 $grid->attach (Gtk3::Separator->new ('horizontal'),
749 } else { # or a title
750 $grid->attach (new_title ($wid),
759 # preference maps and corresponding page creation subs
761 %pr::oth = ( # other preferences
763 'addressbook_use_editaddress_dialog',
764 $xl::s{l_oth_use_dlg},
765 $xl::s{h_oth_use_dlg},
771 'cache_max_mem_usage',
772 [ $xl::s{l_oth_max_use}, $xl::s{l_oth_max_use_units} ],
773 $xl::s{h_oth_max_use},
774 'int,0,524288', # 0 Kb - 512 Mb
779 'cache_min_keep_time',
780 [ $xl::s{l_oth_min_time}, $xl::s{l_oth_min_time_units} ],
781 $xl::s{h_oth_min_time},
782 'int,0,120', # 0 minutes - 2 hours
787 'use_networkmanager',
788 $xl::s{l_oth_use_netm},
789 $xl::s{h_oth_use_netm},
795 'master_passphrase_pbkdf2_rounds',
796 $xl::s{l_oth_mp_rounds},
797 $xl::s{h_oth_mp_rounds},
804 sub new_other_page() {
805 return new_grid_pack (1, 12, [
806 [ _('Addressbook') ],
807 [ new_check_button_for(\%pr::oth, 'use_dlg', \%HPVALUE) ],
810 [ new_text_box_for_int(\%pr::oth, 'max_use', \%HPVALUE) ],
811 [ new_text_box_for_int(\%pr::oth, 'min_time', \%HPVALUE) ],
813 [ _('NetworkManager') ],
814 [ new_check_button_for(\%pr::oth, 'use_netm', \%HPVALUE) ],
816 [ _('Master passphrase') ],
817 [ new_text_box_for_int(\%pr::oth, 'mp_rounds', \%HPVALUE) ]
821 %pr::gui = ( # gui bells and whistles
824 $xl::s{l_gui_b_unread},
825 $xl::s{h_gui_b_unread},
832 $xl::s{l_gui_no_markup},
833 $xl::s{h_gui_no_markup},
839 'enable_dotted_lines',
840 $xl::s{l_gui_dot_lines},
841 $xl::s{h_gui_dot_lines},
843 '2.4.0.115,3.7.10.44',
848 $xl::s{l_gui_h_scroll},
849 $xl::s{h_gui_h_scroll},
856 $xl::s{l_gui_swp_from},
857 $xl::s{h_gui_swp_from},
863 'folderview_vscrollbar_policy',
864 $xl::s{l_gui_v_scroll},
865 $xl::s{h_gui_v_scroll},
866 '0=l_gui_v_scroll_show;1=l_gui_v_scroll_auto;2=l_gui_v_scroll_hide',
872 $xl::s{l_gui_from_show},
873 $xl::s{h_gui_from_show},
874 '0=l_gui_from_show_name;1=l_gui_from_show_addr;2=l_gui_from_show_both',
879 'stripes_color_offset',
880 $xl::s{l_gui_strip_off},
881 $xl::s{h_gui_strip_off},
882 'int,0,40000', # no idea what this number means
887 'textview_cursor_visible',
888 $xl::s{l_gui_cursor_v},
889 $xl::s{h_gui_cursor_v},
895 'toolbar_detachable',
896 $xl::s{l_gui_toolbar_d},
897 $xl::s{h_gui_toolbar_d},
903 'use_stripes_everywhere',
904 $xl::s{l_gui_strip_all},
905 $xl::s{h_gui_strip_all},
911 'use_stripes_in_summaries',
912 $xl::s{l_gui_strip_sum},
913 $xl::s{h_gui_strip_sum},
920 $xl::s{l_gui_two_line_v},
921 $xl::s{h_gui_two_line_v},
927 'show_compose_margin',
928 $xl::s{l_gui_margin_co},
929 $xl::s{h_gui_margin_co},
935 'msgview_date_format',
936 $xl::s{l_gui_mview_date},
937 $xl::s{h_gui_mview_date},
943 'zero_replacement_char',
944 $xl::s{l_gui_zero_char},
945 $xl::s{h_gui_zero_char},
952 $xl::s{l_gui_type_any},
953 $xl::s{h_gui_type_any},
959 'warn_sending_many_recipients_num',
960 [ $xl::s{l_gui_warn_send_multi}, $xl::s{l_gui_warn_send_multi_units} ],
961 $xl::s{h_gui_warn_send_multi},
968 $xl::s{l_gui_next_del},
969 $xl::s{h_gui_next_del},
977 return new_grid_pack (2, 24, [
978 [ _('Coloured stripes') ],
979 [ new_check_button_for (\%pr::gui, 'strip_all', \%HPVALUE),
980 new_check_button_for (\%pr::gui, 'strip_sum', \%HPVALUE) ],
981 [ new_text_box_for_int (\%pr::gui, 'strip_off', \%HPVALUE) ],
983 [ _('Message List') ],
984 [ new_check_button_for (\%pr::gui, 'b_unread', \%HPVALUE),
985 new_check_button_for (\%pr::gui, 'swp_from', \%HPVALUE) ],
986 [ new_check_button_for (\%pr::gui, 'two_linev', \%HPVALUE),
987 new_check_button_for (\%pr::gui, 'next_del', \%HPVALUE) ],
988 [ new_selection_box_for (\%pr::gui, 'from_show', \%HPVALUE) ],
990 [ _('Message View') ],
991 [ new_check_button_for (\%pr::gui, 'cursor_v', \%HPVALUE),
992 new_check_button_for (\%pr::gui, 'mview_date', \%HPVALUE) ],
994 [ _('Compose window') ],
995 [ new_check_button_for (\%pr::gui, 'no_markup', \%HPVALUE),
996 new_check_button_for (\%pr::gui, 'margin_co', \%HPVALUE) ],
997 [ new_check_button_for (\%pr::gui, 'type_any', \%HPVALUE) ],
998 [ new_text_box_for_int (\%pr::gui, 'warn_send_multi', \%HPVALUE) ],
1000 [ _('Scroll bars') ],
1001 [ new_check_button_for (\%pr::gui, 'h_scroll', \%HPVALUE) ],
1002 [ new_selection_box_for (\%pr::gui, 'v_scroll', \%HPVALUE) ],
1005 [ new_check_button_for (\%pr::gui, 'dot_lines', \%HPVALUE),
1006 new_check_button_for (\%pr::gui, 'toolbar_d', \%HPVALUE) ],
1007 [ new_text_box_for_nchar (\%pr::gui, 'zero_char', \%HPVALUE) ]
1011 %pr::beh = ( # tweak some behaviour
1014 [ $xl::s{l_beh_hover_t}, $xl::s{l_beh_hover_t_units} ],
1015 $xl::s{h_beh_hover_t},
1016 'int,100,3000', # 0.1 seconds - 3 seconds
1022 $xl::s{l_beh_dangerous},
1023 $xl::s{h_beh_dangerous},
1029 'respect_flowed_format',
1030 $xl::s{l_beh_flowed},
1031 $xl::s{h_beh_flowed},
1037 'save_parts_readwrite',
1038 $xl::s{l_beh_parts_rw},
1039 $xl::s{h_beh_parts_rw},
1045 'skip_ssl_cert_check',
1046 $xl::s{l_beh_skip_ssl},
1047 $xl::s{h_beh_skip_ssl},
1053 'statusbar_update_step',
1054 [ $xl::s{l_beh_up_step}, $xl::s{l_beh_up_step_units} ],
1055 $xl::s{h_beh_up_step},
1056 'int,1,200', # 1 item - 200 items
1061 'thread_by_subject_max_age',
1062 [ $xl::s{l_beh_thread_a}, $xl::s{l_beh_thread_a_units} ],
1063 $xl::s{h_beh_thread_a},
1064 'int,1,30', # 1 day - 30 days
1070 $xl::s{l_beh_unsafe_ssl},
1071 $xl::s{h_beh_unsafe_ssl},
1077 'utf8_instead_of_locale_for_broken_mail',
1078 $xl::s{l_beh_use_utf8},
1079 $xl::s{h_beh_use_utf8},
1086 $xl::s{l_beh_warn_dnd},
1087 $xl::s{h_beh_warn_dnd},
1093 'outgoing_fallback_to_ascii',
1094 $xl::s{l_beh_out_ascii},
1095 $xl::s{h_beh_out_ascii},
1101 'primary_paste_unselects',
1102 $xl::s{l_beh_pp_unsel},
1103 $xl::s{h_beh_pp_unsel},
1109 'show_inline_attachments',
1110 $xl::s{l_beh_inline_at},
1111 $xl::s{h_beh_inline_at},
1117 'address_search_wildcard',
1118 $xl::s{l_beh_addr_swc},
1119 $xl::s{h_beh_addr_swc},
1125 'folder_search_wildcard',
1126 $xl::s{l_beh_fold_swc},
1127 $xl::s{h_beh_fold_swc},
1133 'rewrite_first_from',
1134 $xl::s{l_beh_rewrite_ff},
1135 $xl::s{h_beh_rewrite_ff},
1142 sub new_behaviour_page() {
1143 my $bf = Gtk3::VBox->new (FALSE, 5);
1144 $bf->set_border_width (PAGE_SPC);
1146 my $tb_up_step = new_text_box_for_int (\%pr::beh, 'up_step', \%HPVALUE);
1147 my $tb_thread_a = new_text_box_for_int (\%pr::beh, 'thread_a', \%HPVALUE);
1149 $bf->pack_start (new_subpage_frame (
1151 new_text_box_for_int (\%pr::beh, 'hover_t', \%HPVALUE),
1152 new_check_button_for (\%pr::beh, 'warn_dnd', \%HPVALUE)),
1153 $xl::s{dnd_frame}, 'not-packed'),
1154 FALSE, FALSE, FRAME_SPC);
1155 $bf->pack_start (new_subpage_frame (
1157 new_check_button_for (\%pr::beh, 'skip_ssl', \%HPVALUE),
1158 new_check_button_for (\%pr::beh, 'unsafe_ssl', \%HPVALUE)),
1159 $xl::s{ssl_frame}, 'not-packed'),
1160 FALSE, FALSE, FRAME_SPC);
1161 $bf->pack_start ($tb_up_step, FALSE, FALSE, 0);
1162 $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0);
1163 $bf->pack_start (new_subpage_frame (
1165 new_check_button_for (\%pr::beh, 'flowed', \%HPVALUE),
1166 new_check_button_for (\%pr::beh, 'parts_rw', \%HPVALUE),
1167 new_check_button_for (\%pr::beh, 'use_utf8', \%HPVALUE),
1168 new_check_button_for (\%pr::beh, 'dangerous', \%HPVALUE),
1169 new_check_button_for (\%pr::beh, 'out_ascii', \%HPVALUE),
1170 new_check_button_for (\%pr::beh, 'pp_unsel', \%HPVALUE),
1171 new_check_button_for (\%pr::beh, 'inline_at', \%HPVALUE),
1172 new_check_button_for (\%pr::beh, 'rewrite_ff', \%HPVALUE)),
1173 $xl::s{msgs_frame}, 'not-packed'),
1174 FALSE, FALSE, FRAME_SPC);
1175 $bf->pack_start (new_subpage_frame (
1177 new_check_button_for (\%pr::beh, 'addr_swc', \%HPVALUE),
1178 new_check_button_for (\%pr::beh, 'fold_swc', \%HPVALUE)),
1179 $xl::s{swc_frame}, 'not-packed'),
1180 FALSE, FALSE, FRAME_SPC);
1184 %pr::col = ( # a variety of colours
1187 $xl::s{l_col_emphasis},
1188 $xl::s{h_col_emphasis},
1195 $xl::s{l_col_log_err},
1196 $xl::s{h_col_log_err},
1203 $xl::s{l_col_log_in},
1204 $xl::s{h_col_log_in},
1211 $xl::s{l_col_log_msg},
1212 $xl::s{h_col_log_msg},
1219 $xl::s{l_col_log_out},
1220 $xl::s{h_col_log_out},
1227 $xl::s{l_col_log_warn},
1228 $xl::s{h_col_log_warn},
1235 $xl::s{l_col_diff_add},
1236 $xl::s{h_col_diff_add},
1242 'diff_deleted_color',
1243 $xl::s{l_col_diff_del},
1244 $xl::s{h_col_diff_del},
1251 $xl::s{l_col_diff_hunk},
1252 $xl::s{h_col_diff_hunk},
1259 $xl::s{l_col_tags_bg},
1260 $xl::s{h_col_tags_bg},
1267 $xl::s{l_col_tags_text},
1268 $xl::s{h_col_tags_text},
1273 default_header_bg => [
1274 'default_header_bgcolor',
1275 $xl::s{l_col_default_header_bg},
1276 $xl::s{h_col_default_header_bg},
1281 default_header_text => [
1282 'default_header_color',
1283 $xl::s{l_col_default_header_text},
1284 $xl::s{h_col_default_header_text},
1290 'qs_active_bgcolor',
1291 $xl::s{l_col_qs_active_bg},
1292 $xl::s{h_col_qs_active_bg},
1299 $xl::s{l_col_qs_active_text},
1300 $xl::s{h_col_qs_active_text},
1307 $xl::s{l_col_qs_error_bg},
1308 $xl::s{h_col_qs_error_bg},
1315 $xl::s{l_col_qs_error_text},
1316 $xl::s{h_col_qs_error_text},
1323 sub new_colours_page() {
1324 return new_grid_pack (3, 18, [
1325 [ _('Message View') ],
1326 [ new_color_button_for (\%pr::col, 'emphasis', \%HPVALUE) ],
1327 [ new_color_button_for (\%pr::col, 'tags_text', \%HPVALUE) ,
1328 new_color_button_for (\%pr::col, 'tags_bg', \%HPVALUE) ],
1330 [ _('Log window') ],
1331 [ new_color_button_for (\%pr::col, 'log_err', \%HPVALUE) ,
1332 new_color_button_for (\%pr::col, 'log_in', \%HPVALUE) ],
1333 [ new_color_button_for (\%pr::col, 'log_warn', \%HPVALUE) ,
1334 new_color_button_for (\%pr::col, 'log_out', \%HPVALUE) ],
1335 [ new_color_button_for (\%pr::col, 'log_msg', \%HPVALUE) ],
1337 [ _('Viewing patches') ],
1338 [ new_color_button_for (\%pr::col, 'diff_add', \%HPVALUE) ,
1339 new_color_button_for (\%pr::col, 'diff_del', \%HPVALUE) ,
1340 new_color_button_for (\%pr::col, 'diff_hunk', \%HPVALUE) ],
1342 [ _('Compose window') ],
1343 [ new_color_button_for (\%pr::col, 'default_header_text', \%HPVALUE) ,
1344 new_color_button_for (\%pr::col, 'default_header_bg', \%HPVALUE) ],
1346 [ _('Quick search') ],
1347 [ new_color_button_for (\%pr::col, 'qs_active_text', \%HPVALUE) ,
1348 new_color_button_for (\%pr::col, 'qs_active_bg', \%HPVALUE) ],
1349 [ new_color_button_for (\%pr::col, 'qs_error_text', \%HPVALUE) ,
1350 new_color_button_for (\%pr::col, 'qs_error_bg', \%HPVALUE) ]
1354 %pr::win = ( # tweak window positions and/or sizes
1359 'int,0,3000', # 0 pixels - 3000 pixels
1367 'int,0,3000', # 0 pixels - 3000 pixels
1375 'int,0,3000', # 0 pixels - 3000 pixels
1383 'int,0,3000', # 0 pixels - 3000 pixels
1388 'mainwin_maximised',
1389 $xl::s{l_win_main_mx},
1390 $xl::s{h_win_main_mx},
1396 'mainwin_fullscreen',
1397 $xl::s{l_win_main_fs},
1398 $xl::s{h_win_main_fs},
1404 'main_messagewin_x',
1407 'int,0,3000', # 0 pixels - 3000 pixels
1412 'main_messagewin_y',
1415 'int,0,3000', # 0 pixels - 3000 pixels
1423 'int,0,3000', # 0 pixels - 3000 pixels
1428 'messagewin_height',
1431 'int,0,3000', # 0 pixels - 3000 pixels
1439 'int,0,3000', # 0 pixels - 3000 pixels
1447 'int,0,3000', # 0 pixels - 3000 pixels
1455 'int,0,3000', # 0 pixels - 3000 pixels
1460 'receivewin_height',
1463 'int,0,3000', # 0 pixels - 3000 pixels
1471 'int,0,3000', # 0 pixels - 3000 pixels
1479 'int,0,3000', # 0 pixels - 3000 pixels
1484 'folderitemwin_width',
1487 'int,0,3000', # 0 pixels - 3000 pixels
1492 'folderitemwin_height',
1495 'int,0,3000', # 0 pixels - 3000 pixels
1500 'folderselwin_width',
1503 'int,0,3000', # 0 pixels - 3000 pixels
1508 'folderselwin_height',
1511 'int,0,3000', # 0 pixels - 3000 pixels
1519 'int,0,3000', # 0 pixels - 3000 pixels
1527 'int,0,3000', # 0 pixels - 3000 pixels
1532 'addressbookwin_width',
1535 'int,0,3000', # 0 pixels - 3000 pixels
1540 'addressbookwin_height',
1543 'int,0,3000', # 0 pixels - 3000 pixels
1548 'addressbookeditpersonwin_width',
1551 'int,0,3000', # 0 pixels - 3000 pixels
1556 'addressbookeditpersonwin_height',
1559 'int,0,3000', # 0 pixels - 3000 pixels
1564 'addressbookeditgroupwin_width',
1567 'int,0,3000', # 0 pixels - 3000 pixels
1572 'addressbookeditgroupwin_height',
1575 'int,0,3000', # 0 pixels - 3000 pixels
1580 'addressaddwin_width',
1583 'int,0,3000', # 0 pixels - 3000 pixels
1588 'addressaddwin_height',
1591 'int,0,3000', # 0 pixels - 3000 pixels
1596 'addressbook_folderselwin_width',
1599 'int,0,3000', # 0 pixels - 3000 pixels
1604 'addressbook_folderselwin_height',
1607 'int,0,3000', # 0 pixels - 3000 pixels
1612 'editaccountwin_width',
1615 'int,0,3000', # 0 pixels - 3000 pixels
1620 'editaccountwin_height',
1623 'int,0,3000', # 0 pixels - 3000 pixels
1628 'accountswin_width',
1631 'int,0,3000', # 0 pixels - 3000 pixels
1636 'accountswin_height',
1639 'int,0,3000', # 0 pixels - 3000 pixels
1644 'filteringwin_width',
1647 'int,0,3000', # 0 pixels - 3000 pixels
1652 'filteringwin_height',
1655 'int,0,3000', # 0 pixels - 3000 pixels
1660 'filteringactionwin_width',
1663 'int,0,3000', # 0 pixels - 3000 pixels
1668 'filteringactionwin_height',
1671 'int,0,3000', # 0 pixels - 3000 pixels
1676 'filtering_debugwin_width',
1679 'int,0,3000', # 0 pixels - 3000 pixels
1684 'filtering_debugwin_height',
1687 'int,0,3000', # 0 pixels - 3000 pixels
1695 'int,0,3000', # 0 pixels - 3000 pixels
1700 'matcherwin_height',
1703 'int,0,3000', # 0 pixels - 3000 pixels
1711 'int,0,3000', # 0 pixels - 3000 pixels
1719 'int,0,3000', # 0 pixels - 3000 pixels
1724 'templateswin_width',
1727 'int,0,3000', # 0 pixels - 3000 pixels
1732 'templateswin_height',
1735 'int,0,3000', # 0 pixels - 3000 pixels
1743 'int,0,3000', # 0 pixels - 3000 pixels
1748 'actionswin_height',
1751 'int,0,3000', # 0 pixels - 3000 pixels
1756 'actionsiodialog_width',
1759 'int,0,3000', # 0 pixels - 3000 pixels
1764 'actionsiodialog_height',
1767 'int,0,3000', # 0 pixels - 3000 pixels
1775 'int,0,3000', # 0 pixels - 3000 pixels
1783 'int,0,3000', # 0 pixels - 3000 pixels
1791 'int,0,3000', # 0 pixels - 3000 pixels
1796 'pluginswin_height',
1799 'int,0,3000', # 0 pixels - 3000 pixels
1807 'int,0,3000', # 0 pixels - 3000 pixels
1815 'int,0,3000', # 0 pixels - 3000 pixels
1820 'print_previewwin_width',
1823 'int,0,3000', # 0 pixels - 3000 pixels
1828 'print_previewwin_height',
1831 'int,0,3000', # 0 pixels - 3000 pixels
1837 sub new_winpos_subpage_main() {
1838 return new_grid_pack (3, 7, [
1839 [ _('Main window'), undef ],
1840 [ new_text_box_for_int (\%pr::win, 'main_x', \%HPVALUE), undef ],
1841 [ new_text_box_for_int (\%pr::win, 'main_y', \%HPVALUE), undef ],
1842 [ new_text_box_for_int (\%pr::win, 'main_w', \%HPVALUE),
1843 new_text_box_for_int (\%pr::win, 'main_h', \%HPVALUE) ],
1844 [ new_check_button_for (\%pr::win, 'main_fs', \%HPVALUE), undef ],
1845 [ new_check_button_for (\%pr::win, 'main_mx', \%HPVALUE), undef ]
1849 sub new_winpos_subpage_msgs() {
1850 return new_grid_pack (3, 4, [
1851 [ _('Message window') ],
1852 [ new_text_box_for_int (\%pr::win, 'msgs_x', \%HPVALUE) ],
1853 [ new_text_box_for_int (\%pr::win, 'msgs_y', \%HPVALUE) ],
1854 [ new_text_box_for_int (\%pr::win, 'msgs_w', \%HPVALUE),
1855 new_text_box_for_int (\%pr::win, 'msgs_h', \%HPVALUE) ]
1859 sub new_winpos_subpage_sendrecv() {
1860 return new_grid_pack (3, 5, [
1861 [ _('Send window') ],
1862 [ new_text_box_for_int (\%pr::win, 'send_w', \%HPVALUE),
1863 new_text_box_for_int (\%pr::win, 'send_h', \%HPVALUE) ],
1865 [ _('Receive window') ],
1866 [ new_text_box_for_int (\%pr::win, 'recv_w', \%HPVALUE),
1867 new_text_box_for_int (\%pr::win, 'recv_h', \%HPVALUE) ]
1871 sub new_winpos_subpage_fold() {
1872 return new_grid_pack (3, 7, [
1873 [ _('Folder window') ],
1874 [ new_text_box_for_int (\%pr::win, 'fold_x', \%HPVALUE) ],
1875 [ new_text_box_for_int (\%pr::win, 'fold_y', \%HPVALUE) ],
1876 [ new_text_box_for_int (\%pr::win, 'fold_w', \%HPVALUE),
1877 new_text_box_for_int (\%pr::win, 'fold_h', \%HPVALUE) ],
1879 [ _('Folder selection window') ],
1880 [ new_text_box_for_int (\%pr::win, 'fsel_w', \%HPVALUE),
1881 new_text_box_for_int (\%pr::win, 'fsel_h', \%HPVALUE) ]
1885 sub new_winpos_subpage_addrbook() {
1886 return new_grid_pack (3, 14, [
1887 [ _('Addressbook main window') ],
1888 [ new_text_box_for_int (\%pr::win, 'addr_w', \%HPVALUE),
1889 new_text_box_for_int (\%pr::win, 'addr_h', \%HPVALUE) ],
1891 [ _('Edit person window') ],
1892 [ new_text_box_for_int (\%pr::win, 'adep_w', \%HPVALUE),
1893 new_text_box_for_int (\%pr::win, 'adep_h', \%HPVALUE) ],
1895 [ _('Edit group window') ],
1896 [ new_text_box_for_int (\%pr::win, 'adeg_w', \%HPVALUE),
1897 new_text_box_for_int (\%pr::win, 'adeg_h', \%HPVALUE) ],
1899 [ _('Add address window') ],
1900 [ new_text_box_for_int (\%pr::win, 'adda_w', \%HPVALUE),
1901 new_text_box_for_int (\%pr::win, 'adda_h', \%HPVALUE) ],
1903 [ _('Folder select window') ],
1904 [ new_text_box_for_int (\%pr::win, 'addf_w', \%HPVALUE),
1905 new_text_box_for_int (\%pr::win, 'addf_h', \%HPVALUE) ]
1909 sub new_winpos_subpage_accounts() {
1910 return new_grid_pack (3, 5, [
1911 [ _('Accounts window') ],
1912 [ new_text_box_for_int (\%pr::win, 'acco_w', \%HPVALUE),
1913 new_text_box_for_int (\%pr::win, 'acco_h', \%HPVALUE) ],
1915 [ _('Edit account window') ],
1916 [ new_text_box_for_int (\%pr::win, 'acce_w', \%HPVALUE),
1917 new_text_box_for_int (\%pr::win, 'acce_h', \%HPVALUE) ]
1921 sub new_winpos_subpage_filtering() {
1922 return new_grid_pack (3, 11, [
1923 [ _('Filtering window') ],
1924 [ new_text_box_for_int (\%pr::win, 'filt_w', \%HPVALUE),
1925 new_text_box_for_int (\%pr::win, 'filt_h', \%HPVALUE) ],
1927 [ _('Filtering actions window') ],
1928 [ new_text_box_for_int (\%pr::win, 'fila_w', \%HPVALUE),
1929 new_text_box_for_int (\%pr::win, 'fila_h', \%HPVALUE) ],
1931 [ _('Filtering debug window') ],
1932 [ new_text_box_for_int (\%pr::win, 'fild_w', \%HPVALUE),
1933 new_text_box_for_int (\%pr::win, 'fild_h', \%HPVALUE) ],
1935 [ ('Matcher window') ],
1936 [ new_text_box_for_int (\%pr::win, 'matc_w', \%HPVALUE),
1937 new_text_box_for_int (\%pr::win, 'matc_h', \%HPVALUE) ]
1942 sub new_winpos_subpage_useractions() {
1943 return new_grid_pack (3, 5, [
1944 [ _('User Actions prefs window') ],
1945 [ new_text_box_for_int (\%pr::win, 'acti_w', \%HPVALUE),
1946 new_text_box_for_int (\%pr::win, 'acti_h', \%HPVALUE) ],
1948 [ _('User Actions I/O window') ],
1949 [ new_text_box_for_int (\%pr::win, 'acio_w', \%HPVALUE),
1950 new_text_box_for_int (\%pr::win, 'acio_h', \%HPVALUE) ]
1954 sub new_winpos_subpage_prefs() {
1955 return new_grid_pack (3, 11, [
1956 [ _('Preferences window') ],
1957 [ new_text_box_for_int (\%pr::win, 'pref_w', \%HPVALUE),
1958 new_text_box_for_int (\%pr::win, 'pref_h', \%HPVALUE) ],
1960 [ _('Templates window') ],
1961 [ new_text_box_for_int (\%pr::win, 'temp_w', \%HPVALUE),
1962 new_text_box_for_int (\%pr::win, 'temp_h', \%HPVALUE) ],
1964 [ _('Tags window') ],
1965 [ new_text_box_for_int (\%pr::win, 'tags_w', \%HPVALUE),
1966 new_text_box_for_int (\%pr::win, 'tags_h', \%HPVALUE) ],
1968 [ _('Plugins window') ],
1969 [ new_text_box_for_int (\%pr::win, 'plug_w', \%HPVALUE),
1970 new_text_box_for_int (\%pr::win, 'plug_h', \%HPVALUE) ]
1974 sub new_winpos_subpage_misc() {
1975 return new_grid_pack (3, 8, [
1976 [ _('Log window') ],
1977 [ new_text_box_for_int (\%pr::win, 'logw_w', \%HPVALUE),
1978 new_text_box_for_int (\%pr::win, 'logw_h', \%HPVALUE) ],
1980 [ _('Print preview window') ],
1981 [ new_text_box_for_int (\%pr::win, 'prin_w', \%HPVALUE),
1982 new_text_box_for_int (\%pr::win, 'prin_h', \%HPVALUE) ],
1984 [ _('View source window') ],
1985 [ new_text_box_for_int (\%pr::win, 'sour_w', \%HPVALUE),
1986 new_text_box_for_int (\%pr::win, 'sour_h', \%HPVALUE) ]
1990 sub new_winpos_page() {
1991 my $winbook = Gtk3::Notebook->new;
1992 $winbook->set_tab_pos ('right');
1993 $winbook->append_page (new_winpos_subpage_main,
1994 Gtk3::Label->new (_('Main')));
1995 $winbook->append_page (new_winpos_subpage_msgs,
1996 Gtk3::Label->new (_('Message')));
1997 $winbook->append_page (new_winpos_subpage_sendrecv,
1998 Gtk3::Label->new (_('Send/Receive')));
1999 $winbook->append_page (new_winpos_subpage_fold,
2000 Gtk3::Label->new (_('Folder')));
2001 $winbook->append_page (new_winpos_subpage_addrbook,
2002 Gtk3::Label->new (_('Addressbook')));
2003 $winbook->append_page (new_winpos_subpage_accounts,
2004 Gtk3::Label->new (_('Accounts')));
2005 $winbook->append_page (new_winpos_subpage_filtering,
2006 Gtk3::Label->new (_('Filtering')));
2007 $winbook->append_page (new_winpos_subpage_useractions,
2008 Gtk3::Label->new (_('User Actions')));
2009 $winbook->append_page (new_winpos_subpage_prefs,
2010 Gtk3::Label->new (_('Preferences')));
2011 $winbook->append_page (new_winpos_subpage_misc,
2012 Gtk3::Label->new (_('Other')));
2016 %pr::acc = ( # per account hidden preferences
2018 'gnutls_set_priority',
2019 $xl::s{l_acc_gtls_set},
2020 $xl::s{h_acc_gtls_set},
2027 $xl::s{l_acc_gtls_pri},
2028 $xl::s{h_acc_gtls_pri},
2035 sub new_account_subpage($) {
2037 return new_grid_pack (1, 3, [
2038 [ _('GnuTLS priority') ],
2039 [ new_check_button_for (\%pr::acc, 'tls_set', $ACHPVALUE{$akey}) ],
2040 [ new_text_box_for_nchar (\%pr::acc, 'tls_pri', $ACHPVALUE{$akey}) ]
2044 sub new_accounts_page() {
2045 my $accbook = Gtk3::Notebook->new;
2046 $accbook->set_tab_pos ('right');
2048 $ACPREFS{$a}{'account_name'} cmp $ACPREFS{$b}{'account_name'}
2051 my $name = $ACPREFS{$_}{'account_name'};
2052 my $isdef = ($ACPREFS{$_}{'is_default'} eq '1');
2053 my $page = new_account_subpage ($_);
2054 my $label = new_label ($isdef? '<u>' . $name . '</u>': $name);
2055 $label->set_use_markup (TRUE);
2056 $accbook->append_page ($page, $label);
2058 $accbook->set_scrollable (TRUE);
2062 %pr::plu = ( # plugins hidden preferences
2068 'int,0,3000', # 0 pixels - 3000 pixels
2077 'int,0,3000', # 0 pixels - 3000 pixels
2084 'autocompletion_limit',
2085 $xl::s{l_plu_gpg_alimit},
2086 $xl::s{h_plu_gpg_alimit},
2094 'manager_win_width',
2097 'int,0,3000', # 0 pixels - 3000 pixels
2103 'manager_win_height',
2106 'int,0,3000', # 0 pixels - 3000 pixels
2114 $xl::s{l_plu_lav_burl},
2115 $xl::s{h_plu_lav_burl},
2118 'http://cdn.libravatar.org/avatar',
2123 'filter_log_verbosity',
2124 $xl::s{l_plu_prl_flvb},
2125 $xl::s{h_plu_prl_flvb},
2126 '0=l_plu_prl_none;1=l_plu_prl_manual;2=l_plu_prl_action;3=l_plu_prl_match',
2133 sub new_plugins_page() {
2136 new_text_box_for_int (\%pr::plu, 'arm_winw', $PLHPVALUE{'AttRemover'}),
2137 new_text_box_for_int (\%pr::plu, 'arm_winh', $PLHPVALUE{'AttRemover'})
2140 new_text_box_for_int (\%pr::plu, 'gpg_alimit', $PLHPVALUE{'GPG'})
2143 new_text_box_for_int (\%pr::plu, 'msv_winw', $PLHPVALUE{'ManageSieve'}),
2144 new_text_box_for_int (\%pr::plu, 'msv_winh', $PLHPVALUE{'ManageSieve'})
2147 new_text_box_for_nchar (\%pr::plu, 'lav_burl', $PLHPVALUE{'Libravatar'})
2150 new_selection_box_for (\%pr::plu, 'prl_flvb', $PLHPVALUE{'PerlPlugin'})
2153 foreach my $pk (@PLUGINS) {
2154 foreach my $wg (@{$widget{$pk}}) {
2155 $wg->set_sensitive (defined $PLHPVALUE{$pk});
2158 return new_grid_pack (3, 14, [
2159 [ _('Attachment remover') ], $widget{'AttRemover'}, [ '--' ],
2160 [ _('GPG') ], $widget{'GPG'}, [ '--' ],
2161 [ _('Sieve manager') ], $widget{'ManageSieve'}, [ '--' ],
2162 [ _('Libravatar') ], $widget{'Libravatar'}, [ '--' ],
2163 [ _('Perl') ], $widget{'PerlPlugin'}
2175 sub new_hotkeys_list_label {
2176 my $renderer = Gtk3::CellRendererText->new ();
2177 $renderer->set_property('alignment' => 'left');
2178 $renderer->set_property('editable' => FALSE);
2182 sub new_hotkeys_list_hotkey {
2183 my $renderer = Gtk3::CellRendererAccel->new ();
2184 $renderer->set_property ('accel-mode' => 'gtk');
2185 $renderer->set_property ('editable' => TRUE);
2186 $renderer->signal_connect ('accel-edited' => sub {
2187 my ($w, $path, $key, $mods, $keycode) = @_;
2188 my $accel = Gtk3::Accelerator->name ($key, $mods);
2189 my ($model, $iter) = $SELHOTKEY->get_selected ();
2190 $model->set($iter, C_HOTKEY, "\"$accel\"");
2191 my $gkey = $model->get_value ($iter, C_GROUP);
2192 my $akey = $model->get_value ($iter, C_ACCEL);
2193 my $data = $HOTKEYS->{$gkey}->{$akey};
2194 $data->{'key'} = "\"$accel\"";
2195 $data->{'enabled'} = TRUE;
2197 $renderer->signal_connect ('accel-cleared' => sub {
2198 my ($w, $path) = @_;
2199 my ($model, $iter) = $SELHOTKEY->get_selected ();
2200 $model->set($iter, C_HOTKEY, "\"\"");
2201 my $gkey = $model->get_value ($iter, C_GROUP);
2202 my $akey = $model->get_value ($iter, C_ACCEL);
2203 my $data = $HOTKEYS->{$gkey}->{$akey};
2204 $data->{'key'} = "\"\"";
2205 $data->{'enabled'} = FALSE;
2210 sub row_background_color {
2211 my ($column, $isodd) = @_;
2212 my $treeview = $column->get_tree_view;
2213 my $stylectx = $treeview->get_style_context;
2215 ? $stylectx->get_background_color ('normal')
2216 : $stylectx->get_background_color ('insensitive');
2219 sub new_hotkeys_list {
2220 my ($gkey, $group) = @_;
2221 my $store = Gtk3::ListStore->new(
2222 qw/Glib::String Glib::String Glib::String Glib::String Glib::Boolean/);
2224 foreach my $akey (sort keys %$group) {
2225 my $iter = $store->append ();
2226 my $hotkey = $group->{$akey}->{'key'};
2228 $label =~ s/[<>]//g; # <rrsyl> and <IMAPFolder> !?
2229 $store->set ($iter, C_LABEL, $label, C_HOTKEY, $hotkey,
2230 C_GROUP, $gkey, C_ACCEL, $akey, C_ODDITY, $even);
2233 my $treeview = Gtk3::TreeView->new_with_model ($store);
2235 $treeview->insert_column_with_data_func (
2236 0, _("Menu path"), new_hotkeys_list_label (),
2238 my ($col, $renderer, $model, $iter, $data) = @_;
2239 $renderer->set_property (
2240 'markup' => '<span size="smaller">'
2241 . $model->get_value ($iter, C_LABEL)
2243 my $bgcol = row_background_color (
2244 $col, $model->get_value ($iter, C_ODDITY));
2245 $renderer->set_property ('cell-background-rgba' => $bgcol);
2249 $treeview->insert_column_with_data_func (
2250 1, _('Hotkey'), new_hotkeys_list_hotkey (),
2252 my ($col, $renderer, $model, $iter, $data) = @_;
2253 my $hkey = $model->get_value ($iter, C_HOTKEY);
2255 my ($acckey, $accmod) = Gtk3::accelerator_parse ($hkey);
2256 $renderer->set_property ('accel-key' => $acckey);
2257 $renderer->set_property ('accel-mods' => $accmod);
2258 my $bgcol = row_background_color (
2259 $col, $model->get_value ($iter, C_ODDITY));
2260 $renderer->set_property ('cell-background-rgba' => $bgcol);
2263 # callback for saving current selection
2264 my $selection = $treeview->get_selection ();
2265 $selection->signal_connect ('changed' => sub { $SELHOTKEY = shift });
2269 sub new_hotkeys_page() {
2270 my $swin = Gtk3::ScrolledWindow->new ();
2271 my $vbox = Gtk3::VBox->new (FALSE, 5);
2272 foreach my $gkey (sort keys %$HOTKEYS) {
2273 my $group = $HOTKEYS->{$gkey};
2275 my $glabel = Gtk3::Label->new ('<b>' . $gkey . '</b>');
2276 $glabel->set_use_markup (TRUE);
2277 $glabel->set_alignment (0, 0.5);
2278 $glabel->set_padding (5, 1);
2279 $vbox->pack_start ($glabel, FALSE, FALSE, 0);
2281 my $keylist = new_hotkeys_list ($gkey, $group);
2282 $vbox->pack_start ($keylist, FALSE, FALSE, 0);
2284 $swin->set_border_width (5);
2285 $swin->set_shadow_type ('none');
2286 $swin->set_policy ('automatic', 'always');
2287 $swin->add_with_viewport ($vbox);
2291 sub new_info_page() {
2292 my $v = get_toolkit_versions ();
2293 my $cfgv = $CONFIGDATA->{'Common'}{'config_version'};
2295 return new_grid_pack (4, 11, [
2296 [ _('Library versions') ],
2297 [ new_label ('Perl-GLib'), new_title ($v->{'glib'}) ],
2298 [ new_label (_('GLib runtime')), new_title ($v->{'glib-r'}) ],
2299 [ new_label (_('GLib built')), new_title ($v->{'glib-b'}) ],
2300 [ new_label ('Perl-GTK3'), new_title ($v->{'gtk'}) ],
2301 [ new_label (_('GTK3 runtime')), new_title ($v->{'gtk-r'}) ],
2302 [ new_label (_('GTK3 built')), new_title ($v->{'gtk-b'}) ],
2304 [ _('Claws Mail versions') ],
2305 [ new_label (_('Binary')), new_title ($CLAWSV) ],
2306 [ new_label (_('Configuration')), new_title ($cfgv) ]
2311 sub get_toolkit_versions {
2313 $versions{'glib'} = $Glib::VERSION;
2314 # version info stuff appeared in 1.040
2315 if ($Glib::VERSION >= 1.040) {
2316 $versions{'glib-b'} = join('.', Glib->GET_VERSION_INFO);
2317 $versions{'glib-r'} = join('.',
2318 &Glib::major_version, &Glib::minor_version, &Glib::micro_version);
2320 $versions{'gtk'} = $Gtk3::VERSION;
2321 if ($Gtk3::VERSION >= 0.034) {
2322 $versions{'gtk-b'} = &Gtk3::GET_VERSION_INFO
2324 $versions{'gtk-b'} = _('Not available')
2326 $versions{'gtk-r'} = join('.',
2327 &Gtk3::get_major_version, &Gtk3::get_minor_version, &Gtk3::get_micro_version);
2331 sub print_version() {
2332 print $xl::s{about_title} . "\n";
2333 print $xl::s{about_version} . " $VERSION\n";
2334 my $v = get_toolkit_versions ();
2335 if ($v->{'glib-b'}) {
2336 print _("Perl-GLib version {glibv}, built for {glibb}, running with {glibr}.",
2337 glibv => $v->{'glib'},
2338 glibb => $v->{'glib-b'},
2339 glibr => $v->{'glib-r'});
2341 print _("Perl-GLib version {glibv}.", glibv => $v->{'glib'});
2344 if ($v->{'gtk-b'}) {
2345 print _("Perl-GTK3 version {gtkv}, built for {gtkb}, running with {gtkr}.",
2346 gtkv => $v->{'gtk'},
2347 gtkb => $v->{'gtk-b'},
2348 gtkr => $v->{'gtk-r'});
2350 print _("Perl-GTK3 version {gtkv}.", gtkv => $v->{'gtk'});
2353 my $clawsver = ($CLAWSV eq "") ?
2354 _("Claws Mail was not found!") :
2355 _("Claws Mail returned version {cmv}.", cmv => $CLAWSV);
2356 print $clawsver . "\n";
2359 # the command line help
2361 my $line = '-' x length ($xl::s{about_title});
2363 say $xl::s{about_title};
2367 _(" clawsker [options]"),
2369 _(" -a|--alternate-config-dir <dir> Uses <dir> as Claws Mail configuration."),
2370 _(" -b|--verbose More messages on standard output."),
2371 _(" -c|--clawsrc <file> Uses <file> as full resource name."),
2372 _(" -h|--help Prints this help screen and exits."),
2373 _(" -r|--read-only Disables writing changes to disk."),
2374 _(" -v|--version Prints version information and exits.")
2376 foreach (@help) { say $_ }
2379 sub parse_command_line {
2381 $CLAWSV = get_claws_version ();
2383 GetOptions('h|help' => sub { print_help (); $cont = FALSE },
2384 'v|version' => sub { print_version (); $cont = FALSE },
2385 'b|verbose' => sub { $VERBOSE = TRUE },
2386 'r|read-only' => sub { $READONLY = TRUE },
2387 'u|use-claws-version=s' => \&opt_use_claws_version,
2388 'a|alternate-config-dir=s' => \&opt_alternate_config_dir,
2389 'c|clawsrc=s' => \&opt_clawsrc)
2390 or die _("try -h or --help for syntax.\n");
2393 my $msg = _("Error in options: {msg}\n", msg => $@);
2394 if (defined $ENV{'DISPLAY'} and $ENV{'DISPLAY'} ne '') {
2395 eval { Gtk3->init };
2396 error_dialog ($msg) unless $@;
2403 sub opt_use_claws_version {
2404 my ($name, $value) = @_;
2405 die _("Error: {opt} requires a dotted numeric value argument\n", opt => $name)
2406 unless ($value =~ /^[\d\.]+$/);
2410 sub opt_alternate_config_dir {
2411 my ($name, $value) = @_;
2412 die _("Error: '{dir}' is not a directory or does not exist\n", dir => $value)
2414 $CONFIGDIR = $value;
2415 $CONFIGDIR .= "/" unless ($CONFIGDIR =~ /.*\/$/);
2416 $ALTCONFIGDIR = TRUE;
2420 my ($name, $value) = @_;
2421 die _("Error: '{value}' is not a file or does not exist\n", value => $value)
2423 set_rc_filename ($value);
2426 # update the hidden preferences status from loaded values
2427 sub init_hidden_preferences {
2428 foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth, \%pr::win) {
2429 foreach my $key (keys %$hash) {
2430 $HPVALUE{${$hash}{$key}[NAME]}[VALUE] = $PREFS{${$hash}{$key}[NAME]};
2431 $HPVALUE{${$hash}{$key}[NAME]}[IVALUE] = $PREFS{${$hash}{$key}[NAME]};
2434 foreach my $akey (keys %ACPREFS) {
2435 foreach my $key (keys %pr::acc) {
2436 my $pname = $pr::acc{$key}[NAME];
2437 $ACHPVALUE{$akey}{$pname}[VALUE] = $ACPREFS{$akey}{$pname};
2438 $ACHPVALUE{$akey}{$pname}[IVALUE] = $ACPREFS{$akey}{$pname};
2441 foreach my $key (keys %pr::plu) {
2442 my $plugin = $pr::plu{$key}[PLUGIN];
2443 my $pname = $pr::plu{$key}[NAME];
2444 if (defined $PLPREFS{$plugin}) {
2445 $PLHPVALUE{$plugin}{$pname}[VALUE] = $PLPREFS{$plugin}{$pname};
2446 $PLHPVALUE{$plugin}{$pname}[IVALUE] = $PLPREFS{$plugin}{$pname};
2452 # generic load/save resource files
2458 open (RCF, '<:encoding(utf8)', $rc)
2459 or die _("Error: opening '{file}' for reading", file => $rc) . ": $!\n";
2460 my $section = '_'; # default unnamed section
2465 if (/^\[([^\]]+)\]$/) { # new section
2467 die _("Error: duplicate section '{sect}' in resource file '{file}'\n",
2468 sect => $section, file => $rc) if ($data{$section});
2469 $data{$section} = {};
2470 $meta{$section}{'#'} = $line;
2472 elsif (/^([0-9a-z_]+)=(.*)$/) { # key=value
2473 $data{$section}{$1} = $2;
2474 $meta{$section}{$1} = $line;
2476 elsif (/^(.*)$/) { # lone value
2477 push (@{$data{$section}{'_'}}, $1);
2481 return (\%data, \%meta);
2485 my ($rc, $data, $meta) = @_;
2486 open (RCF, '>:utf8', $rc)
2487 or die _("Error: opening '{file}' for writing", file => $rc) . ": $!\n";
2488 my @sections = keys %$data;
2489 if (defined $meta) {
2491 $meta->{$a}{'#'} <=> $meta->{$b}{'#'}
2494 foreach my $section (@sections) {
2495 say RCF "[$section]";
2496 if (ref ($data->{$section}{'_'}) eq 'ARRAY') {
2497 foreach my $val (@{$data->{$section}{'_'}}) {
2501 my @keys = keys %{$data->{$section}};
2502 if (defined $meta) {
2504 $meta->{$section}{$a} <=> $meta->{$section}{$b}
2507 foreach my $key (@keys) {
2508 my $val = $data->{$section}{$key};
2509 say RCF "$key=$val";
2517 sub backup_resource {
2519 my $rcbak = "$rc.backup";
2521 my $emsg = _("Unable to create backup file '{name}'\n", name => $rcbak);
2522 log_message ($emsg);
2523 error_dialog ($emsg);
2525 } unless rename ($rc, $rcbak);
2532 open (RCF, '<:encoding(utf8)', $rc)
2533 or die _("Error: opening '{file}' for reading", file => $rc) . ": $!\n";
2538 if (/^; \(gtk_accel_path "<([A-Za-z]+)>([^"]+)" ([^\)]+)\)$/) {
2539 my %data = ('key' => $3, 'enabled' => FALSE, 'line' => $line);
2540 $groups{$1}{$2} = \%data;
2541 # say "group -> $1 | path -> $2 | key -> $3";
2542 } elsif (/^\(gtk_accel_path "<([A-Za-z]+)>([^"]+)" ([^\)]+)\)$/) {
2543 my %data = ('key' => $3, 'enabled' => TRUE, 'line' => $line);
2544 $groups{$1}{$2} = \%data;
2545 # say "group -> $1 | path -> $2 | key -> $3";
2554 my ($rc, $groups) = @_;
2556 foreach my $gkey (keys %$groups) {
2557 my $group = $groups->{$gkey};
2558 foreach my $akey (keys %$group) {
2559 my $data = $group->{$akey};
2560 my $key = $data->{'key'};
2561 my $line = $data->{'line'};
2562 $lines[$line] = ($data->{'enabled'})? '': '; ';
2563 $lines[$line] .= '(gtk_accel_path "<'
2564 . $gkey . '>' . $akey . '" ' . $key . ')';
2567 open (RCF, '>:utf8', $rc)
2568 or die _("Error: opening '{file}' for writing", file => $rc) . ": $!\n";
2569 say RCF '; claws-mail GtkAccelMap rc-file -*- scheme -*-';
2570 say RCF '; this file is an automated accelerator map dump';
2572 foreach (@lines) { say RCF $_ if $_ }
2576 # load current status from disc
2577 sub load_rc_preferences {
2578 my $rc = get_rc_filename ();
2579 log_message ("Loading preferences from $rc\n");
2580 return FALSE unless check_rc_file ($rc);
2581 ($CONFIGDATA, $CONFIGMETA) = load_resource ($rc);
2582 foreach (keys %{$CONFIGDATA->{'Common'}}) {
2583 $PREFS{$_} = $CONFIGDATA->{'Common'}{$_};
2585 foreach my $plugin (@PLUGINS) {
2586 if (defined $CONFIGDATA->{$plugin}) {
2587 push (@AVPLUGINS, $plugin);
2588 foreach (keys %{$CONFIGDATA->{$plugin}}) {
2589 $PLPREFS{$plugin}{$_} = $CONFIGDATA->{$plugin}{$_};
2596 sub load_ac_preferences {
2597 my $rc = get_ac_rc_filename ();
2598 log_message ("Loading account preferences from $rc\n");
2599 return FALSE unless check_rc_file ($rc);
2600 ($ACCOUNTDATA, $ACCOUNTMETA) = load_resource ($rc);
2601 foreach my $asect (keys %$ACCOUNTDATA) {
2602 if ($asect =~ /^Account: (\d+)$/) {
2603 foreach (keys %{$ACCOUNTDATA->{$asect}}) {
2604 $ACPREFS{$1}{$_} = $ACCOUNTDATA->{$asect}{$_};
2611 sub load_hk_preferences {
2612 my $rc = get_menurc_filename ();
2613 return FALSE unless check_rc_file ($rc);
2614 $HOTKEYS = load_menurc ($rc);
2618 sub load_preferences {
2619 return FALSE unless check_claws_not_running ();
2620 return (load_rc_preferences ()
2621 and load_ac_preferences ()
2622 and load_hk_preferences ()
2626 # save current preferences to disc
2627 sub save_rc_preferences {
2628 my $rc = get_rc_filename ();
2629 log_message ("Saving preferences to $rc\n");
2630 return FALSE unless check_rc_file ($rc);
2631 return FALSE unless check_claws_not_running ();
2632 return FALSE unless backup_resource ($rc);
2633 foreach (keys %PREFS) {
2634 if (defined $HPVALUE{$_}) {
2635 $CONFIGDATA->{'Common'}{$_} = $HPVALUE{$_}[VALUE];
2638 foreach my $plugin (@AVPLUGINS) {
2639 foreach (keys %{$CONFIGDATA->{$plugin}}) {
2640 if (defined $PLHPVALUE{$plugin}{$_}) {
2641 $CONFIGDATA->{$plugin}{$_} = $PLHPVALUE{$plugin}{$_}[VALUE];
2645 save_resource ($rc, $CONFIGDATA, $CONFIGMETA);
2649 sub save_ac_preferences {
2650 my $rc = get_ac_rc_filename ();
2651 log_message ("Saving account preferences to $rc\n");
2652 return FALSE unless check_rc_file ($rc);
2653 return FALSE unless check_claws_not_running ();
2654 return FALSE unless backup_resource ($rc);
2655 foreach my $asect (keys %$ACCOUNTDATA) {
2656 if ($asect =~ /^Account: (\d+)$/) {
2657 foreach (keys %{$ACCOUNTDATA->{$asect}}) {
2658 if (defined $ACHPVALUE{$1}{$_}) {
2659 $ACCOUNTDATA->{$asect}{$_} = $ACHPVALUE{$1}{$_}[VALUE];
2664 save_resource ($rc, $ACCOUNTDATA, $ACCOUNTMETA);
2668 sub save_hk_preferences {
2669 my $rc = get_menurc_filename ();
2670 log_message ("Saving hotkey preferences to $rc\n");
2671 return FALSE unless check_rc_file ($rc);
2672 return FALSE unless check_claws_not_running ();
2673 return FALSE unless backup_resource ($rc);
2674 save_menurc ($rc, $HOTKEYS);
2678 sub save_preferences {
2679 my $result = save_rc_preferences ()
2680 and save_ac_preferences ()
2681 and save_hk_preferences ();
2682 $MODIFIED = 0 if $result;
2688 my $nb = Gtk3::Notebook->new;
2690 $nb->append_page (new_behaviour_page (),
2691 Gtk3::Label->new ($xl::s{tab_behaviour}));
2692 $nb->append_page (new_colours_page (),
2693 Gtk3::Label->new ($xl::s{tab_colours}));
2694 $nb->append_page (new_gui_page (),
2695 Gtk3::Label->new ($xl::s{tab_gui}));
2696 $nb->append_page (new_other_page (),
2697 Gtk3::Label->new ($xl::s{tab_other}));
2698 $nb->append_page (new_winpos_page (),
2699 Gtk3::Label->new ($xl::s{tab_winpos}));
2700 $nb->append_page (new_accounts_page (),
2701 Gtk3::Label->new ($xl::s{tab_accounts}));
2702 $nb->append_page (new_plugins_page (),
2703 Gtk3::Label->new ($xl::s{tab_plugins}));
2704 $nb->append_page (new_hotkeys_page (),
2705 Gtk3::Label->new ($xl::s{tab_hotkeys}));
2706 $nb->append_page (new_info_page (),
2707 Gtk3::Label->new ($xl::s{tab_info}));
2712 # create an about dialog
2713 sub new_about_dialog {
2715 my $year = '2007-2018';
2716 my $holder = 'Ricardo Mones <ricardo@mones.org>';
2717 my $url = 'http://www.claws-mail.org/clawsker.php';
2718 my $icons = &get_app_icons;
2720 my $dialog = Gtk3::AboutDialog->new;
2721 $dialog->set_transient_for ($parent);
2722 $dialog->set_program_name ('Clawsker');
2723 $dialog->set_version ($VERSION);
2724 $dialog->set_copyright ("Copyright © $year $holder");
2725 $dialog->set_license_type ('gpl-3-0');
2726 $dialog->set_website ($url);
2727 $dialog->set_website_label ($xl::s{about_web_label});
2728 # committers, by number of commits
2729 $dialog->set_authors ([
2731 'Tristan Chabredier (wwp) <subscript@free.fr>',
2732 'Andreas Rönnquist <andreas@ronnquist.net>',
2733 'Christian Hesse <mail@eworm.de>',
2735 $dialog->set_artists ([
2736 'Jesper Schultz <jesper@schultz-net.dk>',
2739 $dialog->set_documenters ([
2741 'Paul Mangan <paul@claws-mail.org>',
2743 # active translators, in alphabetical order
2744 $dialog->set_translator_credits (join ("\n",
2745 'Andreas Rönnquist <andreas@ronnquist.net>',
2746 'Axel Köllhofer <AxelKoellhofer@web.de>',
2747 'David Medina <opensusecatala@gmail.com>',
2748 'Erik P. Olsen <erik@epo.dk>',
2749 'Frederico Goncalves Guimaraes <frederico@teia.bio.br>',
2750 'Marcel Pol <marcel@timelord.nl>',
2751 'Mark Chang <mark.cyj@gmail.com>',
2752 'M. Sulchan Darmawan <bleketux@gmail.com>',
2753 'Numan Demirdöğen <if.gnu.linux@gmail.com>',
2754 'Petter Adsen <petter@synth.no>',
2756 'Tristan Chabredier (wwp) <subscript@free.fr>',
2758 $dialog->set_title ($xl::s{about_title});
2759 $dialog->set_logo ($icons->[-1]);
2766 if ($MODIFIED != 0 and not $READONLY) {
2767 my $markup = "<span>" . $xl::s{exit_fact} . "</span>\n\n"
2768 . "<span weight=\"bold\">" . $xl::s{exit_question} . "</span>\n";
2769 my $dialog = message_dialog (
2770 $parent, $xl::s{exit_title}, $markup, 'question',
2771 [ 'gtk-no', 1, 'gtk-yes', 0 ]
2773 my $resp = $dialog->run;
2775 return TRUE if ($resp == 1);
2780 # create buttons box
2781 sub new_button_box {
2782 my ($parent, $adlg) = @_;
2783 my $b_about = Gtk3::Button->new_from_stock ('gtk-about');
2784 my $b_exit = Gtk3::Button->new_from_stock ('gtk-quit');
2785 my $b_apply = Gtk3::Button->new_from_stock ('gtk-apply');
2786 # disable button until is really implemented
2787 # my $b_undo = Gtk3::Button->new_from_stock ('gtk-undo');
2788 my $hbox = Gtk3::HBox->new (FALSE, 5);
2790 $b_exit->signal_connect (clicked => sub { exit_handler($parent) });
2791 $b_apply->set_sensitive (not $READONLY);
2792 $b_apply->signal_connect (clicked => sub { save_preferences($parent) });
2793 # $b_undo->signal_connect (clicked => sub { undo_current_changes });
2794 $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
2796 $hbox->pack_end ($b_apply, FALSE, FALSE, 0);
2797 $hbox->pack_end ($b_exit, FALSE, FALSE, 0);
2798 # $hbox->pack_end ($b_undo, FALSE, FALSE, 0);
2799 $hbox->pack_start ($b_about, FALSE, FALSE, 0);
2805 return \@APPICONS if (@APPICONS);
2807 if (-d $DATADIR) { # installed
2808 my $dir = $DATADIR . '/icons/hicolor';
2810 join ('/', ($dir, $_ . 'x' . $_, 'apps', $NAME . '.png'))
2812 } else { # unpacked tarball or git clone
2814 join ('/', ('./icons', $NAME . '-' . $_ . '.png'));
2819 $icon = Gtk3::Gdk::Pixbuf->new_from_file($_) if (-f $_);
2820 push @APPICONS, $icon if ($icon);
2825 sub escape_key_handler {
2826 my ($widget, $event) = @_;
2827 if ($event->keyval == Gtk3::Gdk::keyval_from_name('Escape')) {
2828 exit_handler($widget);
2833 exit unless parse_command_line ();
2835 $main_window = Gtk3::Window->new ('toplevel');
2836 exit unless load_preferences ();
2837 exit unless init_hidden_preferences ();
2839 my $box = Gtk3::VBox->new (FALSE, 5);
2840 $box->set_border_width(3);
2841 my $about = new_about_dialog ($main_window);
2842 $box->pack_start (new_notebook (), TRUE, TRUE, 0);
2843 $box->pack_end (new_button_box ($main_window, $about), FALSE, FALSE, 0);
2844 $main_window->signal_connect (delete_event => sub { exit_handler($main_window) });
2845 $main_window->signal_connect (key_press_event => \&escape_key_handler);
2846 $main_window->set_title ($xl::s{win_title});
2847 $main_window->set_icon_list (get_app_icons ());
2848 $main_window->add ($box);
2849 $main_window->show_all;