10 Clawsker - A Claws Mail Tweaker
18 Clawsker is an applet to edit the so called Claws Mail hidden preferences.
20 Claws Mail is a fast, lightweight and feature-rich MUA with a high number
21 of configurable options. To keep the binary small and fast some of these
22 preferences which are not widely used are not provided with a graphical
23 interface for inspection and/or modification.
25 Users wanting to edit such preferences had to face raw editing of their
26 configuration files, now you can do it with a convenient GTK2 interface
32 Shows a brief help screen.
35 Show information about program, Claws Mail and Perl-GTK versions.
38 Tells more on the standard output.
40 --alternate-config-dir <dir>
41 Uses <dir> as Claws Mail configuration dir.
44 Uses <file> as Claws Mail resource configuration file. This sets
45 the full file name overriding any previous setting.
47 Multiple options are allowed, although only the last one has effect. Weird
48 option specifications may produce weird results (but otherwise correct).
52 A running Claws Mail cannot be detected if using the --clawsrc option because
53 the directory is not assumed to be a Claws Mail configuration dir. If that is
54 the case use the --alternate-config-dir option instead.
58 Ricardo Mones E<lt>ricardo@mones.orgE<gt>
62 Copyright (c) 2007-2009 by Ricardo Mones Lastra
64 This program is free software: you can redistribute it and/or modify
65 it under the terms of the GNU General Public License as published by
66 the Free Software Foundation, either version 3 of the License, or
67 (at your option) any later version.
69 This program is distributed in the hope that it will be useful,
70 but WITHOUT ANY WARRANTY; without even the implied warranty of
71 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
72 GNU General Public License for more details.
74 You should have received a copy of the GNU General Public License
75 along with this program. If not, see E<lt>http://www.gnu.org/licenses/E<gt>.
81 use Glib qw(TRUE FALSE);
83 use POSIX qw(setlocale);
86 my $NAME = 'clawsker';
87 my $PREFIX = '@PREFIX@';
88 my $LIBDIR = '@LIBDIR@';
89 my $VERSION = '@VERSION@';
92 my $main_window = undef;
94 my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG});
95 $locale = "C" unless defined($locale);
96 setlocale (LC_ALL, $locale);
97 bindtextdomain ($NAME, sprintf ('%s/share/locale', $PREFIX));
100 my $SHOWHINTS = FALSE;
101 $SHOWHINTS = TRUE if ($Gtk2::VERSION >= 1.040 and Gtk2->CHECK_VERSION (2, 12, 0));
106 my $xla = gettext ($str);
107 if (scalar(keys(%par)) > 0) {
108 foreach my $key (keys %par) {
109 $xla =~ s/\{$key\}/$par{$key}/g;
117 win_title => _('Claws Mail Hidden Preferences'),
118 about => _('About...'),
119 about_title => _('Clawsker :: A Claws Mail Tweaker'),
120 about_license => _('License:'),
121 about_version => _('Version:'),
123 tab_colours => _('Colours'),
124 tab_behaviour => _('Behaviour'),
126 tab_other => _('Other'),
128 ab_frame => _('Addressbook'),
129 mem_frame => _('Memory'),
130 msgview_frame => _('Message view'),
131 log_frame => _('Log window'),
132 dnd_frame => _('Drag \'n\' drop'),
133 ssl_frame => _('Secure Sockets Layer'),
134 msgs_frame => _('Messages'),
135 stripes_frame => _('Coloured stripes'),
136 sbar_frame => _('Scroll bars'),
137 mlist_frame => _('Message List'),
138 netm_frame => _('NetworkManager'),
140 l_oth_use_dlg => _('Use detached address book edit dialogue'),
141 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.'),
142 l_oth_max_use => _('Maximum memory for message cache (kB)'),
143 h_oth_max_use => _('The maximum amount of memory to use to cache messages, in kilobytes.'),
144 l_oth_min_time => _('Minimun time for cache elements (minutes)'),
145 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.'),
146 l_oth_use_netm => _('Use NetworkManager'),
147 h_oth_use_netm => _('Use NetworkManager to switch offline automatically.'),
149 l_gui_b_unread => _('Show unread messages with bold font'),
150 h_gui_b_unread => _('Show unread messages in the Message List using a bold font.'),
151 l_gui_no_markup => _('Don\'t use markup in compose window'),
152 h_gui_no_markup => _('Don\'t use bold and italic text in Compose dialogue\'s account selector.'),
153 l_gui_dot_lines => _('Use dotted lines in tree view components'),
154 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.'),
155 l_gui_h_scroll => _('Enable horizontal scrollbar'),
156 h_gui_h_scroll => _('Enable the horizontal scrollbar in the Message List.'),
157 l_gui_swp_from => _('Display To column instead From column in Sent folder'),
158 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.'),
159 l_gui_v_scroll => _('Folder List scrollbar behaviour'),
160 h_gui_v_scroll => _('Specify the policy of vertical scrollbar of Folder List: show always, automatic or hide always.'),
161 l_gui_v_scroll_show => _('Show always'),
162 l_gui_v_scroll_auto => _('Automatic'),
163 l_gui_v_scroll_hide => _('Hide always'),
164 l_gui_strip_off => _('Coloured lines contrast'),
165 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.'),
166 l_gui_cursor_v => _('Show Cursor in message view'),
167 h_gui_cursor_v => _('Display the cursor in the message view.'),
168 l_gui_toolbar_d => _('Detachable toolbars'),
169 h_gui_toolbar_d => _('Show handles in the toolbars.'),
170 l_gui_strip_all => _('Use stripes in all tree view components'),
171 h_gui_strip_all => _('Enable alternately coloured lines in all tree view components.'),
172 l_gui_strip_sum => _('Use stripes in Folder List and Message List'),
173 h_gui_strip_sum => _('Enable alternately coloured lines in Message list and Folder list.'),
174 l_gui_two_line_v => _('2 lines per Message List item in 3-column layout'),
175 h_gui_two_line_v => _('Spread Message List information over two lines when using the three column mode.'),
177 l_beh_hover_t => _('Drag \'n\' drop hover timeout (ms)'),
178 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.'),
179 l_beh_dangerous => _('Don\'t confirm deletions (dangerous!)'),
180 h_beh_dangerous => _('Don\'t ask for confirmation before definitive deletion of emails.'),
181 l_beh_flowed => _('Respect format=flowed in messages'),
182 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.'),
183 l_beh_parts_rw => _('Allow writable temporary files'),
184 h_beh_parts_rw => _('Saves temporary files when opening attachment with write bit set.'),
185 l_beh_skip_ssl => _('Don\'t check SSL certificates'),
186 h_beh_skip_ssl => _('Disables the verification of SSL certificates.'),
187 l_beh_up_step => _('Progress bar update step (items)'),
188 h_beh_up_step => _('Update stepping in progress bars.'),
189 l_beh_thread_a => _('Maximum age when threading by subject (days)'),
190 h_beh_thread_a => _('Number of days to include a message in a thread when using "Thread using subject in addition to standard headers".'),
191 l_beh_unsafe_ssl => _('Allow unsafe SSL certificates'),
192 h_beh_unsafe_ssl => _('Allows Claws Mail to remember multiple SSL certificates for a given server/port.'),
193 l_beh_use_utf8 => _('Force UTF-8 for broken mails'),
194 h_beh_use_utf8 => _('Use UTF-8 encoding for broken mails instead of current locale.'),
195 l_beh_warn_dnd => _('Warn on drag \'n\' drop'),
196 h_beh_warn_dnd => _('Display a confirmation dialogue on drag \'n\' drop of folders.'),
197 l_beh_out_ascii => _('Ougoing messages fallback to ASCII'),
198 h_beh_out_ascii => _('If content allows, ASCII will be used to encode outgoing messages, otherwise the user-defined encoding is enforced always.'),
199 l_beh_pp_unsel => _('Primary paste unselects selection'),
200 h_beh_pp_unsel => _('Controls how pasting using middle-click changes the selected text and insertion point.'),
202 l_col_emphasis => _('X-Mailer header'),
203 h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
204 l_col_log_err => _('Error messages'),
205 h_col_log_err => _('Colour for error messages in log window.'),
206 l_col_log_in => _('Server messages'),
207 h_col_log_in => _('Colour for messages received from servers in log window.'),
208 l_col_log_msg => _('Standard messages'),
209 h_col_log_msg => _('Colour for messages in log window.'),
210 l_col_log_out => _('Client messages'),
211 h_col_log_out => _('Colour for messages sent to servers in log window.'),
212 l_col_log_warn => _('Warnings'),
213 h_col_log_warn => _('Colour for warning messages in log window.'),
215 e_error => _('Error: '),
216 e_noclawsrc => _('resource file for Claws Mail was not found.'),
217 e_running => _('seems Claws Mail is currently running, close it first.'),
218 e_requireddir => _('option requires a directory name.'),
219 e_requiredfile => _('option requires a file name.'),
220 e_notadir => _('specified name is not a directory or does not exist.'),
221 e_notafile => _('specified name is not a file or does not exist.'),
224 # all preferences read by load_preferences
226 # values of all preferences handled by clawsker
228 # default config dir and file name
229 my $ALTCONFIGDIR = FALSE;
230 my $CONFIGDIR = $ENV{HOME} . '/.claws-mail/';
231 my $CONFIGRC = 'clawsrc';
233 # index constants for preference arrays
234 use constant NAME => 0; # the name on the rc file
235 use constant LABEL => 1; # the label on the GUI
236 use constant DESC => 2; # the description for the hint/help
237 use constant TYPE => 3; # data type: bool, int, float, string, color
238 use constant CMVER => 4; # lowest Claws Mail version the feature exists
239 use constant CMDEF => 5; # default value for the preference in Claws Mail
240 use constant GUI => 6; # GUI element
242 # constants for GUI spacing
243 use constant HBOX_SPC => 5;
244 use constant FRAME_SPC => 2;
245 use constant PAGE_SPC => 5;
249 sub version_greater_or_equal() {
250 my ($version, $refvers) = @_;
251 my @version = split (/\./, $version);
252 my @refvers = split (/\./, $refvers);
253 while ($#version < $#refvers) {
254 push (@version, '0');
257 while (($idx <= $#refvers)
258 and (int ($version[$idx]) == int ($refvers[$idx]))) {
261 return TRUE if (($idx > $#refvers)
262 or (int ($version[$idx]) >= int ($refvers[$idx])));
266 sub get_claws_version() {
271 foreach my $bin (@cmbin) {
276 return "" unless ($_); # not installed
279 my @fver = split (/ /);
280 die "Invalid version string" unless ($fver[2] eq "version");
281 my @ver = split (/\./, $fver[3]);
284 if ($ver[2] =~ /(\d+)cvs(\d+)/) {
293 # data handlers and auxiliar functions
295 sub handle_bool_value {
296 my ($widget, $event, $dataref) = @_;
297 $$dataref = ($widget->get_active ())? '1': '0';
300 sub handle_int_value {
301 my ($widget, $event, $dataref) = @_;
302 $_ = $widget->get_text ();
307 $widget->set_text ($_);
310 $widget->set_text ($$dataref);
314 sub handle_string_value {
315 my ($widget, $event, $dataref) = @_;
316 $$dataref = $widget->get_text ();
319 sub gdk_color_from_str {
321 my ($rr, $gg, $bb) = (0, 0 ,0);
323 if (/\#([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])/) {
328 my $color = Gtk2::Gdk::Color->new ($rr, $gg, $bb);
332 sub str_from_gdk_color {
334 my $rr = $color->red / 256;
335 my $gg = $color->green / 256;
336 my $bb = $color->blue / 256;
337 my $str = sprintf ("#%.2x%.2x%.2x", $rr, $gg, $bb);
341 sub handle_color_value {
342 my ($widget, $event, $dataref) = @_;
343 my $newcol = $widget->get_color;
344 $$dataref = &str_from_gdk_color ($newcol);
347 sub handle_selection_value {
348 my ($widget, $event, $dataref) = @_;
349 $$dataref = $widget->get_active;
352 sub get_rc_filename {
353 return $CONFIGDIR . $CONFIGRC;
356 sub set_rc_filename {
358 my @parts = split ('/', $fullname);
359 $CONFIGRC = $parts[$#parts];
360 $parts[$#parts] = '';
361 $CONFIGDIR = join ('/', @parts);
365 my ($mesg, $fatal) = @_;
366 if (defined($fatal) && $fatal eq 'die') {
367 die "$NAME: $mesg\n";
370 print "$NAME: $mesg\n";
376 my $markup = "<span weight=\"bold\" size=\"large\">" . $emsg . "</span>";
377 my $errordlg = Gtk2::MessageDialog->new_with_markup ($main_window, 'modal', 'error', 'cancel', $markup);
378 $errordlg->set_title (_('Error message'));
383 sub check_claws_not_running() {
384 my $socket = (not $ALTCONFIGDIR)? "/tmp/": $CONFIGDIR;
385 $socket .= "claws-mail-$<";
387 my $emsg = "$xl::s{e_error}$xl::s{e_running}";
389 error_dialog ($emsg);
396 sub check_rc_file() {
398 (defined($rcfile) && -f $rcfile) or do {
399 my $emsg = "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
401 error_dialog ($emsg);
408 sub set_widget_hint() {
410 my ($wdgt, $hint) = @_;
411 $wdgt->set_tooltip_text ($hint);
412 $wdgt->set_has_tooltip (TRUE);
416 sub set_widget_sens() {
417 my ($wdgt, $minver) = @_;
418 $wdgt->set_sensitive (&version_greater_or_equal ($CLAWSV, $minver));
421 # graphic element creation
423 sub new_check_button_for {
424 my ($hash, $key) = @_;
425 my $name = $$hash{$key}[NAME];
426 my $label = $$hash{$key}[LABEL];
428 my $hbox = Gtk2::HBox->new (FALSE, 5);
429 my $cb = Gtk2::CheckButton->new ($label);
430 $$hash{$key}[GUI] = $cb;
431 if (defined ($HPVALUE{$name})) {
432 $cb->set_active ($HPVALUE{$name} eq '1');
434 $cb->signal_connect (clicked => sub {
436 &handle_bool_value($w, $e, \$HPVALUE{$name});
438 &set_widget_hint ($cb, $$hash{$key}[DESC]);
439 &set_widget_sens ($cb, $$hash{$key}[CMVER]);
440 $hbox->pack_start ($cb, FALSE, FALSE, HBOX_SPC);
445 sub new_text_box_for_int {
446 my ($hash, $key) = @_;
447 my $name = $$hash{$key}[NAME];
448 my $label = $$hash{$key}[LABEL];
449 my @type = split (/,/, $$hash{$key}[TYPE]);
450 push (@type, 0), push (@type, 10000) unless ($#type > 0);
452 my $hbox = Gtk2::HBox->new (FALSE, 5);
453 my $glabel = Gtk2::Label->new ($label);
454 my $pagei = int (($type[2] - $type[1]) / 10);
455 my $adjust = Gtk2::Adjustment->new (
456 $HPVALUE{$name}, $type[1], $type[2], 1, $pagei, 10
458 my $gentry = Gtk2::SpinButton->new ($adjust, 1, 0);
459 $gentry->set_numeric (TRUE);
460 $$hash{$key}[GUI] = $gentry;
461 $gentry->signal_connect('value-changed' => sub {
463 &handle_int_value($w, $e, \$HPVALUE{$name});
465 &set_widget_hint ($gentry, $$hash{$key}[DESC]);
466 &set_widget_sens ($gentry, $$hash{$key}[CMVER]);
467 $glabel->set_sensitive ($gentry->sensitive);
468 $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
469 $hbox->pack_start ($gentry, FALSE, FALSE, HBOX_SPC);
474 sub new_color_button_for {
475 my ($hash, $key) = @_;
476 my $name = $$hash{$key}[NAME];
477 my $label = $$hash{$key}[LABEL];
479 my $col = &gdk_color_from_str ($HPVALUE{$name});
480 my $hbox = Gtk2::HBox->new (FALSE, 5);
481 my $glabel = Gtk2::Label->new ($label);
482 my $button = Gtk2::ColorButton->new_with_color ($col);
483 $$hash{$key}[GUI] = $button;
484 $button->set_title ($label);
485 $button->set_relief ('none');
486 $button->signal_connect ('color-set' => sub {
488 &handle_color_value($w, $e, \$HPVALUE{$name});
490 &set_widget_hint ($button, $$hash{$key}[DESC]);
491 &set_widget_sens ($button, $$hash{$key}[CMVER]);
492 $glabel->set_sensitive ($button->sensitive);
493 $hbox->pack_start ($button, FALSE, FALSE, HBOX_SPC);
494 $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
499 sub new_selection_box_for {
500 my ($hash, $key) = @_;
501 my $name = $$hash{$key}[NAME];
502 my $label = $$hash{$key}[LABEL];
504 my $hbox = Gtk2::HBox->new (FALSE, 5);
505 my $glabel = Gtk2::Label->new ($label);
506 my $combo = Gtk2::ComboBox->new_text;
507 $$hash{$key}[GUI] = $combo;
508 my @options = split (';', $$hash{$key}[TYPE]);
509 foreach my $opt (@options) {
510 my ($index, $textkey) = split ('=', $opt);
511 $combo->insert_text ($index, $xl::s{$textkey});
513 $combo->signal_connect ('changed' => sub {
515 &handle_selection_value($w, $e, \$HPVALUE{$name});
517 $combo->set_active ($HPVALUE{$name});
518 &set_widget_hint ($combo, $$hash{$key}[DESC]);
519 &set_widget_sens ($combo, $$hash{$key}[CMVER]);
520 $glabel->set_sensitive ($combo->sensitive);
521 $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
522 $hbox->pack_start ($combo, FALSE, FALSE, HBOX_SPC);
527 # preference maps and corresponding page creation subs
529 %pr::oth = ( # other preferences
531 'addressbook_use_editaddress_dialog',
532 $xl::s{l_oth_use_dlg},
533 $xl::s{h_oth_use_dlg},
540 'cache_max_mem_usage',
541 $xl::s{l_oth_max_use},
542 $xl::s{h_oth_max_use},
543 'int,0,262144', # 0 Kb - 256 Mb
549 'cache_min_keep_time',
550 $xl::s{l_oth_min_time},
551 $xl::s{h_oth_min_time},
552 'int,0,120', # 0 minutes - 2 hours
558 'use_networkmanager',
559 $xl::s{l_oth_use_netm},
560 $xl::s{h_oth_use_netm},
568 sub new_other_page() {
569 my $of = Gtk2::VBox->new (FALSE, 5);
570 $of->set_border_width (PAGE_SPC);
572 my $ab_frame = Gtk2::Frame->new ($xl::s{ab_frame});
573 my $cb_use_dlg = &new_check_button_for(\%pr::oth, 'use_dlg');
574 my $vb1 = Gtk2::VBox->new (FALSE, 5);
575 $vb1->set_border_width (PAGE_SPC);
576 $vb1->pack_start ($cb_use_dlg, FALSE, FALSE, 0);
577 $ab_frame->add ($vb1);
579 my $mem_frame = Gtk2::Frame->new ($xl::s{mem_frame});
580 my $tb_max_use = &new_text_box_for_int(\%pr::oth, 'max_use');
581 my $tb_min_time = &new_text_box_for_int(\%pr::oth, 'min_time');
582 my $vb2 = Gtk2::VBox->new (FALSE, 5);
583 $vb2->set_border_width (PAGE_SPC);
584 $vb2->pack_start ($tb_max_use, FALSE, FALSE, 0);
585 $vb2->pack_start ($tb_min_time, FALSE, FALSE, 0);
586 $mem_frame->add ($vb2);
588 my $netm_frame = Gtk2::Frame->new ($xl::s{netm_frame});
589 my $cb_use_netm = &new_check_button_for(\%pr::oth, 'use_netm');
590 my $vb3 = Gtk2::VBox->new (FALSE, 5);
591 $vb3->set_border_width (PAGE_SPC);
592 $vb3->pack_start ($cb_use_netm, FALSE, FALSE, 0);
593 $netm_frame->add ($vb3);
595 $of->pack_start ($ab_frame, FALSE, FALSE, FRAME_SPC);
596 $of->pack_start ($mem_frame, FALSE, FALSE, FRAME_SPC);
597 $of->pack_start ($netm_frame, FALSE, FALSE, FRAME_SPC);
602 %pr::gui = ( # gui bells and whistles
605 $xl::s{l_gui_b_unread},
606 $xl::s{h_gui_b_unread},
614 $xl::s{l_gui_no_markup},
615 $xl::s{h_gui_no_markup},
622 'enable_dotted_lines',
623 $xl::s{l_gui_dot_lines},
624 $xl::s{h_gui_dot_lines},
632 $xl::s{l_gui_h_scroll},
633 $xl::s{h_gui_h_scroll},
641 $xl::s{l_gui_swp_from},
642 $xl::s{h_gui_swp_from},
649 'folderview_vscrollbar_policy',
650 $xl::s{l_gui_v_scroll},
651 $xl::s{h_gui_v_scroll},
652 '0=l_gui_v_scroll_show;1=l_gui_v_scroll_auto;2=l_gui_v_scroll_hide',
658 'stripes_color_offset',
659 $xl::s{l_gui_strip_off},
660 $xl::s{h_gui_strip_off},
661 'int,0,10000', # no idea what this number means
667 'textview_cursor_visible',
668 $xl::s{l_gui_cursor_v},
669 $xl::s{h_gui_cursor_v},
676 'toolbar_detachable',
677 $xl::s{l_gui_toolbar_d},
678 $xl::s{h_gui_toolbar_d},
685 'use_stripes_everywhere',
686 $xl::s{l_gui_strip_all},
687 $xl::s{h_gui_strip_all},
694 'use_stripes_in_summaries',
695 $xl::s{l_gui_strip_sum},
696 $xl::s{h_gui_strip_sum},
704 $xl::s{l_gui_two_line_v},
705 $xl::s{h_gui_two_line_v},
714 my $gf = Gtk2::VBox->new (FALSE, 5);
715 $gf->set_border_width (PAGE_SPC);
717 my $stripes_frame = Gtk2::Frame->new ($xl::s{stripes_frame});
718 my $cb_strip_all = &new_check_button_for (\%pr::gui, 'strip_all');
719 my $cb_strip_sum = &new_check_button_for (\%pr::gui, 'strip_sum');
720 my $tb_strip_off = &new_text_box_for_int (\%pr::gui, 'strip_off');
721 my $vb1 = Gtk2::VBox->new (FALSE, 5);
722 $vb1->set_border_width (PAGE_SPC);
723 $vb1->pack_start ($cb_strip_all, FALSE, FALSE, 0);
724 $vb1->pack_start ($cb_strip_sum, FALSE, FALSE, 0);
725 $vb1->pack_start ($tb_strip_off, FALSE, FALSE, 0);
726 $stripes_frame->add ($vb1);
728 my $mlist_frame = Gtk2::Frame->new ($xl::s{mlist_frame});
729 my $cb_b_unread = &new_check_button_for (\%pr::gui, 'b_unread');
730 my $cb_swp_from = &new_check_button_for (\%pr::gui, 'swp_from');
731 my $vb3 = Gtk2::VBox->new (FALSE, 5);
732 $vb3->set_border_width (PAGE_SPC);
733 $vb3->pack_start ($cb_b_unread, FALSE, FALSE, 0);
734 $vb3->pack_start ($cb_swp_from, FALSE, FALSE, 0);
735 $mlist_frame->add ($vb3);
737 my $sbar_frame = Gtk2::Frame->new ($xl::s{sbar_frame});
738 my $cb_h_scroll = &new_check_button_for (\%pr::gui, 'h_scroll');
739 my $sb_v_scroll = &new_selection_box_for (\%pr::gui, 'v_scroll');
740 my $vb2 = Gtk2::VBox->new (FALSE, 5);
741 $vb2->set_border_width (PAGE_SPC);
742 $vb2->pack_start ($cb_h_scroll, FALSE, FALSE, 0);
743 $vb2->pack_start ($sb_v_scroll, FALSE, FALSE, 0);
744 $sbar_frame->add ($vb2);
746 my $cb_no_markup = &new_check_button_for (\%pr::gui, 'no_markup');
747 my $cb_dot_lines = &new_check_button_for (\%pr::gui, 'dot_lines');
748 my $cb_cursor_v = &new_check_button_for (\%pr::gui, 'cursor_v');
749 my $cb_toolbar_d = &new_check_button_for (\%pr::gui, 'toolbar_d');
750 my $cb_two_linev = &new_check_button_for (\%pr::gui, 'two_linev');
752 $gf->pack_start ($stripes_frame, FALSE, FALSE, FRAME_SPC);
753 $gf->pack_start ($mlist_frame, FALSE, FALSE, FRAME_SPC);
754 $gf->pack_start ($cb_no_markup, FALSE, FALSE, 0);
755 $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
756 $gf->pack_start ($cb_cursor_v, FALSE, FALSE, 0);
757 $gf->pack_start ($cb_toolbar_d, FALSE, FALSE, 0);
758 $gf->pack_start ($cb_two_linev, FALSE, FALSE, 0);
759 $gf->pack_start ($sbar_frame, FALSE, FALSE, FRAME_SPC);
764 %pr::beh = ( # tweak some behaviour
767 $xl::s{l_beh_hover_t},
768 $xl::s{h_beh_hover_t},
769 'int,100,3000', # 0.1 seconds - 3 seconds
776 $xl::s{l_beh_dangerous},
777 $xl::s{h_beh_dangerous},
784 'respect_flowed_format',
785 $xl::s{l_beh_flowed},
786 $xl::s{h_beh_flowed},
793 'save_parts_readwrite',
794 $xl::s{l_beh_parts_rw},
795 $xl::s{h_beh_parts_rw},
802 'skip_ssl_cert_check',
803 $xl::s{l_beh_skip_ssl},
804 $xl::s{h_beh_skip_ssl},
811 'statusbar_update_step',
812 $xl::s{l_beh_up_step},
813 $xl::s{h_beh_up_step},
814 'int,1,200', # 1 item - 200 items
820 'thread_by_subject_max_age',
821 $xl::s{l_beh_thread_a},
822 $xl::s{h_beh_thread_a},
823 'int,1,30', # 1 day - 30 days
830 $xl::s{l_beh_unsafe_ssl},
831 $xl::s{h_beh_unsafe_ssl},
838 'utf8_instead_of_locale_for_broken_mail',
839 $xl::s{l_beh_use_utf8},
840 $xl::s{h_beh_use_utf8},
848 $xl::s{l_beh_warn_dnd},
849 $xl::s{h_beh_warn_dnd},
856 'outgoing_fallback_to_ascii',
857 $xl::s{l_beh_out_ascii},
858 $xl::s{h_beh_out_ascii},
865 'primary_paste_unselects',
866 $xl::s{l_beh_pp_unsel},
867 $xl::s{h_beh_pp_unsel},
875 sub new_behaviour_page() {
876 my $bf = Gtk2::VBox->new (FALSE, 5);
877 $bf->set_border_width (PAGE_SPC);
879 my $dnd_frame = Gtk2::Frame->new ($xl::s{dnd_frame});
880 my $tb_hoover_t = &new_text_box_for_int (\%pr::beh, 'hover_t');
881 my $cb_warn_dnd = &new_check_button_for (\%pr::beh, 'warn_dnd');
882 my $vb1 = Gtk2::VBox->new (FALSE, 5);
883 $vb1->set_border_width (PAGE_SPC);
884 $vb1->pack_start ($cb_warn_dnd, FALSE, FALSE, 0);
885 $vb1->pack_start ($tb_hoover_t, FALSE, FALSE, 0);
886 $dnd_frame->add ($vb1);
888 my $ssl_frame = Gtk2::Frame->new ($xl::s{ssl_frame});
889 my $cb_skip_ssl = &new_check_button_for (\%pr::beh, 'skip_ssl');
890 my $cb_unsafe_ssl = &new_check_button_for (\%pr::beh, 'unsafe_ssl');
891 my $hb1 = Gtk2::HBox->new (FALSE, 5);
892 $hb1->set_border_width (PAGE_SPC);
893 $hb1->pack_start ($cb_skip_ssl, FALSE, FALSE, 0);
894 $hb1->pack_start ($cb_unsafe_ssl, FALSE, FALSE, 0);
895 $ssl_frame->add ($hb1);
897 my $tb_up_step = &new_text_box_for_int (\%pr::beh, 'up_step');
898 my $tb_thread_a = &new_text_box_for_int (\%pr::beh, 'thread_a');
900 my $msgs_frame = Gtk2::Frame->new ($xl::s{msgs_frame});
901 my $cb_flowed = &new_check_button_for (\%pr::beh, 'flowed');
902 my $cb_parts_rw = &new_check_button_for (\%pr::beh, 'parts_rw');
903 my $cb_use_utf8 = &new_check_button_for (\%pr::beh, 'use_utf8');
904 my $cb_dangerous = &new_check_button_for (\%pr::beh, 'dangerous');
905 my $cb_out_ascii = &new_check_button_for (\%pr::beh, 'out_ascii');
906 my $cb_pp_unsel = &new_check_button_for (\%pr::beh, 'pp_unsel');
907 my $vb2 = Gtk2::VBox->new (FALSE, 5);
908 $vb2->set_border_width (PAGE_SPC);
909 $vb2->pack_start ($cb_flowed, FALSE, FALSE, 0);
910 $vb2->pack_start ($cb_parts_rw, FALSE, FALSE, 0);
911 $vb2->pack_start ($cb_use_utf8, FALSE, FALSE, 0);
912 $vb2->pack_start ($cb_dangerous, FALSE, FALSE, 0);
913 $vb2->pack_start ($cb_out_ascii, FALSE, FALSE, 0);
914 $vb2->pack_start ($cb_pp_unsel, FALSE, FALSE, 0);
915 $msgs_frame->add ($vb2);
917 $bf->pack_start ($dnd_frame, FALSE, FALSE, FRAME_SPC);
918 $bf->pack_start ($ssl_frame, FALSE, FALSE, FRAME_SPC);
919 $bf->pack_start ($tb_up_step, FALSE, FALSE, 0);
920 $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0);
921 $bf->pack_start ($msgs_frame, FALSE, FALSE, FRAME_SPC);
926 %pr::col = ( # a variety of colours
929 $xl::s{l_col_emphasis},
930 $xl::s{h_col_emphasis},
938 $xl::s{l_col_log_err},
939 $xl::s{h_col_log_err},
947 $xl::s{l_col_log_in},
948 $xl::s{h_col_log_in},
956 $xl::s{l_col_log_msg},
957 $xl::s{h_col_log_msg},
965 $xl::s{l_col_log_out},
966 $xl::s{h_col_log_out},
974 $xl::s{l_col_log_warn},
975 $xl::s{h_col_log_warn},
983 sub new_colours_page() {
984 my $cf = Gtk2::VBox->new (FALSE, 5);
985 $cf->set_border_width (PAGE_SPC);
987 my $msgview_frame = Gtk2::Frame->new ($xl::s{msgview_frame});
988 my $cb_emphasis = &new_color_button_for (\%pr::col, 'emphasis');
989 my $vb1 = Gtk2::VBox->new (FALSE, 5);
990 $vb1->set_border_width (PAGE_SPC);
991 $vb1->pack_start ($cb_emphasis, FALSE, FALSE, 0);
992 $msgview_frame->add ($vb1);
994 my $log_frame = Gtk2::Frame->new ($xl::s{log_frame});
995 my $cb_log_err = &new_color_button_for (\%pr::col, 'log_err');
996 my $cb_log_in = &new_color_button_for (\%pr::col, 'log_in');
997 my $cb_log_msg = &new_color_button_for (\%pr::col, 'log_msg');
998 my $cb_log_out = &new_color_button_for (\%pr::col, 'log_out');
999 my $cb_log_warn = &new_color_button_for (\%pr::col, 'log_warn');
1000 my $vb2 = Gtk2::VBox->new (FALSE, 5);
1001 $vb2->set_border_width (PAGE_SPC);
1002 $vb2->pack_start ($cb_log_err, FALSE, FALSE, 0);
1003 $vb2->pack_start ($cb_log_in, FALSE, FALSE, 0);
1004 $vb2->pack_start ($cb_log_msg, FALSE, FALSE, 0);
1005 $vb2->pack_start ($cb_log_out, FALSE, FALSE, 0);
1006 $vb2->pack_start ($cb_log_warn, FALSE, FALSE, 0);
1007 $log_frame->add ($vb2);
1009 $cf->pack_start ($msgview_frame, FALSE, FALSE, 0);
1010 $cf->pack_start ($log_frame, FALSE, FALSE, 0);
1016 sub print_version() {
1017 print $xl::s{about_title} . "\n";
1018 print $xl::s{about_version} . " $VERSION\n";
1019 print "Perl-GLib " . $Glib::VERSION;
1020 # version info stuff appeared in 1.040
1021 if ($Glib::VERSION >= 1.040) {
1022 print _(", built for ") . join(".", Glib->GET_VERSION_INFO)
1023 . _(", running with ") . join(".", &Glib::major_version,
1024 &Glib::minor_version, &Glib::micro_version);
1027 print "Perl-GTK2 " . $Gtk2::VERSION;
1028 if ($Gtk2::VERSION >= 1.040) {
1029 print _(", built for ") . join(".", Gtk2->GET_VERSION_INFO)
1030 . _(", running with ") . join(".", &Gtk2::major_version,
1031 &Gtk2::minor_version, &Gtk2::micro_version);
1034 my $clawsver = ($CLAWSV eq "") ?
1035 _("was not found!") :
1036 (_("returned version ") . $CLAWSV);
1037 print "Claws Mail " . $clawsver . "\n";
1040 # the command line help
1042 my $line = '-' x length ($xl::s{about_title}) . "\n";
1044 print $xl::s{about_title} . "\n";
1046 print _("Syntax:\n");
1047 print _(" clawsker [options]\n");
1048 print _("Options:\n");
1049 print _(" --help Prints this help screen.\n");
1050 print _(" --version Prints version infos.\n");
1051 print _(" --verbose More messages on standard output.\n");
1052 print _(" --alternate-config-dir <dir> Uses <dir> as Claws Mail config dir.\n");
1053 print _(" --clawsrc <file> Uses <file> as full resource name.\n");
1056 # handle errors which don't allow to run
1057 sub command_line_fatal() {
1059 my $emsg = $xl::s{e_error} . $reason;
1060 error_dialog ($emsg);
1061 log_message ("$emsg", 'die');
1064 # parse the command line
1065 sub parse_command_line() {
1066 $CLAWSV = &get_claws_version;
1068 while (defined($ARGV[$arg])) {
1082 /--use-claws-version/ && do {
1084 &command_line_fatal ("required version")
1085 unless defined($ARGV[$arg]);
1086 &command_line_fatal ("required a dotted numeric value")
1087 unless ($ARGV[$arg] =~ /[\d\.]+/);
1088 $CLAWSV = $ARGV[$arg];
1091 /--alternate-config-dir/ && do {
1093 &command_line_fatal ($xl::s{e_requireddir})
1094 unless defined($ARGV[$arg]);
1095 &command_line_fatal ($xl::s{e_notadir})
1096 unless -d $ARGV[$arg];
1097 $CONFIGDIR = $ARGV[$arg];
1099 unless ($CONFIGDIR =~ /.*\/$/);
1100 $ALTCONFIGDIR = TRUE;
1105 &command_line_fatal($xl::s{e_requiredfile})
1106 unless defined($ARGV[$arg]);
1107 &command_line_fatal($xl::s{e_notafile})
1108 unless -f $ARGV[$arg];
1109 &set_rc_filename ($ARGV[$arg]);
1112 /.*/ && &command_line_fatal (
1113 _("unknown option '{opt}'.\n", opt => $ARGV[$arg]));
1117 # eveything continues...
1121 # update the hidden preferences status from loaded values
1122 sub init_hidden_preferences() {
1123 foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth) {
1124 foreach my $key (keys %$hash) {
1125 $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]};
1131 # load current status from disc
1132 sub load_preferences() {
1133 my $rc = &get_rc_filename;
1134 &log_message ("Loading preferences from $rc\n");
1135 return FALSE unless &check_rc_file ($rc);
1136 return FALSE unless &check_claws_not_running;
1140 if (/^([8a-z_]+)=(.*)$/) {
1148 # save current preferences to disc
1149 sub save_preferences() {
1150 my $rc = &get_rc_filename;
1151 &log_message ("Saving preferences to $rc\n");
1152 return FALSE unless &check_rc_file ($rc);
1153 return FALSE unless &check_claws_not_running;
1154 my $rcbak = "$rc.backup";
1155 rename ($rc, $rcbak);
1157 open (RCB, "<$rcbak");
1160 if (/^([8a-z_]+)=(.*)$/) {
1161 if (defined($HPVALUE{$1})) {
1162 print RCF $1 . "=" . $HPVALUE{$1} . "\n";
1165 print RCF $_ . "\n";
1169 print RCF $_ . "\n";
1178 sub new_notebook() {
1179 my $nb = Gtk2::Notebook->new;
1181 $nb->append_page (&new_behaviour_page, $xl::s{tab_behaviour});
1182 $nb->append_page (&new_colours_page, $xl::s{tab_colours});
1183 $nb->append_page (&new_gui_page, $xl::s{tab_gui});
1184 $nb->append_page (&new_other_page, $xl::s{tab_other});
1189 # create an about dialog
1190 sub new_about_dialog() {
1192 my $title = $xl::s{about_title};
1193 my $lic = $xl::s{about_license};
1194 my $vers = $xl::s{about_version} . " $VERSION";
1196 "This program is free software: you can redistribute it and/or modify
1197 it under the terms of the GNU General Public License as published by
1198 the Free Software Foundation, either version 3 of the License, or
1199 (at your option) any later version.
1201 This program is distributed in the hope that it will be useful,
1202 but WITHOUT ANY WARRANTY; without even the implied warranty of
1203 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1204 GNU General Public License for more details.
1206 You should have received a copy of the GNU General Public License
1207 along with this program. If not, see <http://www.gnu.org/licenses/>.";
1208 my $year = "2007-2009";
1209 my $holder = "Ricardo Mones <ricardo\@mones.org>";
1211 my $dialog = Gtk2::MessageDialog->new_with_markup ($parent,
1212 [qw/modal destroy-with-parent/],
1214 "<span size=\"x-large\" weight=\"bold\">$title</span>\n"
1215 . "<span size=\"large\">$vers</span>\n"
1216 . "<span>Copyright $year by $holder</span>\n\n"
1217 . "<span size=\"large\">$lic</span>\n\n"
1218 . "<span size=\"small\">$license</span>");
1219 $dialog->set_title ($xl::s{about});
1224 # create buttons box
1225 sub new_button_box() {
1226 my ($parent, $adlg) = @_;
1227 my $b_about = Gtk2::Button->new_from_stock ('gtk-about');
1228 my $b_exit = Gtk2::Button->new_from_stock ('gtk-quit');
1229 my $b_apply = Gtk2::Button->new_from_stock ('gtk-apply');
1230 # disable button until is really implemented
1231 # my $b_undo = Gtk2::Button->new_from_stock ('gtk-undo');
1232 my $hbox = Gtk2::HBox->new (FALSE, 5);
1234 $b_exit->signal_connect (clicked => sub { Gtk2->main_quit });
1235 $b_apply->signal_connect (clicked => sub { &save_preferences ($parent) });
1236 # $b_undo->signal_connect (clicked => sub { &undo_current_changes });
1237 $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
1239 $hbox->pack_end ($b_apply, FALSE, FALSE, 0);
1240 $hbox->pack_end ($b_exit, FALSE, FALSE, 0);
1241 # $hbox->pack_end ($b_undo, FALSE, FALSE, 0);
1242 $hbox->pack_start ($b_about, FALSE, FALSE, 0);
1248 $main_window = Gtk2::Window->new ('toplevel');
1249 exit unless &parse_command_line;
1250 exit unless &load_preferences;
1251 exit unless &init_hidden_preferences;
1253 my $box = Gtk2::VBox->new (FALSE, 5);
1254 $box->set_border_width(3);
1255 my $about = &new_about_dialog;
1256 $box->pack_start (&new_notebook, FALSE, FALSE, 0);
1257 $box->pack_end (&new_button_box ($main_window, $about), FALSE, FALSE, 0);
1258 $main_window->signal_connect (delete_event => sub { Gtk2->main_quit });
1259 $main_window->set_title ($xl::s{win_title});
1260 $main_window->add ($box);
1261 $main_window->show_all;