#!/usr/bin/perl -w # # $Id$ # =pod =head1 NAME Clawsker - A Claws Mail Tweaker =head1 SYNOPSIS clawsker [options] =head1 DESCRIPTION Clawsker is an applet to edit the so called Claws Mail hidden preferences. Claws Mail is a fast, lightweight and feature-rich MUA with a high number of configurable options. To keep the binary small and fast some of these preferences which not widely used are not provided with a graphical interface for inspection and/or modification. Users wanting to edit such preferences had to face raw edition of their configuration files, now you can do it with a convenient GTK2 interface using Clawsker. =head1 OPTIONS --help Shows a brief help screen. --alternate-config-dir Uses as Claws Mail configuration dir. --clawsrc Uses as Claws Mail resource configuration file. This sets the full file name overriding any previous setting. Multiple options are allowed, although only the last one has effect. Weird option specifications may produce weird results (but otherwise correct). =head1 LIMITATIONS A running Claws Mail cannot be detected if using the --clawsrc option because the directory is not assumed to be a Claws Mail configuration dir. If that is the case the --alternate-config-dir option instead. =head1 AUTHOR Ricardo Mones Ericardo@mones.orgE =head1 LICENSE Copyright (c) 2007 by Ricardo Mones This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see Ehttp://www.gnu.org/licenses/E. =cut use strict; use POSIX qw(setlocale); use Locale::gettext; use Glib qw(TRUE FALSE); use Gtk2 -init; my $NAME = 'clawsker'; my $PREFIX = '@PREFIX@'; my $LIBDIR = '@LIBDIR@'; my $VERSION = '@VERSION@'; my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG}); setlocale (LC_ALL, $locale); bindtextdomain ($NAME, sprintf ('%s/share/locale', $PREFIX)); textdomain ($NAME); sub _ { my $str = shift; my %par = @_; my $xla = gettext ($str); if (scalar(keys(%par)) > 0) { foreach my $key (keys %par) { $xla =~ s/\{$key\}/$par{$key}/g; } } return $xla; } # default messages %xl::s = ( win_title => _('Claws Mail Hidden Preferences'), about => _('About...'), about_title => _('Clawsker :: A Claws Mail Tweaker'), about_license => _('License:'), about_version => _('Version:'), tab_colours => _('Colours'), tab_behaviour => _('Behaviour'), tab_gui => _('GUI'), tab_other => _('Other'), ab_frame => _('Addressbook'), mem_frame => _('Memory'), msgview_frame => _('Message view'), log_frame => _('Log window'), dnd_frame => _('Drag and drop'), ssl_frame => _('Secure Sockets Layer'), msgs_frame => _('Messages'), stripes_frame => _('Coloured stripes'), sbar_frame => _('Scroll bars'), mlist_frame => _('Message list'), l_oth_use_dlg => _('Use address book dialog'), h_oth_use_dlg => _('If true use a separate dialogue to edit a person\'s details. Otherwise will use a form embedded in the addressbook\'s main window.'), l_oth_max_use => _('Maximum memory for message cache (Kb)'), h_oth_max_use => _('The maximum amount of memory to use to cache messages, in kB.'), l_oth_min_time => _('Minimun time for cache elements (minutes)'), 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.'), l_gui_b_unread => _('Show unread messages with bold font'), h_gui_b_unread => _('Show unread messages in the Message List using a bold font.'), l_gui_no_markup => _('Don\'t use markup in compose window'), h_gui_no_markup => _('Don\'t use bold and italic text in Compose dialogue\'s account selector.'), l_gui_dot_lines => _('Use dotted lines in tree views'), h_gui_dot_lines => _('Use the old dotted line look in the main window tree views (folder and message lists) instead of the modern lineless look.'), l_gui_h_scroll => _('Enable horizontal scrollbar'), h_gui_h_scroll => _('Enable the horizontal scrollbar in the Message List.'), l_gui_swp_from => _('Swap From field in Sent folder'), h_gui_swp_from => _('Display the sender\'s email address in the To column of the Sent folder instead of the recipient\'s.'), l_gui_v_scroll => _('Folder list scrollbar behaviour'), h_gui_v_scroll => _('Specify the policy of vertical scrollbar of Folder List: show always, automatic or hide always.'), l_gui_v_scroll_show => _('Show always'), l_gui_v_scroll_auto => _('Automatic'), l_gui_v_scroll_hide => _('Hide always'), l_gui_strip_off => _('Striped lines contrast'), 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.'), l_gui_cursor_v => _('Cursor visible in message view'), h_gui_cursor_v => _('Display the cursor in the message view.'), l_gui_toolbar_d => _('Detachable toolbars'), h_gui_toolbar_d => _('Show handles in the toolbars.'), l_gui_strip_all => _('Use stripes in tree view components'), h_gui_strip_all => _('Enable alternately coloured lines in all tree view components.'), l_gui_strip_sum => _('Use stripes in summary view'), h_gui_strip_sum => _('Enable alternately coloured lines in summary view and folder list'), l_beh_hover_t => _('Drag and drop hover timeout (ms)'), 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.'), l_beh_dangerous => _('Don\'t confirm deletions (dangerous!)'), h_beh_dangerous => _('Don\'t ask for confirmation before definitive deletion of emails.'), l_beh_flowed => _('Respect flowed format in messages'), 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.'), l_beh_parts_rw => _('Allow writable temporary files'), h_beh_parts_rw => _('Saves temporary files when opening attachment with write bit set.'), l_beh_skip_ssl => _('Don\'t check SSL certificates'), h_beh_skip_ssl => _('Disables the verification of SSL certificates.'), l_beh_up_step => _('Progress update step (items)'), h_beh_up_step => _('Update stepping in progress bars.'), l_beh_thread_a => _('Maximum age in threading by subject (days)'), h_beh_thread_a => _('Number of days to include a message in a thread when using "Thread using subject in addition to standard headers".'), l_beh_unsafe_ssl => _('Allow unsafe SSL certificates'), h_beh_unsafe_ssl => _('Allows Claws Mail to remember multiple SSL certificates for a given server/port.'), l_beh_use_utf8 => _('Use UTF-8 in broken mails'), h_beh_use_utf8 => _('Use UTF-8 encoding for broken mails instead of current locale.'), l_beh_warn_dnd => _('Warning on drag and drop'), h_beh_warn_dnd => _('Display a confirmation dialogue on drag \'n\' drop of folders.'), l_col_emphasis => _('Emphasised X-Mailer colour'), h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'), l_col_log_err => _('Errors colour'), h_col_log_err => _('Colour for error messages in log window.'), l_col_log_in => _('Input colour'), h_col_log_in => _('Colour for input messages in log window.'), l_col_log_msg => _('Messages colour'), h_col_log_msg => _('Colour for messages in log window.'), l_col_log_out => _('Output colour'), h_col_log_out => _('Colour for output messages in log window.'), l_col_log_warn => _('Warnings colour'), h_col_log_warn => _('Colour for warning messages in log window.'), e_error => _('Error: '), e_noclawsrc => _('resource file for Claws Mail was not found.'), e_running => _('seems Claws Mail is currently running, close it first.'), e_requireddir => _('option requires a directory name.'), e_requiredfile => _('option requires a file name.'), e_notadir => _('specified name is not a directory or does not exist.'), e_notafile => _('specified name is not a file or does not exist.'), ); # all preferences read by load_preferences my %PREFS = (); # values of all preferences handled by clawsker my %HPVALUE = (); # default config dir and file name my $ALTCONFIGDIR = FALSE; my $CONFIGDIR = $ENV{HOME} . '/.claws-mail/'; my $CONFIGRC = 'clawsrc'; # index constants for preference arrays use constant NAME => 0; # the name on the rc file use constant LABEL => 1; # the label on the GUI use constant DESC => 2; # the description for the hint/help use constant TYPE => 3; # data type: bool, int, float, string, color use constant GUI => 4; # GUI element # constants for GUI spacing use constant HBOX_SPC => 5; use constant FRAME_SPC => 2; use constant PAGE_SPC => 5; # data handlers and auxiliar functions sub handle_bool_value { my ($widget, $event, $dataref) = @_; $$dataref = ($widget->get_active ())? '1': '0'; } sub handle_int_value { my ($widget, $event, $dataref) = @_; $_ = $widget->get_text (); s/^\s+//; s/\s+$//; if (/^[0-9]+$/) { $$dataref = $_; $widget->set_text ($_); } else { $widget->set_text ($$dataref); } } sub handle_string_value { my ($widget, $event, $dataref) = @_; $$dataref = $widget->get_text (); } sub gdk_color_from_str { my ($str) = @_; my ($rr, $gg, $bb) = (0, 0 ,0); $_ = uc ($str); if (/\#([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])([A-F0-9][A-F0-9])/) { $rr = hex($1) * 256; $gg = hex($2) * 256; $bb = hex($3) * 256; } my $color = Gtk2::Gdk::Color->new ($rr, $gg, $bb); return $color; } sub str_from_gdk_color { my ($color) = @_; my $rr = $color->red / 256; my $gg = $color->green / 256; my $bb = $color->blue / 256; my $str = sprintf ("#%.2x%.2x%.2x", $rr, $gg, $bb); return $str; } sub handle_color_value { my ($widget, $event, $dataref) = @_; my $newcol = $widget->get_color; $$dataref = &str_from_gdk_color ($newcol); } sub handle_selection_value { my ($widget, $event, $dataref) = @_; $$dataref = $widget->get_active; } sub get_rc_filename { return $CONFIGDIR . $CONFIGRC; } sub set_rc_filename { my ($fullname) = @_; my @parts = split ('/', $fullname); $CONFIGRC = $parts[$#parts]; $parts[$#parts] = ''; $CONFIGDIR = join ('/', @parts); } sub check_claws_not_running() { my $socket = ($ALTCONFIGDIR)? "/tmp/": $CONFIGDIR; $socket .= "claws-mail-$<"; -S $socket and die "$xl::s{e_error}$xl::s{e_running}\n"; } # graphic element creation sub new_check_button_for { my ($hash, $key) = @_; my $name = $$hash{$key}[NAME]; my $label = $$hash{$key}[LABEL]; # my $hbox = Gtk2::HBox->new (FALSE, 5); my $cb = Gtk2::CheckButton->new ($label); $$hash{$key}[GUI] = $cb; $cb->set_active ($HPVALUE{$name} eq '1'); $cb->signal_connect (clicked => sub { my ($w, $e) = @_; &handle_bool_value($w, $e, \$HPVALUE{$name}); }); $hbox->pack_start ($cb, FALSE, FALSE, HBOX_SPC); # return $hbox; } sub new_text_box_for { my ($hash, $key) = @_; my $name = $$hash{$key}[NAME]; my $label = $$hash{$key}[LABEL]; # my $hbox = Gtk2::HBox->new (FALSE, 5); my $glabel = Gtk2::Label->new ($label); my $gentry = Gtk2::Entry->new; $gentry->set_width_chars (8); $$hash{$key}[GUI] = $gentry; $gentry->set_text($HPVALUE{$name}); $gentry->signal_connect(changed => sub { my ($w, $e) = @_; &handle_int_value($w, $e, \$HPVALUE{$name}); # FIXME int only }); $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC); $hbox->pack_start ($gentry, FALSE, FALSE, HBOX_SPC); # return $hbox; } sub new_color_button_for { my ($hash, $key) = @_; my $name = $$hash{$key}[NAME]; my $label = $$hash{$key}[LABEL]; # my $col = &gdk_color_from_str ($HPVALUE{$name}); my $hbox = Gtk2::HBox->new (FALSE, 5); my $glabel = Gtk2::Label->new ($label); my $button = Gtk2::ColorButton->new_with_color ($col); $$hash{$key}[GUI] = $button; $button->set_title ($label); $button->set_relief ('none'); $button->signal_connect ('color-set' => sub { my ($w, $e) = @_; &handle_color_value($w, $e, \$HPVALUE{$name}); }); $hbox->pack_start ($button, FALSE, FALSE, HBOX_SPC); $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC); # return $hbox; } sub new_selection_box_for { my ($hash, $key) = @_; my $name = $$hash{$key}[NAME]; my $label = $$hash{$key}[LABEL]; # my $hbox = Gtk2::HBox->new (FALSE, 5); my $glabel = Gtk2::Label->new ($label); my $combo = Gtk2::ComboBox->new_text; $$hash{$key}[GUI] = $combo; my @options = split (';', $$hash{$key}[TYPE]); foreach my $opt (@options) { my ($index, $textkey) = split ('=', $opt); $combo->insert_text ($index, $xl::s{$textkey}); } $combo->signal_connect ('changed' => sub { my ($w, $e) = @_; &handle_selection_value($w, $e, \$HPVALUE{$name}); }); $combo->set_active ($HPVALUE{$name}); $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC); $hbox->pack_start ($combo, FALSE, FALSE, HBOX_SPC); # return $hbox; } # preference maps and corresponding page creation subs %pr::oth = ( # other preferences use_dlg => [ 'addressbook_use_editaddress_dialog', $xl::s{l_oth_use_dlg}, $xl::s{h_oth_use_dlg}, 'bool', undef, ], max_use => [ 'cache_max_mem_usage', $xl::s{l_oth_max_use}, $xl::s{h_oth_max_use}, 'int', undef, ], min_time => [ 'cache_min_keep_time', $xl::s{l_oth_min_time}, $xl::s{h_oth_min_time}, 'int', undef, ], ); sub new_other_page() { my $of = Gtk2::VBox->new (FALSE, 5); $of->set_border_width (PAGE_SPC); my $ab_frame = Gtk2::Frame->new ($xl::s{ab_frame}); my $cb_use_dlg = &new_check_button_for(\%pr::oth, 'use_dlg'); my $vb1 = Gtk2::VBox->new (FALSE, 5); $vb1->set_border_width (PAGE_SPC); $vb1->pack_start ($cb_use_dlg, FALSE, FALSE, 0); $ab_frame->add ($vb1); my $mem_frame = Gtk2::Frame->new ($xl::s{mem_frame}); my $tb_max_use = &new_text_box_for(\%pr::oth, 'max_use'); my $tb_min_time = &new_text_box_for(\%pr::oth, 'min_time'); my $vb2 = Gtk2::VBox->new (FALSE, 5); $vb2->set_border_width (PAGE_SPC); $vb2->pack_start ($tb_max_use, FALSE, FALSE, 0); $vb2->pack_start ($tb_min_time, FALSE, FALSE, 0); $mem_frame->add ($vb2); $of->pack_start ($ab_frame, FALSE, FALSE, FRAME_SPC); $of->pack_start ($mem_frame, FALSE, FALSE, FRAME_SPC); return $of; } %pr::gui = ( # gui bells and whistles b_unread => [ 'bold_unread', $xl::s{l_gui_b_unread}, $xl::s{h_gui_b_unread}, 'bool', undef, ], no_markup => [ 'compose_no_markup', $xl::s{l_gui_no_markup}, $xl::s{h_gui_no_markup}, 'bool', undef, ], dot_lines => [ 'enable_dotted_lines', $xl::s{l_gui_dot_lines}, $xl::s{h_gui_dot_lines}, 'bool', undef, ], h_scroll => [ 'enable_hscrollbar', $xl::s{l_gui_h_scroll}, $xl::s{h_gui_h_scroll}, 'bool', undef, ], swp_from => [ 'enable_swap_from', $xl::s{l_gui_swp_from}, $xl::s{h_gui_swp_from}, 'bool', undef, ], v_scroll => [ 'folderview_vscrollbar_policy', $xl::s{l_gui_v_scroll}, $xl::s{h_gui_v_scroll}, '0=l_gui_v_scroll_show;1=l_gui_v_scroll_auto;2=l_gui_v_scroll_hide', undef, ], strip_off => [ 'stripes_color_offset', $xl::s{l_gui_strip_off}, $xl::s{h_gui_strip_off}, 'int', undef, ], cursor_v => [ 'textview_cursor_visible', $xl::s{l_gui_cursor_v}, $xl::s{h_gui_cursor_v}, 'bool', undef, ], toolbar_d => [ 'toolbar_detachable', $xl::s{l_gui_toolbar_d}, $xl::s{h_gui_toolbar_d}, 'bool', undef, ], strip_all => [ 'use_stripes_everywhere', $xl::s{l_gui_strip_all}, $xl::s{h_gui_strip_all}, 'bool', undef, ], strip_sum => [ 'use_stripes_in_summaries', $xl::s{l_gui_strip_sum}, $xl::s{h_gui_strip_sum}, 'bool', undef, ], ); sub new_gui_page() { my $gf = Gtk2::VBox->new (FALSE, 5); $gf->set_border_width (PAGE_SPC); my $stripes_frame = Gtk2::Frame->new ($xl::s{stripes_frame}); my $cb_strip_all = &new_check_button_for (\%pr::gui, 'strip_all'); my $cb_strip_sum = &new_check_button_for (\%pr::gui, 'strip_sum'); my $tb_strip_off = &new_text_box_for (\%pr::gui, 'strip_off'); my $vb1 = Gtk2::VBox->new (FALSE, 5); $vb1->set_border_width (PAGE_SPC); $vb1->pack_start ($cb_strip_all, FALSE, FALSE, 0); $vb1->pack_start ($cb_strip_sum, FALSE, FALSE, 0); $vb1->pack_start ($tb_strip_off, FALSE, FALSE, 0); $stripes_frame->add ($vb1); my $mlist_frame = Gtk2::Frame->new ($xl::s{mlist_frame}); my $cb_b_unread = &new_check_button_for (\%pr::gui, 'b_unread'); my $cb_swp_from = &new_check_button_for (\%pr::gui, 'swp_from'); my $vb3 = Gtk2::VBox->new (FALSE, 5); $vb3->set_border_width (PAGE_SPC); $vb3->pack_start ($cb_b_unread, FALSE, FALSE, 0); $vb3->pack_start ($cb_swp_from, FALSE, FALSE, 0); $mlist_frame->add ($vb3); my $sbar_frame = Gtk2::Frame->new ($xl::s{sbar_frame}); my $cb_h_scroll = &new_check_button_for (\%pr::gui, 'h_scroll'); my $sb_v_scroll = &new_selection_box_for (\%pr::gui, 'v_scroll'); my $vb2 = Gtk2::VBox->new (FALSE, 5); $vb2->set_border_width (PAGE_SPC); $vb2->pack_start ($cb_h_scroll, FALSE, FALSE, 0); $vb2->pack_start ($sb_v_scroll, FALSE, FALSE, 0); $sbar_frame->add ($vb2); my $cb_no_markup = &new_check_button_for (\%pr::gui, 'no_markup'); my $cb_dot_lines = &new_check_button_for (\%pr::gui, 'dot_lines'); my $cb_cursor_v = &new_check_button_for (\%pr::gui, 'cursor_v'); my $cb_toolbar_d = &new_check_button_for (\%pr::gui, 'toolbar_d'); $gf->pack_start ($stripes_frame, FALSE, FALSE, FRAME_SPC); $gf->pack_start ($mlist_frame, FALSE, FALSE, FRAME_SPC); $gf->pack_start ($cb_no_markup, FALSE, FALSE, 0); $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0); $gf->pack_start ($cb_cursor_v, FALSE, FALSE, 0); $gf->pack_start ($cb_toolbar_d, FALSE, FALSE, 0); $gf->pack_start ($sbar_frame, FALSE, FALSE, FRAME_SPC); return $gf; } %pr::beh = ( # tweak some behaviour hover_t => [ 'hover_timeout', $xl::s{l_beh_hover_t}, $xl::s{h_beh_hover_t}, 'int', undef, ], dangerous => [ 'live_dangerously', $xl::s{l_beh_dangerous}, $xl::s{h_beh_dangerous}, 'bool', undef, ], flowed => [ 'respect_flowed_format', $xl::s{l_beh_flowed}, $xl::s{h_beh_flowed}, 'bool', undef, ], parts_rw => [ 'save_parts_readwrite', $xl::s{l_beh_parts_rw}, $xl::s{h_beh_parts_rw}, 'bool', undef, ], skip_ssl => [ 'skip_ssl_cert_check', $xl::s{l_beh_skip_ssl}, $xl::s{h_beh_skip_ssl}, 'bool', undef, ], up_step => [ 'statusbar_update_step', $xl::s{l_beh_up_step}, $xl::s{h_beh_up_step}, 'int', undef, ], thread_a => [ 'thread_by_subject_max_age', $xl::s{l_beh_thread_a}, $xl::s{h_beh_thread_a}, 'int', undef, ], unsafe_ssl => [ 'unsafe_ssl_certs', $xl::s{l_beh_unsafe_ssl}, $xl::s{h_beh_unsafe_ssl}, 'bool', undef, ], use_utf8 => [ 'utf8_instead_of_locale_for_broken_mail', $xl::s{l_beh_use_utf8}, $xl::s{h_beh_use_utf8}, 'bool', undef, ], warn_dnd => [ 'warn_dnd', $xl::s{l_beh_warn_dnd}, $xl::s{h_beh_warn_dnd}, 'bool', undef, ], ); sub new_behaviour_page() { my $bf = Gtk2::VBox->new (FALSE, 5); $bf->set_border_width (PAGE_SPC); my $dnd_frame = Gtk2::Frame->new ($xl::s{dnd_frame}); my $tb_hoover_t = &new_text_box_for (\%pr::beh, 'hover_t'); my $cb_warn_dnd = &new_check_button_for (\%pr::beh, 'warn_dnd'); my $vb1 = Gtk2::VBox->new (FALSE, 5); $vb1->set_border_width (PAGE_SPC); $vb1->pack_start ($cb_warn_dnd, FALSE, FALSE, 0); $vb1->pack_start ($tb_hoover_t, FALSE, FALSE, 0); $dnd_frame->add ($vb1); my $ssl_frame = Gtk2::Frame->new ($xl::s{ssl_frame}); my $cb_skip_ssl = &new_check_button_for (\%pr::beh, 'skip_ssl'); my $cb_unsafe_ssl = &new_check_button_for (\%pr::beh, 'unsafe_ssl'); my $hb1 = Gtk2::HBox->new (FALSE, 5); $hb1->set_border_width (PAGE_SPC); $hb1->pack_start ($cb_skip_ssl, FALSE, FALSE, 0); $hb1->pack_start ($cb_unsafe_ssl, FALSE, FALSE, 0); $ssl_frame->add ($hb1); my $tb_up_step = &new_text_box_for (\%pr::beh, 'up_step'); my $tb_thread_a = &new_text_box_for (\%pr::beh, 'thread_a'); my $msgs_frame = Gtk2::Frame->new ($xl::s{msgs_frame}); my $cb_flowed = &new_check_button_for (\%pr::beh, 'flowed'); my $cb_parts_rw = &new_check_button_for (\%pr::beh, 'parts_rw'); my $cb_use_utf8 = &new_check_button_for (\%pr::beh, 'use_utf8'); my $cb_dangerous = &new_check_button_for (\%pr::beh, 'dangerous'); my $vb2 = Gtk2::VBox->new (FALSE, 5); $vb2->set_border_width (PAGE_SPC); $vb2->pack_start ($cb_flowed, FALSE, FALSE, 0); $vb2->pack_start ($cb_parts_rw, FALSE, FALSE, 0); $vb2->pack_start ($cb_use_utf8, FALSE, FALSE, 0); $vb2->pack_start ($cb_dangerous, FALSE, FALSE, 0); $msgs_frame->add ($vb2); $bf->pack_start ($dnd_frame, FALSE, FALSE, FRAME_SPC); $bf->pack_start ($ssl_frame, FALSE, FALSE, FRAME_SPC); $bf->pack_start ($tb_up_step, FALSE, FALSE, 0); $bf->pack_start ($tb_thread_a, FALSE, FALSE, 0); $bf->pack_start ($msgs_frame, FALSE, FALSE, FRAME_SPC); return $bf; } %pr::col = ( # a variety of colours emphasis => [ 'emphasis_color', $xl::s{l_col_emphasis}, $xl::s{h_col_emphasis}, 'color', undef, ], log_err => [ 'log_error_color', $xl::s{l_col_log_err}, $xl::s{h_col_log_err}, 'color', undef, ], log_in => [ 'log_in_color', $xl::s{l_col_log_in}, $xl::s{h_col_log_in}, 'color', undef, ], log_msg => [ 'log_msg_color', $xl::s{l_col_log_msg}, $xl::s{h_col_log_msg}, 'color', undef, ], log_out => [ 'log_out_color', $xl::s{l_col_log_out}, $xl::s{h_col_log_out}, 'color', undef, ], log_warn => [ 'log_warn_color', $xl::s{l_col_log_warn}, $xl::s{h_col_log_warn}, 'color', undef, ], ); sub new_colours_page() { my $cf = Gtk2::VBox->new (FALSE, 5); $cf->set_border_width (PAGE_SPC); my $msgview_frame = Gtk2::Frame->new ($xl::s{msgview_frame}); my $cb_emphasis = &new_color_button_for (\%pr::col, 'emphasis'); my $vb1 = Gtk2::VBox->new (FALSE, 5); $vb1->set_border_width (PAGE_SPC); $vb1->pack_start ($cb_emphasis, FALSE, FALSE, 0); $msgview_frame->add ($vb1); my $log_frame = Gtk2::Frame->new ($xl::s{log_frame}); my $cb_log_err = &new_color_button_for (\%pr::col, 'log_err'); my $cb_log_in = &new_color_button_for (\%pr::col, 'log_in'); my $cb_log_msg = &new_color_button_for (\%pr::col, 'log_msg'); my $cb_log_out = &new_color_button_for (\%pr::col, 'log_out'); my $cb_log_warn = &new_color_button_for (\%pr::col, 'log_warn'); my $vb2 = Gtk2::VBox->new (FALSE, 5); $vb2->set_border_width (PAGE_SPC); $vb2->pack_start ($cb_log_err, FALSE, FALSE, 0); $vb2->pack_start ($cb_log_in, FALSE, FALSE, 0); $vb2->pack_start ($cb_log_msg, FALSE, FALSE, 0); $vb2->pack_start ($cb_log_out, FALSE, FALSE, 0); $vb2->pack_start ($cb_log_warn, FALSE, FALSE, 0); $log_frame->add ($vb2); $cf->pack_start ($msgview_frame, FALSE, FALSE, 0); $cf->pack_start ($log_frame, FALSE, FALSE, 0); return $cf; } # the command line help sub print_help() { my $line = '-' x length ($xl::s{about_title}) . "\n"; print $line; print $xl::s{about_title} . "\n"; print $xl::s{about_version} . " $VERSION\n"; print $line; print _("Syntax:\n"); print _(" clawsker [options]\n"); print _("Options:\n"); print _(" --help Prints this help screen.\n"); print _(" --alternate-config-dir Uses as Claws Mail config dir.\n"); print _(" --clawsrc Uses as full resource name.\n"); } # parse the command line sub parse_command_line() { my $arg = 0; while (defined($ARGV[$arg])) { for ($ARGV[$arg]) { /--help/ && do { &print_help; exit 0; }; /--alternate-config-dir/ && do { ++$arg; die "$xl::s{e_error}$xl::s{e_requireddir}\n" unless defined($ARGV[$arg]); die "$xl::s{e_error}$xl::s{e_notadir}\n" unless -d $ARGV[$arg]; $CONFIGDIR = $ARGV[$arg]; $ALTCONFIGDIR = TRUE; last; }; /--clawsrc/ && do { ++$arg; die "$xl::s{e_error}$xl::s{e_requiredfile}\n" unless defined($ARGV[$arg]); die "$xl::s{e_error}$xl::s{e_notafile}\n" unless -f $ARGV[$arg]; &set_rc_filename ($ARGV[$arg]); last; }; /.*/ && die $xl::s{e_error} . _("unknown option '{opt}'.\n", opt => $ARGV[$arg]); } ++$arg; } } # update the hidden preferences status from loaded values sub init_hidden_preferences() { foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth) { foreach my $key (keys %$hash) { $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]}; } } } # load current status from disc sub load_preferences() { my $rc = &get_rc_filename; -f $rc or die "$xl::s{e_error}$xl::s{e_noclawsrc}\n"; &check_claws_not_running; open (RCF, "<$rc"); while () { chomp; if (/^([8a-z_]+)=(.*)$/) { $PREFS{$1} = "$2"; } } close (RCF); } # save current preferences to disc sub save_preferences() { my $rc = &get_rc_filename; -f $rc or die "$xl::s{e_error}$xl::s{e_noclawsrc}\n"; &check_claws_not_running; my $rcbak = "$rc.backup"; rename ($rc, $rcbak); open (RCF, ">$rc"); open (RCB, "<$rcbak"); while () { chomp; if (/^([8a-z_]+)=(.*)$/) { if (defined($HPVALUE{$1})) { print RCF $1 . "=" . $HPVALUE{$1} . "\n"; } else { print RCF $_ . "\n"; } } else { print RCF $_ . "\n"; } } close (RCB); close (RCF); } # create notebook sub new_notebook() { my $nb = Gtk2::Notebook->new; # $nb->append_page (&new_behaviour_page, $xl::s{tab_behaviour}); $nb->append_page (&new_colours_page, $xl::s{tab_colours}); $nb->append_page (&new_gui_page, $xl::s{tab_gui}); $nb->append_page (&new_other_page, $xl::s{tab_other}); return $nb; } # create an about dialog sub new_about_dialog() { my ($parent) = @_; my $title = $xl::s{about_title}; my $lic = $xl::s{about_license}; my $vers = $xl::s{about_version} . " $VERSION"; my $license = "This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>."; my $year = "2007"; my $holder = "Ricardo Mones <ricardo\@mones.org>"; my $dialog = Gtk2::MessageDialog->new_with_markup ($parent, [qw/modal destroy-with-parent/], 'info', 'close', "$title\n" . "$vers\n" . "Copyright $year by $holder\n\n" . "$lic\n\n" . "$license"); $dialog->set_title ($xl::s{about}); # return $dialog; } # create buttons box sub new_button_box() { my ($adlg) = @_; my $b_about = Gtk2::Button->new_from_stock ('gtk-about'); my $b_exit = Gtk2::Button->new_from_stock ('gtk-quit'); my $b_apply = Gtk2::Button->new_from_stock ('gtk-apply'); # disable button until is really implemented # my $b_undo = Gtk2::Button->new_from_stock ('gtk-undo'); my $hbox = Gtk2::HBox->new (FALSE, 5); # signal handlers $b_exit->signal_connect (clicked => sub { Gtk2->main_quit }); $b_apply->signal_connect (clicked => sub { &save_preferences }); # $b_undo->signal_connect (clicked => sub { &undo_current_changes }); $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide }); # package them $hbox->pack_end ($b_exit, FALSE, FALSE, 0); $hbox->pack_end ($b_apply, FALSE, FALSE, 0); # $hbox->pack_end ($b_undo, FALSE, FALSE, 0); $hbox->pack_start ($b_about, FALSE, FALSE, 0); # return $hbox; } # initialise &parse_command_line; &load_preferences; &init_hidden_preferences; # create main GUI my $window = Gtk2::Window->new ('toplevel'); my $box = Gtk2::VBox->new (FALSE, 5); $box->set_border_width(3); my $about = &new_about_dialog; $box->pack_start (&new_notebook, FALSE, FALSE, 0); $box->pack_end (&new_button_box ($about), FALSE, FALSE, 0); $window->signal_connect (delete_event => sub { Gtk2->main_quit }); $window->set_title ($xl::s{win_title}); $window->add ($box); $window->show_all; Gtk2->main;