Unhidden 'warn_sending_many_recipients_num'
[clawsker.git] / clawsker
index 109d1eaac2fada76deed238ce40acc7a4566fca0..f3af7a5a9066c4d70f858726b3a33bbd5265813c 100755 (executable)
--- a/clawsker
+++ b/clawsker
@@ -1,7 +1,7 @@
 #!/usr/bin/perl -w
 #
 # Clawsker :: A Claws Mail Tweaker
-# Copyright 2007-2016 Ricardo Mones <ricardo@mones.org>
+# Copyright 2007-2017 Ricardo Mones <ricardo@mones.org>
 #
 # 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
@@ -17,6 +17,7 @@ binmode STDOUT, ":encoding(utf8)";
 use 5.010_000;
 use strict;
 use utf8;
+use version 0.77;
 use Glib qw(TRUE FALSE);
 use Gtk2;
 use POSIX qw(setlocale);
@@ -71,6 +72,7 @@ sub _ {
     tab_winpos => _('Windows'),
     tab_accounts => _('Accounts'),
     tab_plugins => _('Plugins'),
+    tab_hotkeys => _('Hotkeys'),
     tab_info => _('Info'),
 
     ab_frame => _('Addressbook'),
@@ -89,12 +91,16 @@ sub _ {
     netm_frame => _('NetworkManager'),
     diff_frame => _('Viewing patches'),
     mpass_frame => _('Master passphrase'),
+    compose_frame => _('Compose window'),
+    qs_frame => _('Quick search'),
 
     l_oth_use_dlg => _('Use detached address book edit dialogue'),
     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.'),
-    l_oth_max_use => _('Maximum memory for message cache (kB)'),
+    l_oth_max_use => _('Maximum memory for message cache'),
+    l_oth_max_use_units => _('kilobytes'),
     h_oth_max_use => _('The maximum amount of memory to use to cache messages, in kilobytes.'),
-    l_oth_min_time => _('Minimun time for cache elements (minutes)'),
+    l_oth_min_time => _('Minimun time for cache elements'),
+    l_oth_min_time_units => _('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_oth_use_netm => _('Use NetworkManager'),
     h_oth_use_netm => _('Use NetworkManager to switch offline automatically.'),
@@ -141,10 +147,14 @@ sub _ {
     h_gui_zero_char => _('Replaces \'0\' with the given character in Folder List.'),
     l_gui_type_any => _('Editable headers'),
     h_gui_type_any => _('Allows to manually type any value in Compose Window header entries or just select from the available choices in the associated dropdown list.'),
+    l_gui_warn_send_multi => _('Warn when sending to more than'),
+    l_gui_warn_send_multi_units => _('recipients'),
+    h_gui_warn_send_multi => _('Show a warning dialogue when sending to more recipients than specified. Use 0 to disable this check.'),
     l_gui_next_del => _('Select next message on delete'),
     h_gui_next_del => _('When deleting a message, toggles between selecting the next one (newer message) or the previous one (older message).'),
 
-    l_beh_hover_t => _('Drag \'n\' drop hover timeout (ms)'),
+    l_beh_hover_t => _('Drag \'n\' drop hover timeout'),
+    l_beh_hover_t_units => _('milliseconds'),
     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.'),
@@ -154,9 +164,11 @@ sub _ {
     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 bar update step (items)'),
+    l_beh_up_step => _('Progress bar update step every'),
+    l_beh_up_step_units => _('items'),
     h_beh_up_step => _('Update stepping in progress bars.'),
-    l_beh_thread_a => _('Maximum age when threading by subject (days)'),
+    l_beh_thread_a => _('Maximum age when threading by subject'),
+    l_beh_thread_a_units => _('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.'),
@@ -187,15 +199,34 @@ sub _ {
     h_col_log_msg => _('Colour for messages in log window.'),
     l_col_log_out => _('Client messages'),
     h_col_log_out => _('Colour for messages sent to servers in log window.'),
-    l_col_log_warn => _('Warnings'),
+    l_col_log_warn => _('Warning messages'),
     h_col_log_warn => _('Colour for warning messages in log window.'),
 
+    l_col_tags_bg => _('Tags background'),
+    h_col_tags_bg => _('Background colour for tags in message view.'),
+    l_col_tags_text => _('Tags text'),
+    h_col_tags_text => _('Text colour for tags in message view.'),
+
+    l_col_default_header_bg => _('Default headers background'),
+    h_col_default_header_bg => _('Background colour for default headers in compose window.'),
+    l_col_default_header_text => _('Default headers text'),
+    h_col_default_header_text => _('Text colour for default headers in compose window.'),
+
+    l_col_qs_active_bg => _('Active quick search background'),
+    h_col_qs_active_bg => _('Background colour for active quick search.'),
+    l_col_qs_active_text => _('Active quick search text'),
+    h_col_qs_active_text => _('Text colour for active quick search.'),
+    l_col_qs_error_bg => _('Quick search error background'),
+    h_col_qs_error_bg => _('Background colour for quick search error.'),
+    l_col_qs_error_text => _('Quick search error text'),
+    h_col_qs_error_text => _('Text colour for quick search error.'),
+
     l_col_diff_add => _('Added lines'),
     h_col_diff_add => _('Colour for added lines in patches.'),
     l_col_diff_del => _('Deleted lines'),
     h_col_diff_del => _('Colour for deleted lines in patches.'),
     l_col_diff_hunk => _('Hunk lines'),
-    h_col_diff_hunk => _('Color for hunk headers in patches.'),
+    h_col_diff_hunk => _('Colour for hunk headers in patches.'),
 
     l_win_x => _('X position'),
     h_win_x => _('X coordinate for window\'s top-left corner.'),
@@ -221,7 +252,7 @@ sub _ {
     l_plu_lav_burl => _('Base URL'),
     h_plu_lav_burl => _('This is the URL where avatar requests are sent. You can use the one of your own libravatar server, if available.'),
     l_plu_prl_flvb => _('Log level'),
-    h_plu_prl_flvb => _('Verbosity level of log, acumulative.'),
+    h_plu_prl_flvb => _('Verbosity level of log, accumulative.'),
     l_plu_prl_none => _('None'),
     l_plu_prl_manual => _('Manual'),
     l_plu_prl_action => _('Actions'),
@@ -249,6 +280,10 @@ my $ACCOUNTRC = 'accountrc';
 # supported and available plugins lists
 my @PLUGINS = qw(AttRemover GPG ManageSieve Libravatar PerlPlugin);
 my @AVPLUGINS = ();
+# loaded hotkeys from load_menurc
+my $HOTKEYS;
+# current tree selection
+my $SELHOTKEY;
 # loaded icons
 my @APPICONS = ();
 
@@ -262,7 +297,7 @@ use constant CMDEF => 5; # default value for the preference in Claws Mail
 use constant PLUGIN => 6; # plugin section (only in plugin preferences)
 
 # constants for GUI spacing
-use constant HBOX_SPC => 5;
+use constant HBOX_PAD => 5;
 use constant FRAME_SPC => 2;
 use constant PAGE_SPC => 5;
 
@@ -270,18 +305,9 @@ use constant PAGE_SPC => 5;
 
 sub version_greater_or_equal {
     my ($version, $refvers) = @_;
-    my @version = split (/\./, $version);
-    my @refvers = split (/\./, $refvers);
-    while ($#version < $#refvers) {
-        push (@version, '0');
-    }
-    my $idx = 0;
-    while (($idx <= $#refvers)
-            and (int ($version[$idx]) == int ($refvers[$idx]))) {
-        ++$idx;
-    }
-    return TRUE if (($idx > $#refvers)
-        or (int ($version[$idx]) >= int ($refvers[$idx])));
+    return TRUE if (length($version) == 0 and length($refvers) >= 0);
+    return FALSE if (length($version) >= 0 and length($refvers) == 0);
+    return TRUE if (version->parse($version) >= version->parse($refvers));
     return FALSE;
 }
 
@@ -380,6 +406,10 @@ sub get_ac_rc_filename {
     return $CONFIGDIR . $ACCOUNTRC;
 }
 
+sub get_menurc_filename {
+    return $CONFIGDIR . "menurc";
+}
+
 sub set_rc_filename {
     my ($fullname) = @_;
     my @parts = split ('/', $fullname);
@@ -462,12 +492,19 @@ sub set_widget_sens {
 
 # graphic element creation
 
+sub new_hbox_spaced_pack {
+    my $hbox = Gtk2::HBox->new (FALSE);
+    foreach (@_) {
+        $hbox->pack_start ($_, FALSE, FALSE, HBOX_PAD);
+    }
+    return $hbox;
+}
+
 sub new_check_button_for($$$) {
     my ($hash, $key, $vhash) = @_;
     my $name = $$hash{$key}[NAME];
     my $label = $$hash{$key}[LABEL];
     #
-    my $hbox = Gtk2::HBox->new (FALSE, 5);
     my $cb = Gtk2::CheckButton->new ($label);
     my $value = $$vhash{$name};
     $value //= $$hash{$key}[CMDEF];
@@ -478,9 +515,8 @@ sub new_check_button_for($$$) {
         });
     set_widget_hint ($cb, $$hash{$key}[DESC]);
     set_widget_sens ($cb, $$hash{$key}[CMVER]);
-    $hbox->pack_start ($cb, FALSE, FALSE, HBOX_SPC);
     #
-    return $hbox;
+    return new_hbox_spaced_pack ($cb);
 }
 
 sub new_text_box_for_int($$$) {
@@ -490,7 +526,11 @@ sub new_text_box_for_int($$$) {
     my @type = split (/,/, $$hash{$key}[TYPE]);
     push (@type, 0), push (@type, 10000) unless ($#type > 0);
     #
-    my $hbox = Gtk2::HBox->new (FALSE, 5);
+    my $gunits = undef;
+    if (ref $label eq 'ARRAY') {
+        $gunits = Gtk2::Label->new ($label->[1]);
+        $label = $label->[0];
+    }
     my $glabel = Gtk2::Label->new ($label);
     my $pagei = int (($type[2] - $type[1]) / 10);
     my $gentry = Gtk2::SpinButton->new_with_range ($type[1], $type[2], $pagei);
@@ -505,10 +545,10 @@ sub new_text_box_for_int($$$) {
     set_widget_hint ($gentry, $$hash{$key}[DESC]);
     set_widget_sens ($gentry, $$hash{$key}[CMVER]);
     $glabel->set_sensitive ($gentry->sensitive);
-    $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
-    $hbox->pack_start ($gentry, FALSE, FALSE, HBOX_SPC);
+    $gunits->set_sensitive ($gentry->sensitive) if ($gunits);
     #
-    return $hbox;
+    return new_hbox_spaced_pack ($glabel, $gentry, $gunits) if ($gunits);
+    return new_hbox_spaced_pack ($glabel, $gentry);
 }
 
 sub new_text_box_for_nchar($$$) {
@@ -516,7 +556,6 @@ sub new_text_box_for_nchar($$$) {
     my $name = $$hash{$key}[NAME];
     my $label = $$hash{$key}[LABEL];
     my @type = split (/,/, $$hash{$key}[TYPE]); # char,minlen,maxlen,width
-    my $hbox = Gtk2::HBox->new (FALSE, 5);
     my $glabel = Gtk2::Label->new ($label);
     my $gentry = Gtk2::Entry->new ();
     $gentry->set_max_length($type[2]) if defined ($type[2]);
@@ -533,10 +572,8 @@ sub new_text_box_for_nchar($$$) {
     set_widget_hint ($gentry, $$hash{$key}[DESC]);
     set_widget_sens ($gentry, $$hash{$key}[CMVER]);
     $glabel->set_sensitive ($gentry->sensitive);
-    $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
-    $hbox->pack_start ($gentry, FALSE, FALSE, HBOX_SPC);
     #
-    return $hbox;
+    return new_hbox_spaced_pack ($glabel, $gentry);
 }
 
 sub new_color_button_for($$$) {
@@ -547,7 +584,6 @@ sub new_color_button_for($$$) {
     my $value = $$vhash{$name};
     $value //= $$hash{$key}[CMDEF];
     my $col = gdk_color_from_str ($value);
-    my $hbox = Gtk2::HBox->new (FALSE, 5);
     my $glabel = Gtk2::Label->new ($label);
     my $button = Gtk2::ColorButton->new_with_color ($col);
     $button->set_title ($label);
@@ -559,10 +595,8 @@ sub new_color_button_for($$$) {
     set_widget_hint ($button, $$hash{$key}[DESC]);
     set_widget_sens ($button, $$hash{$key}[CMVER]);
     $glabel->set_sensitive ($button->sensitive);
-    $hbox->pack_start ($button, FALSE, FALSE, HBOX_SPC);
-    $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
     #
-    return $hbox;
+    return new_hbox_spaced_pack ($button, $glabel);
 }
 
 sub new_selection_box_for($$$) {
@@ -570,7 +604,6 @@ sub new_selection_box_for($$$) {
     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;
     my @options = split (';', $$hash{$key}[TYPE]);
@@ -588,16 +621,14 @@ sub new_selection_box_for($$$) {
     set_widget_hint ($combo, $$hash{$key}[DESC]);
     set_widget_sens ($combo, $$hash{$key}[CMVER]);
     $glabel->set_sensitive ($combo->sensitive);
-    $hbox->pack_start ($glabel, FALSE, FALSE, HBOX_SPC);
-    $hbox->pack_start ($combo, FALSE, FALSE, HBOX_SPC);
     #
-    return $hbox;
+    return new_hbox_spaced_pack ($glabel, $combo);
 }
 
 # more graphic helpers
 
 sub new_hbox_pack {
-    my $hbox = Gtk2::HBox->new (FALSE, 5);
+    my $hbox = Gtk2::HBox->new (FALSE);
     $hbox->set_border_width (PAGE_SPC);
     foreach (@_) {
         $hbox->pack_start ($_, FALSE, FALSE, 0);
@@ -605,6 +636,15 @@ sub new_hbox_pack {
     return $hbox;
 }
 
+sub new_hbox_pack_compact {
+    my $hbox = Gtk2::HBox->new (FALSE);
+    $hbox->set_border_width (0);
+    foreach (@_) {
+        $hbox->pack_start ($_, FALSE, FALSE, 0);
+    }
+    return $hbox;
+}
+
 sub new_vbox_pack {
     my $vbox = Gtk2::VBox->new (FALSE, 5);
     $vbox->set_border_width (PAGE_SPC);
@@ -644,18 +684,18 @@ sub new_subpage_frame {
     ],
     max_use => [
         'cache_max_mem_usage',
-        $xl::s{l_oth_max_use},
+        [ $xl::s{l_oth_max_use}, $xl::s{l_oth_max_use_units} ],
         $xl::s{h_oth_max_use},
         'int,0,262144', # 0 Kb - 256 Mb
-        '0.0.0',
+        '0.7.8.36',
         '4096',
     ],
     min_time => [
         'cache_min_keep_time',
-        $xl::s{l_oth_min_time},
+        [ $xl::s{l_oth_min_time}, $xl::s{l_oth_min_time_units} ],
         $xl::s{h_oth_min_time},
         'int,0,120', # 0 minutes - 2 hours
-        '0.0.0',
+        '0.7.8.36',
         '15',
     ],
     use_netm => [
@@ -704,7 +744,7 @@ sub new_other_page() {
         $xl::s{l_gui_b_unread},
         $xl::s{h_gui_b_unread},
         'bool',
-        '0.0.0',
+        '0.5.3',
         '1',
     ],
     no_markup => [
@@ -712,7 +752,7 @@ sub new_other_page() {
         $xl::s{l_gui_no_markup},
         $xl::s{h_gui_no_markup},
         'bool',
-        '0.0.0',
+        '2.1.0.16',
         '0',
     ],
     dot_lines => [
@@ -720,7 +760,7 @@ sub new_other_page() {
         $xl::s{l_gui_dot_lines},
         $xl::s{h_gui_dot_lines},
         'bool',
-        '0.0.0,3.7.10.44',
+        '2.4.0.115,3.7.10.44',
         '0',
     ],
     h_scroll => [
@@ -728,7 +768,7 @@ sub new_other_page() {
         $xl::s{l_gui_h_scroll},
         $xl::s{h_gui_h_scroll},
         'bool',
-        '0.0.0',
+        '0.8.6.18',
         '1',
     ],
     swp_from => [
@@ -736,7 +776,7 @@ sub new_other_page() {
         $xl::s{l_gui_swp_from},
         $xl::s{h_gui_swp_from},
         'bool',
-        '0.0.0',
+        '1.9.13.40',
         '0',
     ],
     v_scroll => [
@@ -744,7 +784,7 @@ sub new_other_page() {
         $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',
-        '0.0.0',
+        '0.7.8.14',
         '0',
     ],
     from_show => [
@@ -760,7 +800,7 @@ sub new_other_page() {
         $xl::s{l_gui_strip_off},
         $xl::s{h_gui_strip_off},
         'int,0,40000', # no idea what this number means
-        '0.0.0',
+        '2.4.0.186',
         '4000',
     ],
     cursor_v => [
@@ -835,6 +875,14 @@ sub new_other_page() {
         '3.12.0.44',
         '0',
     ],
+    warn_send_multi => [
+        'warn_sending_many_recipients_num',
+        [ $xl::s{l_gui_warn_send_multi}, $xl::s{l_gui_warn_send_multi_units} ],
+        $xl::s{h_gui_warn_send_multi},
+        'int,0,1000',
+        '3.14.1.125',
+        '3.15.0.28',
+    ],
     next_del => [
         'next_on_delete',
         $xl::s{l_gui_next_del},
@@ -876,10 +924,12 @@ sub new_gui_page() {
                          $xl::s{mview_frame}, 'not-packed'),
                      FALSE, FALSE, FRAME_SPC);
     $gf->pack_start (new_subpage_frame (
-                         new_hbox_pack (
-                             new_check_button_for (\%pr::gui, 'no_markup', \%HPVALUE),
-                             new_check_button_for (\%pr::gui, 'margin_co', \%HPVALUE),
-                             new_check_button_for (\%pr::gui, 'type_any', \%HPVALUE)),
+                         new_vbox_pack (
+                             new_hbox_pack_compact (
+                                 new_check_button_for (\%pr::gui, 'no_markup', \%HPVALUE),
+                                 new_check_button_for (\%pr::gui, 'margin_co', \%HPVALUE),
+                                 new_check_button_for (\%pr::gui, 'type_any', \%HPVALUE)),
+                             new_text_box_for_int (\%pr::gui, 'warn_send_multi', \%HPVALUE)),
                          $xl::s{compo_frame}, 'not-packed'),
                      FALSE, FALSE, FRAME_SPC);
     $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
@@ -898,7 +948,7 @@ sub new_gui_page() {
 %pr::beh = ( # tweak some behaviour
     hover_t => [
         'hover_timeout',
-        $xl::s{l_beh_hover_t},
+        [ $xl::s{l_beh_hover_t}, $xl::s{l_beh_hover_t_units} ],
         $xl::s{h_beh_hover_t},
         'int,100,3000', # 0.1 seconds - 3 seconds
         '0.0.0',
@@ -938,7 +988,7 @@ sub new_gui_page() {
     ],
     up_step => [
         'statusbar_update_step',
-        $xl::s{l_beh_up_step},
+        [ $xl::s{l_beh_up_step}, $xl::s{l_beh_up_step_units} ],
         $xl::s{h_beh_up_step},
         'int,1,200', # 1 item - 200 items
         '0.0.0',
@@ -946,7 +996,7 @@ sub new_gui_page() {
     ],
     thread_a => [
         'thread_by_subject_max_age',
-        $xl::s{l_beh_thread_a},
+        [ $xl::s{l_beh_thread_a}, $xl::s{l_beh_thread_a_units} ],
         $xl::s{h_beh_thread_a},
         'int,1,30', # 1 day - 30 days
         '0.0.0',
@@ -965,7 +1015,7 @@ sub new_gui_page() {
         $xl::s{l_beh_use_utf8},
         $xl::s{h_beh_use_utf8},
         'bool',
-        '0.0.0',
+        '1.9.14.49',
         '0',
     ],
     warn_dnd => [
@@ -1141,28 +1191,111 @@ sub new_behaviour_page() {
         '3.8.0.54',
         '#a52a2a',
     ],
+    tags_bg => [
+        'tags_bgcolor',
+        $xl::s{l_col_tags_bg},
+        $xl::s{h_col_tags_bg},
+        'color',
+        '3.14.1.31',
+        '#f5f6be',
+    ],
+    tags_text => [
+        'tags_color',
+        $xl::s{l_col_tags_text},
+        $xl::s{h_col_tags_text},
+        'color',
+        '3.14.1.31',
+        '#000000',
+    ],
+    default_header_bg => [
+        'default_header_bgcolor',
+        $xl::s{l_col_default_header_bg},
+        $xl::s{h_col_default_header_bg},
+        'color',
+        '3.14.1.31',
+        '#f5f6be',
+    ],
+    default_header_text => [
+        'default_header_color',
+        $xl::s{l_col_default_header_text},
+        $xl::s{h_col_default_header_text},
+        'color',
+        '3.14.1.31',
+        '#000000',
+    ],
+    qs_active_bg => [
+        'qs_active_bgcolor',
+        $xl::s{l_col_qs_active_bg},
+        $xl::s{h_col_qs_active_bg},
+        'color',
+        '3.14.1.31',
+        '#f5f6be',
+    ],
+    qs_active_text => [
+        'qs_active_color',
+        $xl::s{l_col_qs_active_text},
+        $xl::s{h_col_qs_active_text},
+        'color',
+        '3.14.1.31',
+        '#000000',
+    ],
+    qs_error_bg => [
+        '',
+        $xl::s{l_col_qs_error_bg},
+        $xl::s{h_col_qs_error_bg},
+        'qs_error_bgcolor',
+        '3.14.1.31',
+        '#ff7070',
+    ],
+    qs_error_text => [
+        '',
+        $xl::s{l_col_qs_error_text},
+        $xl::s{h_col_qs_error_text},
+        'qs_error_color',
+        '3.14.1.31',
+        '#000000',
+    ],
 );
 
 sub new_colours_page() {
     return new_vbox_pack (
                new_subpage_frame (
                    new_vbox_pack (
-                       new_color_button_for (\%pr::col, 'emphasis', \%HPVALUE)),
+                       new_color_button_for (\%pr::col, 'emphasis', \%HPVALUE),
+                       new_hbox_pack_compact (
+                           new_color_button_for (\%pr::col, 'tags_text', \%HPVALUE),
+                           new_color_button_for (\%pr::col, 'tags_bg', \%HPVALUE))),
                    $xl::s{msgview_frame}, 'not-packed'),
                new_subpage_frame (
-                   new_vbox_pack (
-                       new_color_button_for (\%pr::col, 'log_err', \%HPVALUE),
-                       new_color_button_for (\%pr::col, 'log_in', \%HPVALUE),
-                       new_color_button_for (\%pr::col, 'log_msg', \%HPVALUE),
-                       new_color_button_for (\%pr::col, 'log_out', \%HPVALUE),
-                       new_color_button_for (\%pr::col, 'log_warn', \%HPVALUE)),
+                   new_hbox_pack (
+                       new_vbox_pack_compact (
+                           new_color_button_for (\%pr::col, 'log_err', \%HPVALUE),
+                           new_color_button_for (\%pr::col, 'log_warn', \%HPVALUE),
+                           new_color_button_for (\%pr::col, 'log_msg', \%HPVALUE)),
+                       new_vbox_pack_compact (
+                           new_color_button_for (\%pr::col, 'log_in', \%HPVALUE),
+                           new_color_button_for (\%pr::col, 'log_out', \%HPVALUE))),
                    $xl::s{log_frame}, 'not-packed'),
                new_subpage_frame (
                    new_vbox_pack (
                        new_color_button_for (\%pr::col, 'diff_add', \%HPVALUE),
                        new_color_button_for (\%pr::col, 'diff_del', \%HPVALUE),
                        new_color_button_for (\%pr::col, 'diff_hunk', \%HPVALUE)),
-                   $xl::s{diff_frame}, 'not-packed')
+                   $xl::s{diff_frame}, 'not-packed'),
+               new_subpage_frame (
+                   new_hbox_pack (
+                       new_color_button_for (\%pr::col, 'default_header_text', \%HPVALUE),
+                       new_color_button_for (\%pr::col, 'default_header_bg', \%HPVALUE)),
+                   $xl::s{compose_frame}, 'not-packed'),
+               new_subpage_frame (
+                   new_hbox_pack (
+                       new_vbox_pack_compact (
+                           new_color_button_for (\%pr::col, 'qs_active_text', \%HPVALUE),
+                           new_color_button_for (\%pr::col, 'qs_error_text', \%HPVALUE)),
+                       new_vbox_pack_compact (
+                           new_color_button_for (\%pr::col, 'qs_active_bg', \%HPVALUE),
+                           new_color_button_for (\%pr::col, 'qs_error_bg', \%HPVALUE))),
+                   $xl::s{qs_frame}, 'not-packed')
            );
 }
 
@@ -1877,6 +2010,7 @@ sub new_accounts_page() {
             $label->set_use_markup (TRUE);
         }
     }
+    $accbook->set_scrollable (TRUE);
     return $accbook;
 }
 
@@ -1991,6 +2125,123 @@ sub new_plugins_page() {
                 $frame{'PerlPlugin'});
 }
 
+use constant {
+    C_LABEL => 0,
+    C_HOTKEY => 1,
+    C_GROUP => 2,
+    C_ACCEL => 3,
+    C_BCOLOR => 4,
+    # cell backgrounds
+    BG_LIGHTER => '#ffffff',
+    BG_DARKER => '#eeeeee'
+};
+
+sub new_hotkeys_list_label {
+    my $renderer = Gtk2::CellRendererText->new ();
+    $renderer->set_property('alignment' => 'left');
+    $renderer->set_property('editable' => FALSE);
+    $renderer->set_property('size-points' => 8);
+    $renderer->set_property('size-set' => TRUE);
+    return $renderer;
+}
+
+sub new_hotkeys_list_hotkey {
+    my $renderer = Gtk2::CellRendererAccel->new ();
+    $renderer->set_property ('accel-mode' => 'gtk');
+    $renderer->set_property ('editable' => TRUE);
+    $renderer->signal_connect ('accel-edited' => sub {
+        my ($w, $path, $key, $mods, $keycode) = @_;
+        my $accel = Gtk2::Accelerator->name ($key, $mods);
+        my ($model, $iter) = $SELHOTKEY->get_selected ();
+        $model->set($iter, C_HOTKEY, "\"$accel\"");
+        my $gkey = $model->get_value ($iter, C_GROUP);
+        my $akey = $model->get_value ($iter, C_ACCEL);
+        my $data = $HOTKEYS->{$gkey}->{$akey};
+        $data->{'key'} = "\"$accel\"";
+        $data->{'enabled'} = TRUE;
+    });
+    $renderer->signal_connect ('accel-cleared' => sub {
+        my ($w, $path) = @_;
+        my ($model, $iter) = $SELHOTKEY->get_selected ();
+        $model->set($iter, C_HOTKEY, "\"\"");
+        my $gkey = $model->get_value ($iter, C_GROUP);
+        my $akey = $model->get_value ($iter, C_ACCEL);
+        my $data = $HOTKEYS->{$gkey}->{$akey};
+        $data->{'key'} = "\"\"";
+        $data->{'enabled'} = FALSE;
+    });
+    return $renderer;
+}
+
+sub new_hotkeys_list {
+    my ($gkey, $group) = @_;
+    my $store = Gtk2::ListStore->new(
+        qw/Glib::String Glib::String Glib::String Glib::String Glib::String/);
+    my $even = FALSE;
+    foreach my $akey (sort keys %$group) {
+        my $iter = $store->append ();
+        my $hotkey = $group->{$akey}->{'key'};
+        my $label = $akey;
+        $label =~ s/[<>]//g; # <rrsyl> and <IMAPFolder> !?
+        my $bgcol = $even ? BG_DARKER: BG_LIGHTER;
+        $store->set ($iter, C_LABEL, $label, C_HOTKEY, $hotkey,
+            C_GROUP, $gkey, C_ACCEL, $akey, C_BCOLOR, $bgcol);
+        $even = not $even;
+    }
+    my $treeview = Gtk2::TreeView->new_with_model ($store);
+    # labels column
+    $treeview->insert_column_with_data_func (
+        0, _("Menu path"), new_hotkeys_list_label (),
+        sub {
+            my ($col, $renderer, $model, $iter, $data) = @_;
+            $renderer->set_property (
+                'text' => $model->get_value ($iter, C_LABEL));
+            $renderer->set_property (
+                'background' => $model->get_value ($iter, C_BCOLOR));
+        }
+    );
+    # hotkeys column
+    $treeview->insert_column_with_data_func (
+        1, _('Hotkey'), new_hotkeys_list_hotkey (),
+        sub {
+            my ($col, $renderer, $model, $iter, $data) = @_;
+            my $hkey = $model->get_value ($iter, C_HOTKEY);
+            $hkey =~ s/\"//g;
+            my ($acckey, $accmod) = Gtk2::Accelerator->parse ($hkey);
+            $renderer->set_property ('accel-key' => $acckey);
+            $renderer->set_property ('accel-mods' => $accmod);
+            $renderer->set_property (
+                'background' => $model->get_value ($iter, C_BCOLOR));
+        }
+    );
+    # callback for saving current selection
+    my $selection = $treeview->get_selection ();
+    $selection->signal_connect ('changed' => sub { $SELHOTKEY = shift });
+    return $treeview;
+}
+
+sub new_hotkeys_page() {
+    my $swin = Gtk2::ScrolledWindow->new ();
+    my $vbox = Gtk2::VBox->new (FALSE, 5);
+    foreach my $gkey (sort keys %$HOTKEYS) {
+        my $group = $HOTKEYS->{$gkey};
+        # group title
+        my $glabel = Gtk2::Label->new ('<b>' . $gkey . '</b>');
+        $glabel->set_use_markup (TRUE);
+        $glabel->set_alignment (0, 0.5);
+        $glabel->set_padding (5, 1);
+        $vbox->pack_start ($glabel, FALSE, FALSE, 0);
+        # group key list
+        my $keylist = new_hotkeys_list ($gkey, $group);
+        $vbox->pack_start ($keylist, FALSE, FALSE, 0);
+    }
+    $swin->set_border_width (5);
+    $swin->set_shadow_type ('none');
+    $swin->set_policy ('automatic', 'always');
+    $swin->add_with_viewport ($vbox);
+    return $swin;
+}
+
 sub new_info_page() {
     my $t0 = Gtk2::Table->new (7, 2, FALSE);
     my $v = get_toolkit_versions ();
@@ -2237,6 +2488,53 @@ sub save_resource {
     close (RCF);
 }
 
+# specific loaders
+sub load_menurc {
+    my $rc = shift;
+    open (RCF, '<:encoding(utf8)', $rc)
+        or die _("Error: opening '{file}' for reading", file => $rc) . ": $!\n";
+    my %groups = ();
+    my $line = 0;
+    while (<RCF>) {
+        chomp;
+        if (/^; \(gtk_accel_path "<([A-Za-z]+)>([^"]+)" ([^\)]+)\)$/) {
+            my %data = ('key' => $3, 'enabled' => FALSE, 'line' => $line);
+            $groups{$1}{$2} = \%data;
+            # say "group -> $1 | path -> $2 | key -> $3";
+        } elsif (/^\(gtk_accel_path "<([A-Za-z]+)>([^"]+)" ([^\)]+)\)$/) {
+            my %data = ('key' => $3, 'enabled' => TRUE, 'line' => $line);
+            $groups{$1}{$2} = \%data;
+            # say "group -> $1 | path -> $2 | key -> $3";
+        }
+        ++$line;
+    }
+    close (RCF);
+    return \%groups;
+}
+
+sub save_menurc {
+    my ($rc, $groups) = @_;
+    my @lines = ();
+    foreach my $gkey (keys %$groups) {
+        my $group = $groups->{$gkey};
+        foreach my $akey (keys %$group) {
+            my $data = $group->{$akey};
+            my $key = $data->{'key'};
+            my $line = $data->{'line'};
+            $lines[$line] = ($data->{'enabled'})? '': '; ';
+            $lines[$line] .= '(gtk_accel_path "<'
+                    . $gkey . '>' . $akey . '" ' . $key . ')';
+        }
+    }
+    open (RCF, '>:utf8', $rc)
+        or die _("Error: opening '{file}' for writing", file => $rc) . ": $!\n";
+    say RCF '; claws-mail GtkAccelMap rc-file         -*- scheme -*-';
+    say RCF '; this file is an automated accelerator map dump';
+    say RCF ';';
+    foreach (@lines) { say RCF $_ if $_ }
+    close (RCF);
+}
+
 # load current status from disc
 sub load_rc_preferences {
     my $rc = get_rc_filename ();
@@ -2272,9 +2570,19 @@ sub load_ac_preferences {
     return TRUE;
 }
 
+sub load_hk_preferences {
+    my $rc = get_menurc_filename ();
+    return FALSE unless check_rc_file ($rc);
+    $HOTKEYS = load_menurc ($rc);
+    return TRUE;
+}
+
 sub load_preferences {
     return FALSE unless check_claws_not_running ();
-    return (load_rc_preferences () and load_ac_preferences ());
+    return (load_rc_preferences ()
+        and load_ac_preferences ()
+        and load_hk_preferences ()
+    );
 }
 
 # save current preferences to disc
@@ -2321,6 +2629,15 @@ sub save_ac_preferences {
     return TRUE;
 }
 
+sub save_hk_preferences {
+    my $rc = get_menurc_filename ();
+    log_message ("Saving hotkey preferences to $rc\n");
+    return FALSE unless check_rc_file ($rc);
+    return FALSE unless check_claws_not_running ();
+    save_menurc ($rc, $HOTKEYS);
+    return TRUE;
+}
+
 # create notebook
 sub new_notebook {
     my $nb = Gtk2::Notebook->new;
@@ -2332,6 +2649,7 @@ sub new_notebook {
     $nb->append_page (new_winpos_page (), $xl::s{tab_winpos});
     $nb->append_page (new_accounts_page (), $xl::s{tab_accounts});
     $nb->append_page (new_plugins_page (), $xl::s{tab_plugins});
+    $nb->append_page (new_hotkeys_page (), $xl::s{tab_hotkeys});
     $nb->append_page (new_info_page (), $xl::s{tab_info});
 
     return $nb;
@@ -2356,7 +2674,7 @@ 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 &lt;http://www.gnu.org/licenses/&gt;.";
-    my $year = "2007-2016";
+    my $year = "2007-2017";
     my $holder = "Ricardo Mones &lt;ricardo\@mones.org&gt;";
     my $url = "http://www.claws-mail.org/clawsker.php";
 
@@ -2372,7 +2690,7 @@ along with this program.  If not, see &lt;http://www.gnu.org/licenses/&gt;.";
     $dialog->set_title ($xl::s{about});
     if (Gtk2->CHECK_VERSION (2, 10, 0)) {
         my @icons = get_app_icons ();
-        my $image = Gtk2::Image->new_from_pixbuf ($icons[1]);
+        my $image = Gtk2::Image->new_from_pixbuf ($icons[-1]);
         $image->show ();
         $image->set_alignment (0, 0);
         $dialog->set_image ($image);
@@ -2396,6 +2714,7 @@ sub new_button_box {
     $b_apply->signal_connect (clicked => sub {
         save_preferences ($parent);
         save_ac_preferences ($parent);
+        save_hk_preferences ($parent);
     });
     # $b_undo->signal_connect (clicked => sub { undo_current_changes });
     $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
@@ -2439,7 +2758,7 @@ exit unless init_hidden_preferences ();
 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_start (new_notebook (), TRUE, TRUE, 0);
 $box->pack_end (new_button_box ($main_window, $about), FALSE, FALSE, 0);
 $main_window->signal_connect (delete_event => sub { Gtk2->main_quit });
 $main_window->set_title ($xl::s{win_title});