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 Tells more on the standard output.
37 --alternate-config-dir <dir>
38 Uses <dir> as Claws Mail configuration dir.
41 Uses <file> as Claws Mail resource configuration file. This sets
42 the full file name overriding any previous setting.
44 Multiple options are allowed, although only the last one has effect. Weird
45 option specifications may produce weird results (but otherwise correct).
49 A running Claws Mail cannot be detected if using the --clawsrc option because
50 the directory is not assumed to be a Claws Mail configuration dir. If that is
51 the case use the --alternate-config-dir option instead.
55 Ricardo Mones E<lt>ricardo@mones.orgE<gt>
59 Copyright (c) 2007-2008 by Ricardo Mones Lastra
61 This program is free software: you can redistribute it and/or modify
62 it under the terms of the GNU General Public License as published by
63 the Free Software Foundation, either version 3 of the License, or
64 (at your option) any later version.
66 This program is distributed in the hope that it will be useful,
67 but WITHOUT ANY WARRANTY; without even the implied warranty of
68 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
69 GNU General Public License for more details.
71 You should have received a copy of the GNU General Public License
72 along with this program. If not, see E<lt>http://www.gnu.org/licenses/E<gt>.
78 use Glib qw(TRUE FALSE);
80 use POSIX qw(setlocale);
83 my $NAME = 'clawsker';
84 my $PREFIX = '@PREFIX@';
85 my $LIBDIR = '@LIBDIR@';
86 my $VERSION = '@VERSION@';
88 my $main_window = undef;
90 my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG});
91 $locale = "C" unless defined($locale);
92 setlocale (LC_ALL, $locale);
93 bindtextdomain ($NAME, sprintf ('%s/share/locale', $PREFIX));
96 my $SHOWHINTS = FALSE;
97 $SHOWHINTS = TRUE if ($Gtk2::VERSION >= 1.040 and Gtk2->CHECK_VERSION (2, 12, 0));
102 my $xla = gettext ($str);
103 if (scalar(keys(%par)) > 0) {
104 foreach my $key (keys %par) {
105 $xla =~ s/\{$key\}/$par{$key}/g;
113 win_title => _('Claws Mail Hidden Preferences'),
114 about => _('About...'),
115 about_title => _('Clawsker :: A Claws Mail Tweaker'),
116 about_license => _('License:'),
117 about_version => _('Version:'),
119 tab_colours => _('Colours'),
120 tab_behaviour => _('Behaviour'),
122 tab_other => _('Other'),
124 ab_frame => _('Addressbook'),
125 mem_frame => _('Memory'),
126 msgview_frame => _('Message view'),
127 log_frame => _('Log window'),
128 dnd_frame => _('Drag \'n\' drop'),
129 ssl_frame => _('Secure Sockets Layer'),
130 msgs_frame => _('Messages'),
131 stripes_frame => _('Coloured stripes'),
132 sbar_frame => _('Scroll bars'),
133 mlist_frame => _('Message List'),
134 netm_frame => _('NetworkManager'),
136 l_oth_use_dlg => _('Use detached address book edit dialogue'),
137 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.'),
138 l_oth_max_use => _('Maximum memory for message cache (kB)'),
139 h_oth_max_use => _('The maximum amount of memory to use to cache messages, in kilobytes.'),
140 l_oth_min_time => _('Minimun time for cache elements (minutes)'),
141 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.'),
142 l_oth_use_netm => _('Use NetworkManager'),
143 h_oth_use_netm => _('Use NetworkManager to switch offline automatically.'),
145 l_gui_b_unread => _('Show unread messages with bold font'),
146 h_gui_b_unread => _('Show unread messages in the Message List using a bold font.'),
147 l_gui_no_markup => _('Don\'t use markup in compose window'),
148 h_gui_no_markup => _('Don\'t use bold and italic text in Compose dialogue\'s account selector.'),
149 l_gui_dot_lines => _('Use dotted lines in tree view components'),
150 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.'),
151 l_gui_h_scroll => _('Enable horizontal scrollbar'),
152 h_gui_h_scroll => _('Enable the horizontal scrollbar in the Message List.'),
153 l_gui_swp_from => _('Dislay To column instead From column in Sent folder'),
154 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.'),
155 l_gui_v_scroll => _('Folder List scrollbar behaviour'),
156 h_gui_v_scroll => _('Specify the policy of vertical scrollbar of Folder List: show always, automatic or hide always.'),
157 l_gui_v_scroll_show => _('Show always'),
158 l_gui_v_scroll_auto => _('Automatic'),
159 l_gui_v_scroll_hide => _('Hide always'),
160 l_gui_strip_off => _('Coloured lines contrast'),
161 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.'),
162 l_gui_cursor_v => _('Show Cursor in message view'),
163 h_gui_cursor_v => _('Display the cursor in the message view.'),
164 l_gui_toolbar_d => _('Detachable toolbars'),
165 h_gui_toolbar_d => _('Show handles in the toolbars.'),
166 l_gui_strip_all => _('Use stripes in all tree view components'),
167 h_gui_strip_all => _('Enable alternately coloured lines in all tree view components.'),
168 l_gui_strip_sum => _('Use stripes in Folder List and Message List'),
169 h_gui_strip_sum => _('Enable alternately coloured lines in Message list and Folder list.'),
170 l_gui_two_line_v => _('2 lines per Message List item in 3-column layout'),
171 h_gui_two_line_v => _('Spread Message List information over two lines when using the three column mode.'),
173 l_beh_hover_t => _('Drag \'n\' drop hover timeout (ms)'),
174 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.'),
175 l_beh_dangerous => _('Don\'t confirm deletions (dangerous!)'),
176 h_beh_dangerous => _('Don\'t ask for confirmation before definitive deletion of emails.'),
177 l_beh_flowed => _('Respect format=flowed in messages'),
178 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.'),
179 l_beh_parts_rw => _('Allow writable temporary files'),
180 h_beh_parts_rw => _('Saves temporary files when opening attachment with write bit set.'),
181 l_beh_skip_ssl => _('Don\'t check SSL certificates'),
182 h_beh_skip_ssl => _('Disables the verification of SSL certificates.'),
183 l_beh_up_step => _('Progress bar update step (items)'),
184 h_beh_up_step => _('Update stepping in progress bars.'),
185 l_beh_thread_a => _('Maximum age when threading by subject (days)'),
186 h_beh_thread_a => _('Number of days to include a message in a thread when using "Thread using subject in addition to standard headers".'),
187 l_beh_unsafe_ssl => _('Allow unsafe SSL certificates'),
188 h_beh_unsafe_ssl => _('Allows Claws Mail to remember multiple SSL certificates for a given server/port.'),
189 l_beh_use_utf8 => _('Force UTF-8 for broken mails'),
190 h_beh_use_utf8 => _('Use UTF-8 encoding for broken mails instead of current locale.'),
191 l_beh_warn_dnd => _('Warn on drag \'n\' drop'),
192 h_beh_warn_dnd => _('Display a confirmation dialogue on drag \'n\' drop of folders.'),
193 l_beh_out_ascii => _('Ougoing messages fallback to ASCII'),
194 h_beh_out_ascii => _('If content allows, ASCII will be used to encode outgoing messages, otherwise the user-defined encoding is enforced always.'),
196 l_col_emphasis => _('X-Mailer header'),
197 h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
198 l_col_log_err => _('Error messages'),
199 h_col_log_err => _('Colour for error messages in log window.'),
200 l_col_log_in => _('Server messages'),
201 h_col_log_in => _('Colour for messages received from servers in log window.'),
202 l_col_log_msg => _('Standard messages'),
203 h_col_log_msg => _('Colour for messages in log window.'),
204 l_col_log_out => _('Client messages'),
205 h_col_log_out => _('Colour for messages sent to servers in log window.'),
206 l_col_log_warn => _('Warnings'),
207 h_col_log_warn => _('Colour for warning messages in log window.'),
209 e_error => _('Error: '),
210 e_noclawsrc => _('resource file for Claws Mail was not found.'),
211 e_running => _('seems Claws Mail is currently running, close it first.'),
212 e_requireddir => _('option requires a directory name.'),
213 e_requiredfile => _('option requires a file name.'),
214 e_notadir => _('specified name is not a directory or does not exist.'),
215 e_notafile => _('specified name is not a file or does not exist.'),
218 # all preferences read by load_preferences
220 # values of all preferences handled by clawsker
222 # default config dir and file name
223 my $ALTCONFIGDIR = FALSE;
224 my $CONFIGDIR = $ENV{HOME} . '/.claws-mail/';
225 my $CONFIGRC = 'clawsrc';
227 # index constants for preference arrays
228 use constant NAME => 0; # the name on the rc file
229 use constant LABEL => 1; # the label on the GUI
230 use constant DESC => 2; # the description for the hint/help
231 use constant TYPE => 3; # data type: bool, int, float, string, color
232 use constant CMVER => 4; # lowest Claws Mail version the feature exists
233 use constant CMDEF => 5; # default value for the preference in Claws Mail
234 use constant GUI => 6; # GUI element
236 # constants for GUI spacing
237 use constant HBOX_SPC => 5;
238 use constant FRAME_SPC => 2;
239 use constant PAGE_SPC => 5;
243 sub version_greater() {
244 my ($version, $refvers) = @_;
245 my @version = split (/\./, $version);
246 my @refvers = split (/\./, $refvers);
247 while ($#version < $#refvers) {
248 push (@version, '0');
251 while (($idx <= $#refvers)
252 and (int ($version[$idx]) == int ($refvers[$idx]))) {
255 return TRUE if (($idx > $#refvers)
256 or (int ($version[$idx]) > int ($refvers[$idx])));
260 sub get_claws_version() {
265 foreach my $bin (@cmbin) {
270 return "" unless ($_); # not installed
273 my @fver = split (/ /);
274 die "Invalid version string" unless ($fver[2] eq "version");
275 my @ver = split (/\./, $fver[3]);
278 if ($ver[2] =~ /(\d+)cvs(\d+)/) {
287 # data handlers and auxiliar functions
289 sub handle_bool_value {
290 my ($widget, $event, $dataref) = @_;
291 $$dataref = ($widget->get_active ())? '1': '0';
294 sub handle_int_value {
295 my ($widget, $event, $dataref) = @_;
296 $_ = $widget->get_text ();
301 $widget->set_text ($_);
304 $widget->set_text ($$dataref);
308 sub handle_string_value {
309 my ($widget, $event, $dataref) = @_;
310 $$dataref = $widget->get_text ();
313 sub gdk_color_from_str {
315 my ($rr, $gg, $bb) = (0, 0 ,0);
317 if (/\#([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])/) {
322 my $color = Gtk2::Gdk::Color->new ($rr, $gg, $bb);
326 sub str_from_gdk_color {
328 my $rr = $color->red / 256;
329 my $gg = $color->green / 256;
330 my $bb = $color->blue / 256;
331 my $str = sprintf ("#%.2x%.2x%.2x", $rr, $gg, $bb);
335 sub handle_color_value {
336 my ($widget, $event, $dataref) = @_;
337 my $newcol = $widget->get_color;
338 $$dataref = &str_from_gdk_color ($newcol);
341 sub handle_selection_value {
342 my ($widget, $event, $dataref) = @_;
343 $$dataref = $widget->get_active;
346 sub get_rc_filename {
347 return $CONFIGDIR . $CONFIGRC;
350 sub set_rc_filename {
352 my @parts = split ('/', $fullname);
353 $CONFIGRC = $parts[$#parts];
354 $parts[$#parts] = '';
355 $CONFIGDIR = join ('/', @parts);
359 my ($mesg, $fatal) = @_;
360 if (defined($fatal) && $fatal eq 'die') {
361 die "$NAME: $mesg\n";
364 print "$NAME: $mesg\n";
370 my $markup = "<span weight=\"bold\" size=\"large\">" . $emsg . "</span>";
371 my $errordlg = Gtk2::MessageDialog->new_with_markup ($main_window, 'modal', 'error', 'cancel', $markup);
372 $errordlg->set_title (_('Error message'));
377 sub check_claws_not_running() {
378 my $socket = (not $ALTCONFIGDIR)? "/tmp/": $CONFIGDIR;
379 $socket .= "claws-mail-$<";
381 my $emsg = "$xl::s{e_error}$xl::s{e_running}";
383 error_dialog ($emsg);
390 sub check_rc_file() {
392 (defined($rcfile) && -f $rcfile) or do {
393 my $emsg = "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
395 error_dialog ($emsg);
402 sub set_widget_hint() {
404 my ($wdgt, $hint) = @_;
405 $wdgt->set_tooltip_text ($hint);
406 $wdgt->set_has_tooltip (TRUE);
410 # graphic element creation
412 sub new_check_button_for {
413 my ($hash, $key) = @_;
414 my $name = $$hash{$key}[NAME];
415 my $label = $$hash{$key}[LABEL];
417 my $hbox = Gtk2::HBox->new (FALSE, 5);
418 my $cb = Gtk2::CheckButton->new ($label);
419 $$hash{$key}[GUI] = $cb;
420 if (defined ($HPVALUE{$name})) {
421 $cb->set_active ($HPVALUE{$name} eq '1');
423 $cb->signal_connect (clicked => sub {
425 &handle_bool_value($w, $e, \$HPVALUE{$name});
427 &set_widget_hint ($cb, $$hash{$key}[DESC]);
428 $hbox->pack_start ($cb, FALSE, FALSE, HBOX_SPC);
433 sub new_text_box_for_int {
434 my ($hash, $key) = @_;
435 my $name = $$hash{$key}[NAME];
436 my $label = $$hash{$key}[LABEL];
437 my @type = split (/,/, $$hash{$key}[TYPE]);
438 push (@type, 0), push (@type, 10000) unless ($#type > 0);
440 my $hbox = Gtk2::HBox->new (FALSE, 5);
441 my $glabel = Gtk2::Label->new ($label);
442 my $pagei = int (($type[2] - $type[1]) / 10);
443 my $adjust = Gtk2::Adjustment->new (
444 $HPVALUE{$name}, $type[1], $type[2], 1, $pagei, 10
446 my $gentry = Gtk2::SpinButton->new ($adjust, 1, 0);
447 $gentry->set_numeric (TRUE);
448 $$hash{$key}[GUI] = $gentry;
449 $gentry->signal_connect('value-changed' => sub {
451 &handle_int_value($w, $e, \$HPVALUE{$name});
453 &set_widget_hint ($gentry, $$hash{$key}[DESC]);
454 $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
455 $hbox->pack_start ($gentry, FALSE, FALSE, HBOX_SPC);
460 sub new_color_button_for {
461 my ($hash, $key) = @_;
462 my $name = $$hash{$key}[NAME];
463 my $label = $$hash{$key}[LABEL];
465 my $col = &gdk_color_from_str ($HPVALUE{$name});
466 my $hbox = Gtk2::HBox->new (FALSE, 5);
467 my $glabel = Gtk2::Label->new ($label);
468 my $button = Gtk2::ColorButton->new_with_color ($col);
469 $$hash{$key}[GUI] = $button;
470 $button->set_title ($label);
471 $button->set_relief ('none');
472 $button->signal_connect ('color-set' => sub {
474 &handle_color_value($w, $e, \$HPVALUE{$name});
476 &set_widget_hint ($button, $$hash{$key}[DESC]);
477 $hbox->pack_start ($button, FALSE, FALSE, HBOX_SPC);
478 $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
483 sub new_selection_box_for {
484 my ($hash, $key) = @_;
485 my $name = $$hash{$key}[NAME];
486 my $label = $$hash{$key}[LABEL];
488 my $hbox = Gtk2::HBox->new (FALSE, 5);
489 my $glabel = Gtk2::Label->new ($label);
490 my $combo = Gtk2::ComboBox->new_text;
491 $$hash{$key}[GUI] = $combo;
492 my @options = split (';', $$hash{$key}[TYPE]);
493 foreach my $opt (@options) {
494 my ($index, $textkey) = split ('=', $opt);
495 $combo->insert_text ($index, $xl::s{$textkey});
497 $combo->signal_connect ('changed' => sub {
499 &handle_selection_value($w, $e, \$HPVALUE{$name});
501 $combo->set_active ($HPVALUE{$name});
502 &set_widget_hint ($combo, $$hash{$key}[DESC]);
503 $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
504 $hbox->pack_start ($combo, FALSE, FALSE, HBOX_SPC);
509 # preference maps and corresponding page creation subs
511 %pr::oth = ( # other preferences
513 'addressbook_use_editaddress_dialog',
514 $xl::s{l_oth_use_dlg},
515 $xl::s{h_oth_use_dlg},
522 'cache_max_mem_usage',
523 $xl::s{l_oth_max_use},
524 $xl::s{h_oth_max_use},
525 'int,0,262144', # 0 Kb - 256 Mb
531 'cache_min_keep_time',
532 $xl::s{l_oth_min_time},
533 $xl::s{h_oth_min_time},
534 'int,0,120', # 0 minutes - 2 hours
540 'use_networkmanager',
541 $xl::s{l_oth_use_netm},
542 $xl::s{h_oth_use_netm},
550 sub new_other_page() {
551 my $of = Gtk2::VBox->new (FALSE, 5);
552 $of->set_border_width (PAGE_SPC);
554 my $ab_frame = Gtk2::Frame->new ($xl::s{ab_frame});
555 my $cb_use_dlg = &new_check_button_for(\%pr::oth, 'use_dlg');
556 my $vb1 = Gtk2::VBox->new (FALSE, 5);
557 $vb1->set_border_width (PAGE_SPC);
558 $vb1->pack_start ($cb_use_dlg, FALSE, FALSE, 0);
559 $ab_frame->add ($vb1);
561 my $mem_frame = Gtk2::Frame->new ($xl::s{mem_frame});
562 my $tb_max_use = &new_text_box_for_int(\%pr::oth, 'max_use');
563 my $tb_min_time = &new_text_box_for_int(\%pr::oth, 'min_time');
564 my $vb2 = Gtk2::VBox->new (FALSE, 5);
565 $vb2->set_border_width (PAGE_SPC);
566 $vb2->pack_start ($tb_max_use, FALSE, FALSE, 0);
567 $vb2->pack_start ($tb_min_time, FALSE, FALSE, 0);
568 $mem_frame->add ($vb2);
570 my $netm_frame = Gtk2::Frame->new ($xl::s{netm_frame});
571 my $cb_use_netm = &new_check_button_for(\%pr::oth, 'use_netm');
572 my $vb3 = Gtk2::VBox->new (FALSE, 5);
573 $vb3->set_border_width (PAGE_SPC);
574 $vb3->pack_start ($cb_use_netm, FALSE, FALSE, 0);
575 $netm_frame->add ($vb3);
577 $of->pack_start ($ab_frame, FALSE, FALSE, FRAME_SPC);
578 $of->pack_start ($mem_frame, FALSE, FALSE, FRAME_SPC);
579 $of->pack_start ($netm_frame, FALSE, FALSE, FRAME_SPC);
584 %pr::gui = ( # gui bells and whistles
587 $xl::s{l_gui_b_unread},
588 $xl::s{h_gui_b_unread},
596 $xl::s{l_gui_no_markup},
597 $xl::s{h_gui_no_markup},
604 'enable_dotted_lines',
605 $xl::s{l_gui_dot_lines},
606 $xl::s{h_gui_dot_lines},
614 $xl::s{l_gui_h_scroll},
615 $xl::s{h_gui_h_scroll},
623 $xl::s{l_gui_swp_from},
624 $xl::s{h_gui_swp_from},
631 'folderview_vscrollbar_policy',
632 $xl::s{l_gui_v_scroll},
633 $xl::s{h_gui_v_scroll},
634 '0=l_gui_v_scroll_show;1=l_gui_v_scroll_auto;2=l_gui_v_scroll_hide',
640 'stripes_color_offset',
641 $xl::s{l_gui_strip_off},
642 $xl::s{h_gui_strip_off},
643 'int,0,10000', # no idea what this number means
649 'textview_cursor_visible',
650 $xl::s{l_gui_cursor_v},
651 $xl::s{h_gui_cursor_v},
658 'toolbar_detachable',
659 $xl::s{l_gui_toolbar_d},
660 $xl::s{h_gui_toolbar_d},
667 'use_stripes_everywhere',
668 $xl::s{l_gui_strip_all},
669 $xl::s{h_gui_strip_all},
676 'use_stripes_in_summaries',
677 $xl::s{l_gui_strip_sum},
678 $xl::s{h_gui_strip_sum},
686 $xl::s{l_gui_two_line_v},
687 $xl::s{h_gui_two_line_v},
696 my $gf = Gtk2::VBox->new (FALSE, 5);
697 $gf->set_border_width (PAGE_SPC);
699 my $stripes_frame = Gtk2::Frame->new ($xl::s{stripes_frame});
700 my $cb_strip_all = &new_check_button_for (\%pr::gui, 'strip_all');
701 my $cb_strip_sum = &new_check_button_for (\%pr::gui, 'strip_sum');
702 my $tb_strip_off = &new_text_box_for_int (\%pr::gui, 'strip_off');
703 my $vb1 = Gtk2::VBox->new (FALSE, 5);
704 $vb1->set_border_width (PAGE_SPC);
705 $vb1->pack_start ($cb_strip_all, FALSE, FALSE, 0);
706 $vb1->pack_start ($cb_strip_sum, FALSE, FALSE, 0);
707 $vb1->pack_start ($tb_strip_off, FALSE, FALSE, 0);
708 $stripes_frame->add ($vb1);
710 my $mlist_frame = Gtk2::Frame->new ($xl::s{mlist_frame});
711 my $cb_b_unread = &new_check_button_for (\%pr::gui, 'b_unread');
712 my $cb_swp_from = &new_check_button_for (\%pr::gui, 'swp_from');
713 my $vb3 = Gtk2::VBox->new (FALSE, 5);
714 $vb3->set_border_width (PAGE_SPC);
715 $vb3->pack_start ($cb_b_unread, FALSE, FALSE, 0);
716 $vb3->pack_start ($cb_swp_from, FALSE, FALSE, 0);
717 $mlist_frame->add ($vb3);
719 my $sbar_frame = Gtk2::Frame->new ($xl::s{sbar_frame});
720 my $cb_h_scroll = &new_check_button_for (\%pr::gui, 'h_scroll');
721 my $sb_v_scroll = &new_selection_box_for (\%pr::gui, 'v_scroll');
722 my $vb2 = Gtk2::VBox->new (FALSE, 5);
723 $vb2->set_border_width (PAGE_SPC);
724 $vb2->pack_start ($cb_h_scroll, FALSE, FALSE, 0);
725 $vb2->pack_start ($sb_v_scroll, FALSE, FALSE, 0);
726 $sbar_frame->add ($vb2);
728 my $cb_no_markup = &new_check_button_for (\%pr::gui, 'no_markup');
729 my $cb_dot_lines = &new_check_button_for (\%pr::gui, 'dot_lines');
730 my $cb_cursor_v = &new_check_button_for (\%pr::gui, 'cursor_v');
731 my $cb_toolbar_d = &new_check_button_for (\%pr::gui, 'toolbar_d');
732 my $cb_two_linev = &new_check_button_for (\%pr::gui, 'two_linev');
734 $gf->pack_start ($stripes_frame, FALSE, FALSE, FRAME_SPC);
735 $gf->pack_start ($mlist_frame, FALSE, FALSE, FRAME_SPC);
736 $gf->pack_start ($cb_no_markup, FALSE, FALSE, 0);
737 $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
738 $gf->pack_start ($cb_cursor_v, FALSE, FALSE, 0);
739 $gf->pack_start ($cb_toolbar_d, FALSE, FALSE, 0);
740 $gf->pack_start ($cb_two_linev, FALSE, FALSE, 0);
741 $gf->pack_start ($sbar_frame, FALSE, FALSE, FRAME_SPC);
746 %pr::beh = ( # tweak some behaviour
749 $xl::s{l_beh_hover_t},
750 $xl::s{h_beh_hover_t},
751 'int,100,3000', # 0.1 seconds - 3 seconds
758 $xl::s{l_beh_dangerous},
759 $xl::s{h_beh_dangerous},
766 'respect_flowed_format',
767 $xl::s{l_beh_flowed},
768 $xl::s{h_beh_flowed},
775 'save_parts_readwrite',
776 $xl::s{l_beh_parts_rw},
777 $xl::s{h_beh_parts_rw},
784 'skip_ssl_cert_check',
785 $xl::s{l_beh_skip_ssl},
786 $xl::s{h_beh_skip_ssl},
793 'statusbar_update_step',
794 $xl::s{l_beh_up_step},
795 $xl::s{h_beh_up_step},
796 'int,1,200', # 1 item - 200 items
802 'thread_by_subject_max_age',
803 $xl::s{l_beh_thread_a},
804 $xl::s{h_beh_thread_a},
805 'int,1,30', # 1 day - 30 days
812 $xl::s{l_beh_unsafe_ssl},
813 $xl::s{h_beh_unsafe_ssl},
820 'utf8_instead_of_locale_for_broken_mail',
821 $xl::s{l_beh_use_utf8},
822 $xl::s{h_beh_use_utf8},
830 $xl::s{l_beh_warn_dnd},
831 $xl::s{h_beh_warn_dnd},
838 'outgoing_fallback_to_ascii',
839 $xl::s{l_beh_out_ascii},
840 $xl::s{h_beh_out_ascii},
848 sub new_behaviour_page() {
849 my $bf = Gtk2::VBox->new (FALSE, 5);
850 $bf->set_border_width (PAGE_SPC);
852 my $dnd_frame = Gtk2::Frame->new ($xl::s{dnd_frame});
853 my $tb_hoover_t = &new_text_box_for_int (\%pr::beh, 'hover_t');
854 my $cb_warn_dnd = &new_check_button_for (\%pr::beh, 'warn_dnd');
855 my $vb1 = Gtk2::VBox->new (FALSE, 5);
856 $vb1->set_border_width (PAGE_SPC);
857 $vb1->pack_start ($cb_warn_dnd, FALSE, FALSE, 0);
858 $vb1->pack_start ($tb_hoover_t, FALSE, FALSE, 0);
859 $dnd_frame->add ($vb1);
861 my $ssl_frame = Gtk2::Frame->new ($xl::s{ssl_frame});
862 my $cb_skip_ssl = &new_check_button_for (\%pr::beh, 'skip_ssl');
863 my $cb_unsafe_ssl = &new_check_button_for (\%pr::beh, 'unsafe_ssl');
864 my $hb1 = Gtk2::HBox->new (FALSE, 5);
865 $hb1->set_border_width (PAGE_SPC);
866 $hb1->pack_start ($cb_skip_ssl, FALSE, FALSE, 0);
867 $hb1->pack_start ($cb_unsafe_ssl, FALSE, FALSE, 0);
868 $ssl_frame->add ($hb1);
870 my $tb_up_step = &new_text_box_for_int (\%pr::beh, 'up_step');
871 my $tb_thread_a = &new_text_box_for_int (\%pr::beh, 'thread_a');
873 my $msgs_frame = Gtk2::Frame->new ($xl::s{msgs_frame});
874 my $cb_flowed = &new_check_button_for (\%pr::beh, 'flowed');
875 my $cb_parts_rw = &new_check_button_for (\%pr::beh, 'parts_rw');
876 my $cb_use_utf8 = &new_check_button_for (\%pr::beh, 'use_utf8');
877 my $cb_dangerous = &new_check_button_for (\%pr::beh, 'dangerous');
878 my $cb_out_ascii = &new_check_button_for (\%pr::beh, 'out_ascii');
879 my $vb2 = Gtk2::VBox->new (FALSE, 5);
880 $vb2->set_border_width (PAGE_SPC);
881 $vb2->pack_start ($cb_flowed, FALSE, FALSE, 0);
882 $vb2->pack_start ($cb_parts_rw, FALSE, FALSE, 0);
883 $vb2->pack_start ($cb_use_utf8, FALSE, FALSE, 0);
884 $vb2->pack_start ($cb_dangerous, FALSE, FALSE, 0);
885 $vb2->pack_start ($cb_out_ascii, FALSE, FALSE, 0);
886 $msgs_frame->add ($vb2);
888 $bf->pack_start ($dnd_frame, FALSE, FALSE, FRAME_SPC);
889 $bf->pack_start ($ssl_frame, FALSE, FALSE, FRAME_SPC);
890 $bf->pack_start ($tb_up_step, FALSE, FALSE, 0);
891 $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0);
892 $bf->pack_start ($msgs_frame, FALSE, FALSE, FRAME_SPC);
897 %pr::col = ( # a variety of colours
900 $xl::s{l_col_emphasis},
901 $xl::s{h_col_emphasis},
909 $xl::s{l_col_log_err},
910 $xl::s{h_col_log_err},
918 $xl::s{l_col_log_in},
919 $xl::s{h_col_log_in},
927 $xl::s{l_col_log_msg},
928 $xl::s{h_col_log_msg},
936 $xl::s{l_col_log_out},
937 $xl::s{h_col_log_out},
945 $xl::s{l_col_log_warn},
946 $xl::s{h_col_log_warn},
954 sub new_colours_page() {
955 my $cf = Gtk2::VBox->new (FALSE, 5);
956 $cf->set_border_width (PAGE_SPC);
958 my $msgview_frame = Gtk2::Frame->new ($xl::s{msgview_frame});
959 my $cb_emphasis = &new_color_button_for (\%pr::col, 'emphasis');
960 my $vb1 = Gtk2::VBox->new (FALSE, 5);
961 $vb1->set_border_width (PAGE_SPC);
962 $vb1->pack_start ($cb_emphasis, FALSE, FALSE, 0);
963 $msgview_frame->add ($vb1);
965 my $log_frame = Gtk2::Frame->new ($xl::s{log_frame});
966 my $cb_log_err = &new_color_button_for (\%pr::col, 'log_err');
967 my $cb_log_in = &new_color_button_for (\%pr::col, 'log_in');
968 my $cb_log_msg = &new_color_button_for (\%pr::col, 'log_msg');
969 my $cb_log_out = &new_color_button_for (\%pr::col, 'log_out');
970 my $cb_log_warn = &new_color_button_for (\%pr::col, 'log_warn');
971 my $vb2 = Gtk2::VBox->new (FALSE, 5);
972 $vb2->set_border_width (PAGE_SPC);
973 $vb2->pack_start ($cb_log_err, FALSE, FALSE, 0);
974 $vb2->pack_start ($cb_log_in, FALSE, FALSE, 0);
975 $vb2->pack_start ($cb_log_msg, FALSE, FALSE, 0);
976 $vb2->pack_start ($cb_log_out, FALSE, FALSE, 0);
977 $vb2->pack_start ($cb_log_warn, FALSE, FALSE, 0);
978 $log_frame->add ($vb2);
980 $cf->pack_start ($msgview_frame, FALSE, FALSE, 0);
981 $cf->pack_start ($log_frame, FALSE, FALSE, 0);
987 sub print_version() {
988 print $xl::s{about_title} . "\n";
989 print $xl::s{about_version} . " $VERSION\n";
990 print "Perl-GLib " . $Glib::VERSION;
991 # version info stuff appeared in 1.040
992 if ($Glib::VERSION >= 1.040) {
993 print _(", built for ") . join(".", Glib->GET_VERSION_INFO)
994 . _(", running with ") . join(".", &Glib::major_version,
995 &Glib::minor_version, &Glib::micro_version);
998 print "Perl-GTK2 " . $Gtk2::VERSION;
999 if ($Gtk2::VERSION >= 1.040) {
1000 print _(", built for ") . join(".", Gtk2->GET_VERSION_INFO)
1001 . _(", running with ") . join(".", &Gtk2::major_version,
1002 &Gtk2::minor_version, &Gtk2::micro_version);
1007 # the command line help
1009 my $line = '-' x length ($xl::s{about_title}) . "\n";
1011 print $xl::s{about_title} . "\n";
1013 print _("Syntax:\n");
1014 print _(" clawsker [options]\n");
1015 print _("Options:\n");
1016 print _(" --help Prints this help screen.\n");
1017 print _(" --version Prints version infos.\n");
1018 print _(" --verbose More messages on standard output.\n");
1019 print _(" --alternate-config-dir <dir> Uses <dir> as Claws Mail config dir.\n");
1020 print _(" --clawsrc <file> Uses <file> as full resource name.\n");
1023 # handle errors which don't allow to run
1024 sub command_line_fatal() {
1026 my $emsg = $xl::s{e_error} . $reason;
1027 error_dialog ($emsg);
1028 log_message ("$emsg", 'die');
1031 # parse the command line
1032 sub parse_command_line() {
1034 while (defined($ARGV[$arg])) {
1048 /--alternate-config-dir/ && do {
1050 &command_line_fatal ($xl::s{e_requireddir})
1051 unless defined($ARGV[$arg]);
1052 &command_line_fatal ($xl::s{e_notadir})
1053 unless -d $ARGV[$arg];
1054 $CONFIGDIR = $ARGV[$arg];
1056 unless ($CONFIGDIR =~ /.*\/$/);
1057 $ALTCONFIGDIR = TRUE;
1062 &command_line_fatal($xl::s{e_requiredfile})
1063 unless defined($ARGV[$arg]);
1064 &command_line_fatal($xl::s{e_notafile})
1065 unless -f $ARGV[$arg];
1066 &set_rc_filename ($ARGV[$arg]);
1069 /.*/ && &command_line_fatal (
1070 _("unknown option '{opt}'.\n", opt => $ARGV[$arg]));
1074 # eveything continues...
1078 # update the hidden preferences status from loaded values
1079 sub init_hidden_preferences() {
1080 foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth) {
1081 foreach my $key (keys %$hash) {
1082 $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]};
1088 # load current status from disc
1089 sub load_preferences() {
1090 my $rc = &get_rc_filename;
1091 &log_message ("Loading preferences from $rc\n");
1092 return FALSE unless &check_rc_file ($rc);
1093 return FALSE unless &check_claws_not_running;
1097 if (/^([8a-z_]+)=(.*)$/) {
1105 # save current preferences to disc
1106 sub save_preferences() {
1107 my $rc = &get_rc_filename;
1108 &log_message ("Saving preferences to $rc\n");
1109 return FALSE unless &check_rc_file ($rc);
1110 return FALSE unless &check_claws_not_running;
1111 my $rcbak = "$rc.backup";
1112 rename ($rc, $rcbak);
1114 open (RCB, "<$rcbak");
1117 if (/^([8a-z_]+)=(.*)$/) {
1118 if (defined($HPVALUE{$1})) {
1119 print RCF $1 . "=" . $HPVALUE{$1} . "\n";
1122 print RCF $_ . "\n";
1126 print RCF $_ . "\n";
1135 sub new_notebook() {
1136 my $nb = Gtk2::Notebook->new;
1138 $nb->append_page (&new_behaviour_page, $xl::s{tab_behaviour});
1139 $nb->append_page (&new_colours_page, $xl::s{tab_colours});
1140 $nb->append_page (&new_gui_page, $xl::s{tab_gui});
1141 $nb->append_page (&new_other_page, $xl::s{tab_other});
1146 # create an about dialog
1147 sub new_about_dialog() {
1149 my $title = $xl::s{about_title};
1150 my $lic = $xl::s{about_license};
1151 my $vers = $xl::s{about_version} . " $VERSION";
1153 "This program is free software: you can redistribute it and/or modify
1154 it under the terms of the GNU General Public License as published by
1155 the Free Software Foundation, either version 3 of the License, or
1156 (at your option) any later version.
1158 This program is distributed in the hope that it will be useful,
1159 but WITHOUT ANY WARRANTY; without even the implied warranty of
1160 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1161 GNU General Public License for more details.
1163 You should have received a copy of the GNU General Public License
1164 along with this program. If not, see <http://www.gnu.org/licenses/>.";
1166 my $holder = "Ricardo Mones <ricardo\@mones.org>";
1168 my $dialog = Gtk2::MessageDialog->new_with_markup ($parent,
1169 [qw/modal destroy-with-parent/],
1171 "<span size=\"x-large\" weight=\"bold\">$title</span>\n"
1172 . "<span size=\"large\">$vers</span>\n"
1173 . "<span>Copyright $year by $holder</span>\n\n"
1174 . "<span size=\"large\">$lic</span>\n\n"
1175 . "<span size=\"small\">$license</span>");
1176 $dialog->set_title ($xl::s{about});
1181 # create buttons box
1182 sub new_button_box() {
1183 my ($parent, $adlg) = @_;
1184 my $b_about = Gtk2::Button->new_from_stock ('gtk-about');
1185 my $b_exit = Gtk2::Button->new_from_stock ('gtk-quit');
1186 my $b_apply = Gtk2::Button->new_from_stock ('gtk-apply');
1187 # disable button until is really implemented
1188 # my $b_undo = Gtk2::Button->new_from_stock ('gtk-undo');
1189 my $hbox = Gtk2::HBox->new (FALSE, 5);
1191 $b_exit->signal_connect (clicked => sub { Gtk2->main_quit });
1192 $b_apply->signal_connect (clicked => sub { &save_preferences ($parent) });
1193 # $b_undo->signal_connect (clicked => sub { &undo_current_changes });
1194 $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
1196 $hbox->pack_end ($b_apply, FALSE, FALSE, 0);
1197 $hbox->pack_end ($b_exit, FALSE, FALSE, 0);
1198 # $hbox->pack_end ($b_undo, FALSE, FALSE, 0);
1199 $hbox->pack_start ($b_about, FALSE, FALSE, 0);
1205 $main_window = Gtk2::Window->new ('toplevel');
1206 exit unless &parse_command_line;
1207 exit unless &load_preferences;
1208 exit unless &init_hidden_preferences;
1210 my $box = Gtk2::VBox->new (FALSE, 5);
1211 $box->set_border_width(3);
1212 my $about = &new_about_dialog;
1213 $box->pack_start (&new_notebook, FALSE, FALSE, 0);
1214 $box->pack_end (&new_button_box ($main_window, $about), FALSE, FALSE, 0);
1215 $main_window->signal_connect (delete_event => sub { Gtk2->main_quit });
1216 $main_window->set_title ($xl::s{win_title});
1217 $main_window->add ($box);
1218 $main_window->show_all;