Remove unused constants and add another
[clawsker.git] / clawsker
index 24a370e724d82b65fbad1f71435e64764928f756..9cef429267e50bd6ac313cd5aa2adf43c6838aa6 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-2018 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,8 +17,9 @@ binmode STDOUT, ":encoding(utf8)";
 use 5.010_000;
 use strict;
 use utf8;
+use version 0.77;
 use Glib qw(TRUE FALSE);
-use Gtk2;
+use Gtk3;
 use POSIX qw(setlocale);
 use Locale::gettext;
 use Encode;
@@ -41,9 +42,6 @@ setlocale (LC_ALL, $locale);
 bindtextdomain ($NAME, sprintf ('%s/share/locale', $PREFIX));
 textdomain ($NAME);
 
-my $SHOWHINTS = FALSE;
-$SHOWHINTS = TRUE if ($Gtk2::VERSION >= 1.040 and Gtk2->CHECK_VERSION (2, 12, 0));
-
 sub _ {
     my $str = shift;
     my %par = @_;
@@ -59,10 +57,14 @@ sub _ {
 # 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:'),
+    about_web_label => _("Visit Clawsker's web page"),
+
+    error_title => _('Clawsker error'),
+
+    exit_title => _('Clawsker warning'),
+    exit_fact => _('There are unapplied modifications.'),
+    exit_question => _('Do you really want to quit?'),
 
     tab_colours => _('Colours'),
     tab_behaviour => _('Behaviour'),
@@ -70,29 +72,17 @@ sub _ {
     tab_other => _('Other'),
     tab_winpos => _('Windows'),
     tab_accounts => _('Accounts'),
-
-    ab_frame => _('Addressbook'),
-    mem_frame => _('Memory'),
-    msgview_frame => _('Message View'),
-    log_frame => _('Log window'),
-    dnd_frame => _('Drag \'n\' drop'),
-    ssl_frame => _('Secure Sockets Layer'),
-    msgs_frame => _('Messages'),
-    swc_frame => _('Completion'),
-    stripes_frame => _('Coloured stripes'),
-    sbar_frame => _('Scroll bars'),
-    mlist_frame => _('Message List'),
-    mview_frame => _('Message View'),
-    compo_frame => _('Compose window'),
-    netm_frame => _('NetworkManager'),
-    diff_frame => _('Viewing patches'),
-    mpass_frame => _('Master passphrase'),
+    tab_plugins => _('Plugins'),
+    tab_hotkeys => _('Hotkeys'),
+    tab_info => _('Info'),
 
     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.'),
@@ -139,10 +129,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.'),
@@ -152,9 +146,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.'),
@@ -163,15 +159,17 @@ sub _ {
     l_beh_warn_dnd => _('Warn on drag \'n\' drop'),
     h_beh_warn_dnd => _('Display a confirmation dialogue on drag \'n\' drop of folders.'),
     l_beh_out_ascii => _('Outgoing messages fallback to ASCII'),
-    h_beh_out_ascii => _('If content allows, ASCII will be used to encode outgoing messages, otherwise the user-defined encoding is enforced always.'),
+    h_beh_out_ascii => _('If allowed by content, ASCII will be used to encode outgoing messages, otherwise the user-defined encoding is always enforced.'),
     l_beh_pp_unsel => _('Primary paste unselects selection'),
     h_beh_pp_unsel => _('Controls how pasting using middle-click changes the selected text and insertion point.'),
     l_beh_inline_at => _('Show inline attachments'),
-    h_beh_inline_at => _('Allows hiding inline attachments already shown in mail structure view.'),
+    h_beh_inline_at => _('Allows to hide inline attachments already shown in mail structure view.'),
     l_beh_addr_swc => _('Address search in compose window matches any'),
     h_beh_addr_swc => _('On Tab-key completion, address text will match any part of the string or only from the start.'),
     l_beh_fold_swc => _('Folder search in folder selector matches any'),
     h_beh_fold_swc => _('On folder name completion text will match any part of the string or only from the start.'),
+    l_beh_rewrite_ff => _('Rewrite first \'From\' using QP encoding'),
+    h_beh_rewrite_ff => _('Workaround some servers which convert first \'From\' to \'>From\' by using Quoted-Printable transfer encoding instead of 7bit/8bit encoding.'),
 
     l_col_emphasis => _('X-Mailer header'),
     h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
@@ -183,15 +181,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.'),
@@ -212,61 +229,73 @@ sub _ {
     l_acc_gtls_pri => _('GnuTLS priority'),
     h_acc_gtls_pri => _('Value to use as GnuTLS priority string if custom priority check is enabled. Otherwise this value is ignored.'),
 
+    l_plu_gpg_alimit => _('Autocompletion limit'),
+    h_plu_gpg_alimit => _('Limits the number of addresses obtained from keyring through autocompletion. Use 0 to get all matches.'),
     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'),
     l_plu_prl_match => _('Matches'),
-
-    e_error => _('Error: '),
-    e_noclawsrc => _('resource file for Claws Mail was not found.'),
-    e_running => _('seems Claws Mail is currently running, close it first.'),
 );
 
+# data and metadata of resource files
+my $CONFIGDATA;
+my $CONFIGMETA;
+my $ACCOUNTDATA;
+my $ACCOUNTMETA;
 # all preferences read by load_preferences
 my %PREFS = ();
 my %ACPREFS = ();
+my %PLPREFS = ();
 # values of all preferences handled by clawsker
 my %HPVALUE = ();
 my %ACHPVALUE = ();
+my %PLHPVALUE = ();
 # default config dir and file name
 my $ALTCONFIGDIR = FALSE;
 my $CONFIGDIR = $ENV{HOME} . '/.claws-mail/';
 my $CONFIGRC = 'clawsrc';
 my $ACCOUNTRC = 'accountrc';
-
-# 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 CMVER => 4; # lowest[,highest] Claws Mail version(s) the feature exists
-use constant CMDEF => 5; # default value for the preference in Claws Mail
-
-# constants for GUI spacing
-use constant HBOX_SPC => 5;
-use constant FRAME_SPC => 2;
-use constant PAGE_SPC => 5;
+# 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 = ();
+# modification flag
+my $MODIFIED = 0;
+
+use constant {
+    # index constants for preference arrays
+    NAME  => 0, # the name on the rc file
+    LABEL => 1, # the label on the GUI
+    DESC  => 2, # the description for the hint/help
+    TYPE  => 3, # data type: bool, int, float, string, color
+    CMVER => 4, # lowest[,highest] Claws Mail version(s) the feature exists
+    CMDEF => 5, # default value for the preference in Claws Mail
+    PLUGIN => 6, # plugin section (only in plugin preferences)
+    # constants for GUI spacing
+    HBOX_PAD => 5,
+    GRID_SPC => 10,
+};
+
+# for data references indexing
+use constant VALUE => 0;
+use constant IVALUE => 1;
 
 # version functions
 
 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;
 }
 
@@ -295,7 +324,9 @@ sub get_claws_version {
 
 sub handle_bool_value {
     my ($widget, $event, $dataref) = @_;
-    $$dataref = ($widget->get_active ())? '1': '0';
+    $$dataref->[VALUE] = ($widget->get_active ())? '1': '0';
+    $MODIFIED += $$dataref->[VALUE] != $$dataref->[IVALUE]? 1: -1
+        if defined $$dataref->[IVALUE];
 }
 
 sub handle_int_value {
@@ -304,57 +335,60 @@ sub handle_int_value {
     s/^\s+//;
     s/\s+$//;
     if (/^[0-9]+$/) {
-        $$dataref = $_;
+        $$dataref->[VALUE] = $_;
         $widget->set_text ($_);
     }
     else {
-        $widget->set_text ($$dataref);
+        $widget->set_text ($$dataref->[VALUE]);
     }
-}
-
-sub handle_string_value {
-    my ($widget, $event, $dataref) = @_;
-    $$dataref = $widget->get_text ();
+    $MODIFIED += $$dataref->[VALUE] != $$dataref->[IVALUE]? 1: -1
+        if defined $$dataref->[IVALUE];
 }
 
 sub handle_nchar_value {
     my ($widget, $event, $dataref, $minlen, $maxlen) = @_;
     $_ = substr ($widget->get_text (), 0, $maxlen);
     $widget->set_text ($_);
-    $$dataref = $_;
+    $$dataref->[VALUE] = $_;
+    $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
+        if defined $$dataref->[IVALUE];
 }
 
-sub gdk_color_from_str {
+sub gdk_rgba_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;
+        $rr = hex($1) / 256;
+        $gg = hex($2) / 256;
+        $bb = hex($3) / 256;
     }
-    my $color = Gtk2::Gdk::Color->new ($rr, $gg, $bb);
+    my $color = Gtk3::Gdk::RGBA->new ($rr, $gg, $bb, 1.0);
     return $color;
 }
 
-sub str_from_gdk_color {
+sub str_from_gdk_rgba {
     my ($color) = @_;
-    my $rr = $color->red / 256;
-    my $gg = $color->green / 256;
-    my $bb = $color->blue / 256;
+    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);
+    my $newcol = $widget->get_rgba;
+    $$dataref->[VALUE] = str_from_gdk_rgba ($newcol);
+    $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
+        if defined $$dataref->[IVALUE];
 }
 
 sub handle_selection_value {
     my ($widget, $event, $dataref) = @_;
-    $$dataref = $widget->get_active;
+    $$dataref->[VALUE] = $widget->get_active;
+    $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
+        if defined $$dataref->[IVALUE];
 }
 
 sub get_rc_filename {
@@ -365,6 +399,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);
@@ -383,17 +421,43 @@ sub log_message {
     }
 }
 
+sub message_dialog {
+    my ($parent, $title, $markup, $type, $buttons) = @_;
+    my $flags = [qw/modal destroy-with-parent/];
+    my $dialog = Gtk3::Dialog->new_with_buttons (
+        $title, $parent, $flags, @$buttons
+    );
+    my $label = Gtk3::Label->new;
+    $label->set_markup ($markup);
+    my $icon = undef;
+    if ($type eq 'error') {
+      $icon = Gtk3::Image->new_from_icon_name('dialog-error', 'GTK_ICON_SIZE_DIALOG');
+    } elsif ($type eq 'warning') {
+      $icon = Gtk3::Image->new_from_icon_name('dialog-warning', 'GTK_ICON_SIZE_DIALOG');
+    } elsif ($type eq 'question') {
+      $icon = Gtk3::Image->new_from_icon_name('dialog-question', 'GTK_ICON_SIZE_DIALOG');
+    }
+    my $hbox = Gtk3::Box->new ('horizontal', 5);
+    $hbox->pack_start ($icon, FALSE, FALSE, 5) if defined $icon;
+    $hbox->pack_start ($label, FALSE, FALSE, 5);
+    my $dialogbox = $dialog->get_content_area;
+    $dialogbox->add ($hbox);
+    $dialogbox->show_all;
+    return $dialog;
+}
+
 sub error_dialog {
     my ($emsg) = @_;
     my $markup = "<span weight=\"bold\" size=\"large\">" . $emsg . "</span>";
-    my $errordlg = Gtk2::MessageDialog->new_with_markup ($main_window, 'modal', 'error', 'cancel', $markup);
-    $errordlg->set_title (_('Clawsker error'));
+    my $errordlg = message_dialog (
+        $main_window, $xl::s{error_title}, $markup, 'error', [ 'gtk-cancel', 0 ]
+    );
     $errordlg->run;
     $errordlg->destroy;
 }
 
 sub claws_is_running {
-    my $emsg = "$xl::s{e_error}$xl::s{e_running}";
+    my $emsg = _('Error: seems Claws Mail is currently running, close it first.');
     log_message ($emsg);
     error_dialog ($emsg);
     return FALSE;
@@ -416,7 +480,7 @@ sub check_claws_not_running {
 sub check_rc_file {
     my ($rcfile) = @_;
     (defined($rcfile) && -f $rcfile) or do {
-        my $emsg = "$xl::s{e_error}$xl::s{e_noclawsrc}\n";
+        my $emsg = _('Error: resource file for Claws Mail was not found.');
         log_message ($emsg);
         error_dialog ($emsg);
         return FALSE;
@@ -425,11 +489,9 @@ sub check_rc_file {
 }
 
 sub set_widget_hint {
-    if ($SHOWHINTS) {
-        my ($wdgt, $hint) = @_;
-        $wdgt->set_tooltip_text ($hint);
-        $wdgt->set_has_tooltip (TRUE);
-    }
+    my ($wdgt, $hint) = @_;
+    $wdgt->set_tooltip_text ($hint);
+    $wdgt->set_has_tooltip (TRUE);
 }
 
 sub set_widget_sens {
@@ -447,14 +509,21 @@ sub set_widget_sens {
 
 # graphic element creation
 
+sub new_hbox_spaced_pack {
+    my $hbox = Gtk3::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};
+    my $cb = Gtk3::CheckButton->new ($label);
+    my $value = $$vhash{$name}[VALUE];
     $value //= $$hash{$key}[CMDEF];
     $cb->set_active ($value eq '1');
     $cb->signal_connect (clicked => sub {
@@ -463,9 +532,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($$$) {
@@ -475,11 +543,15 @@ 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 $glabel = Gtk2::Label->new ($label);
+    my $gunits = undef;
+    if (ref $label eq 'ARRAY') {
+        $gunits = Gtk3::Label->new ($label->[1]);
+        $label = $label->[0];
+    }
+    my $glabel = Gtk3::Label->new ($label);
     my $pagei = int (($type[2] - $type[1]) / 10);
-    my $gentry = Gtk2::SpinButton->new_with_range ($type[1], $type[2], $pagei);
-    my $value = $$vhash{$name};
+    my $gentry = Gtk3::SpinButton->new_with_range ($type[1], $type[2], $pagei);
+    my $value = $$vhash{$name}[VALUE];
     $value //= $$hash{$key}[CMDEF];
     $gentry->set_numeric (TRUE);
     $gentry->set_value ($value);
@@ -489,11 +561,11 @@ 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);
+    $glabel->set_sensitive ($gentry->get_sensitive);
+    $gunits->set_sensitive ($gentry->get_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($$$) {
@@ -501,14 +573,13 @@ 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 ();
+    my $glabel = Gtk3::Label->new ($label);
+    my $gentry = Gtk3::Entry->new ();
     $gentry->set_max_length($type[2]) if defined ($type[2]);
     my $width = $type[3];
     $width //= $type[2];
     $gentry->set_width_chars(int ($width) + 2) if defined ($width);
-    my $value = $$vhash{$name};
+    my $value = $$vhash{$name}[VALUE];
     $value //= $$hash{$key}[CMDEF];
     $gentry->set_text ($value);
     $gentry->signal_connect('key-release-event' => sub {
@@ -517,11 +588,9 @@ 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);
+    $glabel->set_sensitive ($gentry->get_sensitive);
     #
-    return $hbox;
+    return new_hbox_spaced_pack ($glabel, $gentry);
 }
 
 sub new_color_button_for($$$) {
@@ -529,12 +598,11 @@ sub new_color_button_for($$$) {
     my $name = $$hash{$key}[NAME];
     my $label = $$hash{$key}[LABEL];
     #
-    my $value = $$vhash{$name};
+    my $value = $$vhash{$name}[VALUE];
     $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);
+    my $col = gdk_rgba_from_str ($value);
+    my $glabel = Gtk3::Label->new ($label);
+    my $button = Gtk3::ColorButton->new_with_rgba ($col);
     $button->set_title ($label);
     $button->set_relief ('none');
     $button->signal_connect ('color-set' => sub {
@@ -543,11 +611,9 @@ 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);
+    $glabel->set_sensitive ($button->get_sensitive);
     #
-    return $hbox;
+    return new_hbox_spaced_pack ($button, $glabel);
 }
 
 sub new_selection_box_for($$$) {
@@ -555,65 +621,83 @@ 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 $glabel = Gtk3::Label->new ($label);
+    my $combo = Gtk3::ComboBoxText->new;
     my @options = split (';', $$hash{$key}[TYPE]);
     foreach my $opt (@options) {
         my ($index, $textkey) = split ('=', $opt);
-        $combo->insert_text ($index, $xl::s{$textkey});
+        $combo->insert (-1, $index, $xl::s{$textkey});
     }
     $combo->signal_connect ('changed' => sub {
             my ($w, $e) = @_;
             handle_selection_value ($w, $e, \$$vhash{$name});
         });
-    my $value = $$vhash{$name};
+    my $value = $$vhash{$name}[VALUE];
     $value //= $$hash{$key}[CMDEF];
     $combo->set_active ($value);
     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);
+    $glabel->set_sensitive ($combo->get_sensitive);
     #
-    return $hbox;
+    return new_hbox_spaced_pack ($glabel, $combo);
 }
 
 # more graphic helpers
 
-sub new_hbox_pack {
-    my $hbox = Gtk2::HBox->new (FALSE, 5);
-    $hbox->set_border_width (PAGE_SPC);
-    foreach my $item (@_) {
-        $hbox->pack_start ($item, FALSE, FALSE, 0);
-    }
-    return $hbox;
+sub new_grid {
+    my ($border_w, $row_s, $col_s) = @_;
+    $border_w //= GRID_SPC;
+    $row_s //= GRID_SPC;
+    $col_s //= GRID_SPC;
+    my $grid = Gtk3::Grid->new;
+    $grid->set_border_width ($border_w);
+    $grid->set_row_spacing ($row_s);
+    $grid->set_column_spacing ($col_s);
+    return $grid;
 }
 
-sub new_vbox_pack {
-    my $vbox = Gtk2::VBox->new (FALSE, 5);
-    $vbox->set_border_width (PAGE_SPC);
-    foreach my $item (@_) {
-        $vbox->pack_start ($item, FALSE, FALSE, 0);
-    }
-    return $vbox;
+sub new_label {
+    my $text = shift;
+    $text //= '';
+    my $label = Gtk3::Label->new ($text);
+    $label->set_alignment (0, 0.5);
+    return $label;
 }
 
-sub new_vbox_pack_compact {
-    my $vbox = Gtk2::VBox->new (FALSE, 0);
-    $vbox->set_border_width (0);
-    foreach my $item (@_) {
-        $vbox->pack_start ($item, FALSE, FALSE, 0);
-    }
-    return $vbox;
+sub new_title {
+    my $text = shift;
+    $text //= '';
+    my $label = Gtk3::Label->new ('<b>' . $text . '</b>');
+    $label->set_use_markup (TRUE);
+    $label->set_alignment (0, 0.5);
+    return $label;
 }
 
-sub new_subpage_frame {
-    my ($box, $title, $notpacked) = @_;
-    my $frame = Gtk2::Frame->new ($title);
-    $frame->add ($box);
-    return new_vbox_pack ($frame) unless defined ($notpacked);
-    return $frame;
+sub new_grid_pack {
+    my ($width, $height, $widget) = @_;
+    my $grid = new_grid ();
+    $grid->set_column_homogeneous (TRUE);
+    for (my $i = 0; $i < $width; ++$i) {
+        for (my $j = 0; $j < $height; ++$j) {
+            my $wid = $widget->[$j]->[$i];
+            next unless defined $wid;
+            my $ww = (($i + 1 < $width) and (defined $widget->[$j]->[$i + 1]))
+                ? 1
+                : $width - $i;
+            if (ref $wid) {
+                $grid->attach ($wid, $i, $j, $ww, 1);
+            } else { # not a widget
+                if ('--' eq $wid) { # a separator
+                    $grid->attach (Gtk3::Separator->new ('horizontal'),
+                        $i, $j, $ww, 1);
+                } else { # or a title
+                    $grid->attach (new_title ($wid),
+                        $i, $j, $ww, 1);
+                }
+            }
+        }
+    }
+    return $grid;
 }
 
 # preference maps and corresponding page creation subs
@@ -629,18 +713,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',
+        'int,0,524288', # 0 Kb - 512 Mb
+        '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 => [
@@ -662,25 +746,20 @@ sub new_subpage_frame {
 );
 
 sub new_other_page() {
-    return new_vbox_pack (
-               new_subpage_frame (
-                   new_vbox_pack (
-                       new_check_button_for(\%pr::oth, 'use_dlg', \%HPVALUE)),
-                   $xl::s{ab_frame}, 'not-packed'),
-               new_subpage_frame (
-                   new_vbox_pack (
-                       new_text_box_for_int(\%pr::oth, 'max_use', \%HPVALUE),
-                       new_text_box_for_int(\%pr::oth, 'min_time', \%HPVALUE)),
-                   $xl::s{mem_frame}, 'not-packed'),
-               new_subpage_frame (
-                   new_vbox_pack (
-                       new_check_button_for(\%pr::oth, 'use_netm', \%HPVALUE)),
-                   $xl::s{netm_frame}, 'not-packed'),
-               new_subpage_frame (
-                   new_vbox_pack (
-                       new_text_box_for_int(\%pr::oth, 'mp_rounds', \%HPVALUE)),
-                   $xl::s{mpass_frame}, 'not-packed')
-           );
+    return new_grid_pack (1, 12, [
+        [ _('Addressbook') ],
+        [ new_check_button_for(\%pr::oth, 'use_dlg', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Memory') ],
+        [ new_text_box_for_int(\%pr::oth, 'max_use', \%HPVALUE) ],
+        [ new_text_box_for_int(\%pr::oth, 'min_time', \%HPVALUE) ],
+        [ '--' ],
+        [ _('NetworkManager') ],
+        [ new_check_button_for(\%pr::oth, 'use_netm', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Master passphrase') ],
+        [ new_text_box_for_int(\%pr::oth, 'mp_rounds', \%HPVALUE) ]
+    ]);
 }
 
 %pr::gui = ( # gui bells and whistles
@@ -689,7 +768,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 => [
@@ -697,7 +776,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 => [
@@ -705,7 +784,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 => [
@@ -713,7 +792,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 => [
@@ -721,7 +800,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 => [
@@ -729,7 +808,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 => [
@@ -745,7 +824,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 => [
@@ -820,6 +899,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},
@@ -831,59 +918,44 @@ sub new_other_page() {
 );
 
 sub new_gui_page() {
-    my $gf = Gtk2::VBox->new (FALSE, 5);
-    $gf->set_border_width (PAGE_SPC);
-
-    my $cb_dot_lines = new_check_button_for (\%pr::gui, 'dot_lines', \%HPVALUE);
-    my $cb_toolbar_d = new_check_button_for (\%pr::gui, 'toolbar_d', \%HPVALUE);
-    my $tb_zero_char = new_text_box_for_nchar (\%pr::gui, 'zero_char', \%HPVALUE);
-
-    $gf->pack_start (new_subpage_frame (
-                         new_vbox_pack (
-                             new_check_button_for (\%pr::gui, 'strip_all', \%HPVALUE),
-                             new_check_button_for (\%pr::gui, 'strip_sum', \%HPVALUE),
-                             new_text_box_for_int (\%pr::gui, 'strip_off', \%HPVALUE)),
-                         $xl::s{stripes_frame}, 'not-packed'),
-                     FALSE, FALSE, FRAME_SPC);
-    $gf->pack_start (new_subpage_frame (
-                         new_vbox_pack (
-                             new_check_button_for (\%pr::gui, 'b_unread', \%HPVALUE),
-                             new_check_button_for (\%pr::gui, 'swp_from', \%HPVALUE),
-                             new_check_button_for (\%pr::gui, 'two_linev', \%HPVALUE),
-                             new_check_button_for (\%pr::gui, 'next_del', \%HPVALUE),
-                             new_selection_box_for (\%pr::gui, 'from_show', \%HPVALUE)),
-                         $xl::s{mlist_frame}, 'not-packed'),
-                     FALSE, FALSE, FRAME_SPC);
-    $gf->pack_start (new_subpage_frame (
-                         new_hbox_pack (
-                             new_check_button_for (\%pr::gui, 'cursor_v', \%HPVALUE),
-                             new_check_button_for (\%pr::gui, 'mview_date', \%HPVALUE)),
-                         $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)),
-                         $xl::s{compo_frame}, 'not-packed'),
-                     FALSE, FALSE, FRAME_SPC);
-    $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
-    $gf->pack_start ($cb_toolbar_d, FALSE, FALSE, 0);
-    $gf->pack_start (new_subpage_frame (
-                         new_vbox_pack (
-                             new_check_button_for (\%pr::gui, 'h_scroll', \%HPVALUE),
-                             new_selection_box_for (\%pr::gui, 'v_scroll', \%HPVALUE)),
-                         $xl::s{sbar_frame}, 'not-packed'),
-                     FALSE, FALSE, FRAME_SPC);
-    $gf->pack_start ($tb_zero_char, FALSE, FALSE, 0);
-
-    return $gf;
+    return new_grid_pack (2, 24, [
+        [ _('Coloured stripes') ],
+        [ new_check_button_for (\%pr::gui, 'strip_all', \%HPVALUE),
+            new_check_button_for (\%pr::gui, 'strip_sum', \%HPVALUE) ],
+        [ new_text_box_for_int (\%pr::gui, 'strip_off', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Message List') ],
+        [ new_check_button_for (\%pr::gui, 'b_unread', \%HPVALUE),
+            new_check_button_for (\%pr::gui, 'swp_from', \%HPVALUE) ],
+        [ new_check_button_for (\%pr::gui, 'two_linev', \%HPVALUE),
+            new_check_button_for (\%pr::gui, 'next_del', \%HPVALUE) ],
+        [ new_selection_box_for (\%pr::gui, 'from_show', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Message View') ],
+        [ new_check_button_for (\%pr::gui, 'cursor_v', \%HPVALUE),
+            new_check_button_for (\%pr::gui, 'mview_date', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Compose window') ],
+        [ 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) ],
+        [ '--' ],
+        [ _('Scroll bars') ],
+        [ new_check_button_for (\%pr::gui, 'h_scroll', \%HPVALUE) ],
+        [ new_selection_box_for (\%pr::gui, 'v_scroll', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Other') ],
+        [ new_check_button_for (\%pr::gui, 'dot_lines', \%HPVALUE),
+            new_check_button_for (\%pr::gui, 'toolbar_d', \%HPVALUE) ],
+        [ new_text_box_for_nchar (\%pr::gui, 'zero_char', \%HPVALUE) ]
+    ]);
 }
 
 %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',
@@ -923,7 +995,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',
@@ -931,7 +1003,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',
@@ -950,7 +1022,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 => [
@@ -1001,47 +1073,44 @@ sub new_gui_page() {
         '3.9.3.18',
         '0',
     ],
+    rewrite_ff => [
+        'rewrite_first_from',
+        $xl::s{l_beh_rewrite_ff},
+        $xl::s{h_beh_rewrite_ff},
+        'bool',
+        '3.14.0.94',
+        '0',
+    ],
 );
 
 sub new_behaviour_page() {
-    my $bf = Gtk2::VBox->new (FALSE, 5);
-    $bf->set_border_width (PAGE_SPC);
-
-    my $tb_up_step = new_text_box_for_int (\%pr::beh, 'up_step', \%HPVALUE);
-    my $tb_thread_a = new_text_box_for_int (\%pr::beh, 'thread_a', \%HPVALUE);
-
-    $bf->pack_start (new_subpage_frame (
-                         new_vbox_pack (
-                             new_text_box_for_int (\%pr::beh, 'hover_t', \%HPVALUE),
-                             new_check_button_for (\%pr::beh, 'warn_dnd', \%HPVALUE)),
-                         $xl::s{dnd_frame}, 'not-packed'),
-                     FALSE, FALSE, FRAME_SPC);
-    $bf->pack_start (new_subpage_frame (
-                         new_hbox_pack (
-                             new_check_button_for (\%pr::beh, 'skip_ssl', \%HPVALUE),
-                             new_check_button_for (\%pr::beh, 'unsafe_ssl', \%HPVALUE)),
-                         $xl::s{ssl_frame}, 'not-packed'),
-                     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 (new_subpage_frame (
-                         new_vbox_pack (
-                             new_check_button_for (\%pr::beh, 'flowed', \%HPVALUE),
-                             new_check_button_for (\%pr::beh, 'parts_rw', \%HPVALUE),
-                             new_check_button_for (\%pr::beh, 'use_utf8', \%HPVALUE),
-                             new_check_button_for (\%pr::beh, 'dangerous', \%HPVALUE),
-                             new_check_button_for (\%pr::beh, 'out_ascii', \%HPVALUE),
-                             new_check_button_for (\%pr::beh, 'pp_unsel', \%HPVALUE),
-                             new_check_button_for (\%pr::beh, 'inline_at', \%HPVALUE)),
-                         $xl::s{msgs_frame}, 'not-packed'),
-                     FALSE, FALSE, FRAME_SPC);
-    $bf->pack_start (new_subpage_frame (
-                         new_vbox_pack (
-                             new_check_button_for (\%pr::beh, 'addr_swc', \%HPVALUE),
-                             new_check_button_for (\%pr::beh, 'fold_swc', \%HPVALUE)),
-                         $xl::s{swc_frame}, 'not-packed'),
-                     FALSE, FALSE, FRAME_SPC);
-    return $bf;
+    return new_grid_pack (2, 20, [
+        [ _('Drag \'n\' drop') ],
+        [ new_text_box_for_int (\%pr::beh, 'hover_t', \%HPVALUE) ],
+        [ new_check_button_for (\%pr::beh, 'warn_dnd', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Secure Sockets Layer') ],
+        [ new_check_button_for (\%pr::beh, 'skip_ssl', \%HPVALUE),
+            new_check_button_for (\%pr::beh, 'unsafe_ssl', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Messages') ],
+        [ new_check_button_for (\%pr::beh, 'flowed', \%HPVALUE),
+            new_check_button_for (\%pr::beh, 'out_ascii', \%HPVALUE) ],
+        [ new_check_button_for (\%pr::beh, 'parts_rw', \%HPVALUE),
+            new_check_button_for (\%pr::beh, 'pp_unsel', \%HPVALUE) ],
+        [ new_check_button_for (\%pr::beh, 'use_utf8', \%HPVALUE),
+            new_check_button_for (\%pr::beh, 'inline_at', \%HPVALUE) ],
+        [ new_check_button_for (\%pr::beh, 'dangerous', \%HPVALUE),
+            new_check_button_for (\%pr::beh, 'rewrite_ff', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Completion') ],
+        [ new_check_button_for (\%pr::beh, 'addr_swc', \%HPVALUE) ],
+        [ new_check_button_for (\%pr::beh, 'fold_swc', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Other') ],
+        [ new_text_box_for_int (\%pr::beh, 'up_step', \%HPVALUE) ],
+        [ new_text_box_for_int (\%pr::beh, 'thread_a', \%HPVALUE) ]
+    ]);
 }
 
 %pr::col = ( # a variety of colours
@@ -1117,29 +1186,101 @@ 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 => [
+        'qs_error_bgcolor',
+        $xl::s{l_col_qs_error_bg},
+        $xl::s{h_col_qs_error_bg},
+        'color',
+        '3.14.1.31',
+        '#ff7070',
+    ],
+    qs_error_text => [
+        'qs_error_color',
+        $xl::s{l_col_qs_error_text},
+        $xl::s{h_col_qs_error_text},
+        '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)),
-                   $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)),
-                   $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')
-           );
+    return new_grid_pack (3, 18, [
+        [ _('Message View') ],
+        [ new_color_button_for (\%pr::col, 'emphasis', \%HPVALUE) ],
+        [ new_color_button_for (\%pr::col, 'tags_text', \%HPVALUE) ,
+            new_color_button_for (\%pr::col, 'tags_bg', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Log window') ],
+        [ 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_warn', \%HPVALUE) ,
+            new_color_button_for (\%pr::col, 'log_out', \%HPVALUE) ],
+        [ new_color_button_for (\%pr::col, 'log_msg', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Viewing patches') ],
+        [ 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) ],
+        [ '--' ],
+        [ _('Compose window') ],
+        [ new_color_button_for (\%pr::col, 'default_header_text', \%HPVALUE) ,
+            new_color_button_for (\%pr::col, 'default_header_bg', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Quick search') ],
+        [ new_color_button_for (\%pr::col, 'qs_active_text', \%HPVALUE) ,
+            new_color_button_for (\%pr::col, 'qs_active_bg', \%HPVALUE) ],
+        [ new_color_button_for (\%pr::col, 'qs_error_text', \%HPVALUE) ,
+            new_color_button_for (\%pr::col, 'qs_error_bg', \%HPVALUE) ]
+    ]);
 }
 
 %pr::win = ( # tweak window positions and/or sizes
@@ -1543,6 +1684,22 @@ sub new_colours_page() {
         '0.0.0',
         '-1',
     ],
+    acio_w => [
+        'actionsiodialog_width',
+        $xl::s{l_win_w},
+        $xl::s{h_win_w},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '0.0.0',
+        '582',
+    ],
+    acio_h => [
+        'actionsiodialog_height',
+        $xl::s{l_win_h},
+        $xl::s{h_win_h},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '0.0.0',
+        '310',
+    ],
     tags_w => [
         'tagswin_width',
         $xl::s{l_win_w},
@@ -1610,200 +1767,181 @@ sub new_colours_page() {
 );
 
 sub new_winpos_subpage_main() {
-    return new_subpage_frame (
-               new_vbox_pack_compact (
-                   new_vbox_pack (
-                       new_text_box_for_int (\%pr::win, 'main_x', \%HPVALUE),
-                       new_text_box_for_int (\%pr::win, 'main_y', \%HPVALUE)),
-                   new_hbox_pack (
-                       new_text_box_for_int (\%pr::win, 'main_w', \%HPVALUE),
-                       new_text_box_for_int (\%pr::win, 'main_h', \%HPVALUE)),
-                   new_vbox_pack (
-                       new_check_button_for (\%pr::win, 'main_fs', \%HPVALUE),
-                       new_check_button_for (\%pr::win, 'main_mx', \%HPVALUE))),
-               _('Main window')
-           );
+    return new_grid_pack (3, 7, [
+        [ _('Main window'), undef ],
+        [ new_text_box_for_int (\%pr::win, 'main_x', \%HPVALUE), undef ],
+        [ new_text_box_for_int (\%pr::win, 'main_y', \%HPVALUE), undef ],
+        [ new_text_box_for_int (\%pr::win, 'main_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'main_h', \%HPVALUE) ],
+        [ new_check_button_for (\%pr::win, 'main_fs', \%HPVALUE), undef ],
+        [ new_check_button_for (\%pr::win, 'main_mx', \%HPVALUE), undef ]
+    ]);
 }
 
 sub new_winpos_subpage_msgs() {
-    return new_subpage_frame (
-               new_vbox_pack_compact (
-                   new_vbox_pack (
-                       new_text_box_for_int (\%pr::win, 'msgs_x', \%HPVALUE),
-                       new_text_box_for_int (\%pr::win, 'msgs_y', \%HPVALUE)),
-                   new_hbox_pack (
-                       new_text_box_for_int (\%pr::win, 'msgs_w', \%HPVALUE),
-                       new_text_box_for_int (\%pr::win, 'msgs_h', \%HPVALUE))),
-               _('Message window')
-           );
+    return new_grid_pack (3, 4, [
+        [ _('Message window') ],
+        [ new_text_box_for_int (\%pr::win, 'msgs_x', \%HPVALUE) ],
+        [ new_text_box_for_int (\%pr::win, 'msgs_y', \%HPVALUE) ],
+        [ new_text_box_for_int (\%pr::win, 'msgs_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'msgs_h', \%HPVALUE) ]
+    ]);
 }
 
 sub new_winpos_subpage_sendrecv() {
-    return new_vbox_pack (
-               new_subpage_frame (
-                   new_hbox_pack (
-                       new_text_box_for_int (\%pr::win, 'send_w', \%HPVALUE),
-                       new_text_box_for_int (\%pr::win, 'send_h', \%HPVALUE)),
-                   _('Send window'), 'not-packed'),
-               new_subpage_frame (
-                   new_hbox_pack (
-                       new_text_box_for_int (\%pr::win, 'recv_w', \%HPVALUE),
-                       new_text_box_for_int (\%pr::win, 'recv_h', \%HPVALUE)),
-                   _('Receive window'), 'not-packed')
-           );
+    return new_grid_pack (3, 5, [
+        [ _('Send window') ],
+        [ new_text_box_for_int (\%pr::win, 'send_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'send_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Receive window') ],
+        [ new_text_box_for_int (\%pr::win, 'recv_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'recv_h', \%HPVALUE) ]
+    ]);
 }
 
 sub new_winpos_subpage_fold() {
-    return new_vbox_pack (
-               new_subpage_frame (
-                   new_vbox_pack_compact (
-                       new_vbox_pack (
-                            new_text_box_for_int (\%pr::win, 'fold_x', \%HPVALUE),
-                            new_text_box_for_int (\%pr::win, 'fold_y', \%HPVALUE)),
-                       new_hbox_pack (
-                           new_text_box_for_int (\%pr::win, 'fold_w', \%HPVALUE),
-                           new_text_box_for_int (\%pr::win, 'fold_h', \%HPVALUE))),
-                   _('Folder window'), 'not-packed'),
-               new_subpage_frame (
-                   new_hbox_pack (
-                       new_text_box_for_int (\%pr::win, 'fsel_w', \%HPVALUE),
-                       new_text_box_for_int (\%pr::win, 'fsel_h', \%HPVALUE)),
-                   _('Folder selection window'), 'not-packed')
-           );
+    return new_grid_pack (3, 7, [
+        [ _('Folder window') ],
+        [ new_text_box_for_int (\%pr::win, 'fold_x', \%HPVALUE) ],
+        [ new_text_box_for_int (\%pr::win, 'fold_y', \%HPVALUE) ],
+        [ new_text_box_for_int (\%pr::win, 'fold_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'fold_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Folder selection window') ],
+        [ new_text_box_for_int (\%pr::win, 'fsel_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'fsel_h', \%HPVALUE) ]
+    ]);
 }
 
 sub new_winpos_subpage_addrbook() {
-    return new_vbox_pack (
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'addr_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'addr_h', \%HPVALUE)),
-                     _('Addressbook main window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'adep_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'adep_h', \%HPVALUE)),
-                     _('Edit person window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'adeg_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'adeg_h', \%HPVALUE)),
-                     _('Edit group window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'adda_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'adda_h', \%HPVALUE)),
-                     _('Add address window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'addf_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'addf_h', \%HPVALUE)),
-                     _('Folder select window'), 'not-packed')
-           );
+    return new_grid_pack (3, 14, [
+        [ _('Addressbook main window') ],
+        [ new_text_box_for_int (\%pr::win, 'addr_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'addr_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Edit person window') ],
+        [ new_text_box_for_int (\%pr::win, 'adep_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'adep_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Edit group window') ],
+        [ new_text_box_for_int (\%pr::win, 'adeg_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'adeg_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Add address window') ],
+        [ new_text_box_for_int (\%pr::win, 'adda_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'adda_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Folder select window') ],
+        [ new_text_box_for_int (\%pr::win, 'addf_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'addf_h', \%HPVALUE) ]
+    ]);
 }
 
 sub new_winpos_subpage_accounts() {
-    return new_vbox_pack (
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'acco_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'acco_h', \%HPVALUE)),
-                     _('Accounts window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'acce_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'acce_h', \%HPVALUE)),
-                     _('Edit account window'), 'not-packed')
-           );
+    return new_grid_pack (3, 5, [
+        [ _('Accounts window') ],
+        [ new_text_box_for_int (\%pr::win, 'acco_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'acco_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Edit account window') ],
+        [ new_text_box_for_int (\%pr::win, 'acce_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'acce_h', \%HPVALUE) ]
+    ]);
 }
 
 sub new_winpos_subpage_filtering() {
-    return new_vbox_pack (
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'filt_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'filt_h', \%HPVALUE)),
-                     _('Filtering window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'fila_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'fila_h', \%HPVALUE)),
-                     _('Filtering actions window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'fild_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'fild_h', \%HPVALUE)),
-                     _('Filtering debug window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'matc_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'matc_h', \%HPVALUE)),
-                     _('Matcher window'), 'not-packed')
-           );
+    return new_grid_pack (3, 11, [
+        [ _('Filtering window') ],
+        [ new_text_box_for_int (\%pr::win, 'filt_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'filt_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Filtering actions window') ],
+        [ new_text_box_for_int (\%pr::win, 'fila_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'fila_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Filtering debug window') ],
+        [ new_text_box_for_int (\%pr::win, 'fild_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'fild_h', \%HPVALUE) ],
+        [ '--' ],
+        [ ('Matcher window') ],
+        [ new_text_box_for_int (\%pr::win, 'matc_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'matc_h', \%HPVALUE) ]
+
+    ]);
+}
+
+sub new_winpos_subpage_useractions() {
+    return new_grid_pack (3, 5, [
+        [ _('User Actions prefs window') ],
+        [ new_text_box_for_int (\%pr::win, 'acti_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'acti_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('User Actions I/O window') ],
+        [ new_text_box_for_int (\%pr::win, 'acio_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'acio_h', \%HPVALUE) ]
+    ]);
 }
 
 sub new_winpos_subpage_prefs() {
-    return new_vbox_pack (
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'pref_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'pref_h', \%HPVALUE)),
-                     _('Preferences window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'temp_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'temp_h', \%HPVALUE)),
-                     _('Templates window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'acti_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'acti_h', \%HPVALUE)),
-                     _('Actions window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'tags_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'tags_h', \%HPVALUE)),
-                     _('Tags window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'plug_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'plug_h', \%HPVALUE)),
-                     _('Plugins window'), 'not-packed')
-           );
+    return new_grid_pack (3, 11, [
+        [ _('Preferences window') ],
+        [ new_text_box_for_int (\%pr::win, 'pref_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'pref_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Templates window') ],
+        [ new_text_box_for_int (\%pr::win, 'temp_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'temp_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Tags window') ],
+        [ new_text_box_for_int (\%pr::win, 'tags_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'tags_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Plugins window') ],
+        [ new_text_box_for_int (\%pr::win, 'plug_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'plug_h', \%HPVALUE) ]
+    ]);
 }
 
 sub new_winpos_subpage_misc() {
-    return new_vbox_pack (
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'logw_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'logw_h', \%HPVALUE)),
-                     _('Log window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'prin_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'prin_h', \%HPVALUE)),
-                     _('Print preview window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'sour_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'sour_h', \%HPVALUE)),
-                     _('View source window'), 'not-packed')
-           );
+    return new_grid_pack (3, 8, [
+        [ _('Log window') ],
+        [ new_text_box_for_int (\%pr::win, 'logw_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'logw_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Print preview window') ],
+        [ new_text_box_for_int (\%pr::win, 'prin_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'prin_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('View source window') ],
+        [ new_text_box_for_int (\%pr::win, 'sour_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'sour_h', \%HPVALUE) ]
+    ]);
 }
 
 sub new_winpos_page() {
-    my $winbook = Gtk2::Notebook->new;
+    my $winbook = Gtk3::Notebook->new;
     $winbook->set_tab_pos ('right');
-    $winbook->append_page (new_winpos_subpage_main, _('Main'));
-    $winbook->append_page (new_winpos_subpage_msgs, _('Message'));
-    $winbook->append_page (new_winpos_subpage_sendrecv, _('Send/Receive'));
-    $winbook->append_page (new_winpos_subpage_fold, _('Folder'));
-    $winbook->append_page (new_winpos_subpage_addrbook, _('Addressbook'));
-    $winbook->append_page (new_winpos_subpage_accounts, _('Accounts'));
-    $winbook->append_page (new_winpos_subpage_filtering, _('Filtering'));
-    $winbook->append_page (new_winpos_subpage_prefs, _('Preferences'));
-    $winbook->append_page (new_winpos_subpage_misc, _('Other'));
+    $winbook->append_page (new_winpos_subpage_main,
+        Gtk3::Label->new (_('Main')));
+    $winbook->append_page (new_winpos_subpage_msgs,
+        Gtk3::Label->new (_('Message')));
+    $winbook->append_page (new_winpos_subpage_sendrecv,
+        Gtk3::Label->new (_('Send/Receive')));
+    $winbook->append_page (new_winpos_subpage_fold,
+        Gtk3::Label->new (_('Folder')));
+    $winbook->append_page (new_winpos_subpage_addrbook,
+        Gtk3::Label->new (_('Addressbook')));
+    $winbook->append_page (new_winpos_subpage_accounts,
+        Gtk3::Label->new (_('Accounts')));
+    $winbook->append_page (new_winpos_subpage_filtering,
+        Gtk3::Label->new (_('Filtering')));
+    $winbook->append_page (new_winpos_subpage_useractions,
+        Gtk3::Label->new (_('User Actions')));
+    $winbook->append_page (new_winpos_subpage_prefs,
+        Gtk3::Label->new (_('Preferences')));
+    $winbook->append_page (new_winpos_subpage_misc,
+        Gtk3::Label->new (_('Other')));
     return $winbook;
 }
 
@@ -1828,22 +1966,28 @@ sub new_winpos_page() {
 
 sub new_account_subpage($) {
     my ($akey) = @_;
-    return new_vbox_pack (
-                new_subpage_frame (
-                    new_vbox_pack (
-                        new_check_button_for (\%pr::acc, 'tls_set', $ACHPVALUE{$akey}),
-                        new_text_box_for_nchar (\%pr::acc, 'tls_pri', $ACHPVALUE{$akey})),
-                    _('GnuTLS priority'), 'not-packed')
-           );
+    return new_grid_pack (1, 3, [
+        [ _('GnuTLS priority') ],
+        [ new_check_button_for (\%pr::acc, 'tls_set', $ACHPVALUE{$akey}) ],
+        [ new_text_box_for_nchar (\%pr::acc, 'tls_pri', $ACHPVALUE{$akey}) ]
+    ]);
 }
 
 sub new_accounts_page() {
-    my $accbook = Gtk2::Notebook->new;
+    my $accbook = Gtk3::Notebook->new;
     $accbook->set_tab_pos ('right');
-    foreach (keys %ACPREFS) {
+    my @akeys = sort {
+        $ACPREFS{$a}{'account_name'} cmp $ACPREFS{$b}{'account_name'}
+    } keys %ACPREFS;
+    foreach (@akeys) {
         my $name = $ACPREFS{$_}{'account_name'};
-        $accbook->append_page (new_account_subpage ($_), $name);
+        my $isdef = ($ACPREFS{$_}{'is_default'} eq '1');
+        my $page = new_account_subpage ($_);
+        my $label = new_label ($isdef? '<u>' . $name . '</u>': $name);
+        $label->set_use_markup (TRUE);
+        $accbook->append_page ($page, $label);
     }
+    $accbook->set_scrollable (TRUE);
     return $accbook;
 }
 
@@ -1856,7 +2000,7 @@ sub new_accounts_page() {
         'int,0,3000', # 0 pixels - 3000 pixels
         '3.9.0.74',
         '-1',
-        undef,
+        'AttRemover',
     ],
     arm_winh => [
         'win_height',
@@ -1865,7 +2009,36 @@ sub new_accounts_page() {
         'int,0,3000', # 0 pixels - 3000 pixels
         '3.9.0.74',
         '-1',
-        undef,
+        'AttRemover',
+    ],
+    # GPG
+    gpg_alimit => [
+        'autocompletion_limit',
+        $xl::s{l_plu_gpg_alimit},
+        $xl::s{h_plu_gpg_alimit},
+        'int,0,100',
+        '3.12.0.75',
+        '0',
+        'GPG',
+    ],
+    # managesieve
+    msv_winw => [
+        'manager_win_width',
+        $xl::s{l_win_w},
+        $xl::s{h_win_w},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.11.1.210',
+        '-1',
+        'ManageSieve',
+    ],
+    msv_winh => [
+        'manager_win_height',
+        $xl::s{l_win_h},
+        $xl::s{h_win_h},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.11.1.210',
+        '-1',
+        'ManageSieve',
     ],
     # libravatar
     lav_burl => [
@@ -1875,7 +2048,7 @@ sub new_accounts_page() {
         'char,0,1024,32',
         '3.9.3.32',
         'http://cdn.libravatar.org/avatar',
-        undef,
+        'Libravatar',
     ],
     # perl
     prl_flvb => [
@@ -1885,49 +2058,254 @@ sub new_accounts_page() {
         '0=l_plu_prl_none;1=l_plu_prl_manual;2=l_plu_prl_action;3=l_plu_prl_match',
         '3.9.0.75',
         '2',
-        undef,
+        'PerlPlugin',
     ],
 );
 
+sub new_plugins_page() {
+    my %widget = (
+        'AttRemover' => [
+            new_text_box_for_int (\%pr::plu, 'arm_winw', $PLHPVALUE{'AttRemover'}),
+            new_text_box_for_int (\%pr::plu, 'arm_winh', $PLHPVALUE{'AttRemover'})
+        ],
+        'GPG' => [
+            new_text_box_for_int (\%pr::plu, 'gpg_alimit', $PLHPVALUE{'GPG'})
+        ],
+        'ManageSieve' => [
+            new_text_box_for_int (\%pr::plu, 'msv_winw', $PLHPVALUE{'ManageSieve'}),
+            new_text_box_for_int (\%pr::plu, 'msv_winh', $PLHPVALUE{'ManageSieve'})
+        ],
+        'Libravatar' => [
+            new_text_box_for_nchar (\%pr::plu, 'lav_burl', $PLHPVALUE{'Libravatar'})
+        ],
+        'PerlPlugin' => [
+            new_selection_box_for (\%pr::plu, 'prl_flvb', $PLHPVALUE{'PerlPlugin'})
+        ]
+    );
+    foreach my $pk (@PLUGINS) {
+        foreach my $wg (@{$widget{$pk}}) {
+            $wg->set_sensitive (defined $PLHPVALUE{$pk});
+        }
+    }
+    return new_grid_pack (3, 14, [
+        [ _('Attachment remover') ], $widget{'AttRemover'}, [ '--' ],
+        [ _('GPG') ], $widget{'GPG'}, [ '--' ],
+        [ _('Sieve manager') ], $widget{'ManageSieve'}, [ '--' ],
+        [ _('Libravatar') ], $widget{'Libravatar'}, [ '--' ],
+        [ _('Perl') ], $widget{'PerlPlugin'}
+    ]);
+}
+
+use constant {
+    C_LABEL => 0,
+    C_HOTKEY => 1,
+    C_GROUP => 2,
+    C_ACCEL => 3,
+    C_ODDITY => 4,
+};
+
+sub new_hotkeys_list_label {
+    my $renderer = Gtk3::CellRendererText->new ();
+    $renderer->set_property('alignment' => 'left');
+    $renderer->set_property('editable' => FALSE);
+    return $renderer;
+}
+
+sub new_hotkeys_list_hotkey {
+    my $renderer = Gtk3::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 = Gtk3::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 row_background_color {
+    my ($column, $isodd) = @_;
+    my $treeview = $column->get_tree_view;
+    my $stylectx = $treeview->get_style_context;
+    return $isodd
+        ? $stylectx->get_background_color ('normal')
+        : $stylectx->get_background_color ('insensitive');
+}
+
+sub new_hotkeys_list {
+    my ($gkey, $group) = @_;
+    my $store = Gtk3::ListStore->new(
+        qw/Glib::String Glib::String Glib::String Glib::String Glib::Boolean/);
+    my $even = TRUE;
+    foreach my $akey (sort keys %$group) {
+        my $iter = $store->append ();
+        my $hotkey = $group->{$akey}->{'key'};
+        my $label = $akey;
+        $label =~ s/[<>]//g; # <rrsyl> and <IMAPFolder> !?
+        $store->set ($iter, C_LABEL, $label, C_HOTKEY, $hotkey,
+            C_GROUP, $gkey, C_ACCEL, $akey, C_ODDITY, $even);
+        $even = not $even;
+    }
+    my $treeview = Gtk3::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 (
+                'markup' => '<span size="smaller">'
+                            . $model->get_value ($iter, C_LABEL)
+                            . '</span>');
+            my $bgcol = row_background_color (
+                $col, $model->get_value ($iter, C_ODDITY));
+            $renderer->set_property ('cell-background-rgba' => $bgcol);
+        }
+    );
+    # 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) = Gtk3::accelerator_parse ($hkey);
+            $renderer->set_property ('accel-key' => $acckey);
+            $renderer->set_property ('accel-mods' => $accmod);
+            my $bgcol = row_background_color (
+                $col, $model->get_value ($iter, C_ODDITY));
+            $renderer->set_property ('cell-background-rgba' => $bgcol);
+        }
+    );
+    # 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 = Gtk3::ScrolledWindow->new ();
+    my $vbox = Gtk3::VBox->new (FALSE, 5);
+    foreach my $gkey (sort keys %$HOTKEYS) {
+        my $group = $HOTKEYS->{$gkey};
+        # group title
+        my $glabel = Gtk3::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 $v = get_toolkit_versions ();
+    my $cfgv = $CONFIGDATA->{'Common'}{'config_version'};
+    $cfgv //= '';
+    return new_grid_pack (4, 11, [
+        [ _('Library versions') ],
+        [ new_label ('Perl-GLib'), new_title ($v->{'glib'}) ],
+        [ new_label (_('GLib runtime')), new_title ($v->{'glib-r'}) ],
+        [ new_label (_('GLib built')), new_title ($v->{'glib-b'}) ],
+        [ new_label ('Perl-GTK3'), new_title ($v->{'gtk'}) ],
+        [ new_label (_('GTK3 runtime')), new_title ($v->{'gtk-r'}) ],
+        [ new_label (_('GTK3 built')), new_title ($v->{'gtk-b'}) ],
+        [ '--' ],
+        [ _('Claws Mail versions') ],
+        [ new_label (_('Binary')), new_title ($CLAWSV) ],
+        [ new_label (_('Configuration')), new_title ($cfgv) ]
+    ]);
+}
+
 # version info
+sub get_toolkit_versions {
+    my %versions = ();
+    $versions{'glib'} = $Glib::VERSION;
+    # version info stuff appeared in 1.040
+    if ($Glib::VERSION >= 1.040) {
+        $versions{'glib-b'} = join('.', Glib->GET_VERSION_INFO);
+        $versions{'glib-r'} = join('.',
+            &Glib::major_version, &Glib::minor_version, &Glib::micro_version);
+    }
+    $versions{'gtk'} = $Gtk3::VERSION;
+    if ($Gtk3::VERSION >= 0.034) {
+        $versions{'gtk-b'} = &Gtk3::GET_VERSION_INFO
+    } else {
+        $versions{'gtk-b'} = _('Not available')
+    }
+    $versions{'gtk-r'} = join('.',
+        &Gtk3::get_major_version, &Gtk3::get_minor_version, &Gtk3::get_micro_version);
+    return \%versions;
+}
+
 sub print_version() {
     print $xl::s{about_title} . "\n";
     print $xl::s{about_version} . " $VERSION\n";
-    print "Perl-GLib " . $Glib::VERSION;
-    # version info stuff appeared in 1.040
-    if ($Glib::VERSION >= 1.040) {
-        print _(", built for ") . join(".", Glib->GET_VERSION_INFO)
-              . _(", running with ") . join(".", &Glib::major_version,
-              &Glib::minor_version, &Glib::micro_version);
+    my $v = get_toolkit_versions ();
+    if ($v->{'glib-b'}) {
+        print _("Perl-GLib version {glibv}, built for {glibb}, running with {glibr}.",
+                glibv => $v->{'glib'},
+                glibb => $v->{'glib-b'},
+                glibr => $v->{'glib-r'});
+    } else {
+        print _("Perl-GLib version {glibv}.", glibv => $v->{'glib'});
     }
     print "\n";
-    print "Perl-GTK2 " . $Gtk2::VERSION;
-    if ($Gtk2::VERSION >= 1.040) {
-        print _(", built for ") . join(".", Gtk2->GET_VERSION_INFO)
-              . _(", running with ") . join(".", &Gtk2::major_version,
-              &Gtk2::minor_version, &Gtk2::micro_version);
+    if ($v->{'gtk-b'}) {
+        print _("Perl-GTK3 version {gtkv}, built for {gtkb}, running with {gtkr}.",
+                gtkv => $v->{'gtk'},
+                gtkb => $v->{'gtk-b'},
+                gtkr => $v->{'gtk-r'});
+    } else {
+        print _("Perl-GTK3 version {gtkv}.", gtkv => $v->{'gtk'});
     }
     print "\n";
     my $clawsver = ($CLAWSV eq "") ?
-                _("was not found!") :
-                (_("returned version ") . $CLAWSV);
-    print "Claws Mail " . $clawsver . "\n";
+                _("Claws Mail was not found!") :
+                _("Claws Mail returned version {cmv}.", cmv => $CLAWSV);
+    print $clawsver . "\n";
 }
 
 # 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 $line;
-    print _("Syntax:\n");
-    print _("  clawsker [options]\n");
-    print _("Options:\n");
-    print _("  -h|--help                        Prints this help screen.\n");
-    print _("  -v|--version                     Prints version infos.\n");
-    print _("  -b|--verbose                     More messages on standard output.\n");
-    print _("  -a|--alternate-config-dir <dir>  Uses <dir> as Claws Mail config dir.\n");
-    print _("  -c|--clawsrc <file>              Uses <file> as full resource name.\n");
+    my $line = '-' x length ($xl::s{about_title});
+    say $line;
+    say $xl::s{about_title};
+    say $line;
+    my @help = (
+        _("Syntax:"),
+        _("  clawsker [options]"),
+        _("Options:"),
+        _("  -a|--alternate-config-dir <dir>  Uses <dir> as Claws Mail configuration."),
+        _("  -b|--verbose                     More messages on standard output."),
+        _("  -c|--clawsrc <file>              Uses <file> as full resource name."),
+        _("  -h|--help                        Prints this help screen and exits."),
+        _("  -r|--read-only                   Disables writing changes to disk."),
+        _("  -v|--version                     Prints version information and exits.")
+    );
+    foreach (@help) { say $_ }
 }
 
 sub parse_command_line {
@@ -1946,7 +2324,7 @@ sub parse_command_line {
     if ($@) {
         my $msg = _("Error in options: {msg}\n", msg => $@);
         if (defined $ENV{'DISPLAY'} and $ENV{'DISPLAY'} ne '') {
-            eval { Gtk2->init };
+            eval { Gtk3->init };
             error_dialog ($msg) unless $@;
         }
         die $msg;
@@ -1981,36 +2359,169 @@ sub opt_clawsrc {
 sub init_hidden_preferences {
     foreach my $hash (\%pr::beh, \%pr::col, \%pr::gui, \%pr::oth, \%pr::win) {
         foreach my $key (keys %$hash) {
-            $HPVALUE{${$hash}{$key}[NAME]} = $PREFS{${$hash}{$key}[NAME]};
+            $HPVALUE{${$hash}{$key}[NAME]}[VALUE] = $PREFS{${$hash}{$key}[NAME]};
+            $HPVALUE{${$hash}{$key}[NAME]}[IVALUE] = $PREFS{${$hash}{$key}[NAME]};
         }
     }
-    return TRUE;
-}
-
-sub init_ac_hidden_preferences {
     foreach my $akey (keys %ACPREFS) {
         foreach my $key (keys %pr::acc) {
             my $pname = $pr::acc{$key}[NAME];
-            $ACHPVALUE{$akey}{$pname} = $ACPREFS{$akey}{$pname};
+            $ACHPVALUE{$akey}{$pname}[VALUE] = $ACPREFS{$akey}{$pname};
+            $ACHPVALUE{$akey}{$pname}[IVALUE] = $ACPREFS{$akey}{$pname};
+        }
+    }
+    foreach my $key (keys %pr::plu) {
+        my $plugin = $pr::plu{$key}[PLUGIN];
+        my $pname = $pr::plu{$key}[NAME];
+        if (defined $PLPREFS{$plugin}) {
+            $PLHPVALUE{$plugin}{$pname}[VALUE] = $PLPREFS{$plugin}{$pname};
+            $PLHPVALUE{$plugin}{$pname}[IVALUE] = $PLPREFS{$plugin}{$pname};
         }
     }
     return TRUE;
 }
 
+# generic load/save resource files
+sub load_resource {
+    my $rc = shift;
+    my %data = ();
+    my %meta = ();
+    my $line = 0;
+    open (RCF, '<:encoding(utf8)', $rc)
+        or die _("Error: opening '{file}' for reading", file => $rc) . ": $!\n";
+    my $section = '_'; # default unnamed section
+    while (<RCF>) {
+        chomp;
+        ++$line;
+        next if (/^\s*$/);
+        if (/^\[([^\]]+)\]$/) { # new section
+            $section = $1;
+            die _("Error: duplicate section '{sect}' in resource file '{file}'\n",
+                sect => $section, file => $rc) if ($data{$section});
+            $data{$section} = {};
+            $meta{$section}{'#'} = $line;
+        }
+        elsif (/^([0-9a-z_]+)=(.*)$/) { # key=value
+            $data{$section}{$1} = $2;
+            $meta{$section}{$1} = $line;
+        }
+        elsif (/^(.*)$/) { # lone value
+            push (@{$data{$section}{'_'}}, $1);
+        }
+    }
+    close (RCF);
+    return (\%data, \%meta);
+}
+
+sub save_resource {
+    my ($rc, $data, $meta) = @_;
+    open (RCF, '>:utf8', $rc)
+        or die _("Error: opening '{file}' for writing", file => $rc) . ": $!\n";
+    my @sections = keys %$data;
+    if (defined $meta) {
+        @sections = sort {
+            $meta->{$a}{'#'} <=> $meta->{$b}{'#'}
+        } @sections
+    }
+    foreach my $section (@sections) {
+        say RCF "[$section]";
+        if (ref ($data->{$section}{'_'}) eq 'ARRAY') {
+            foreach my $val (@{$data->{$section}{'_'}}) {
+                say RCF $val;
+            }
+        } else {
+            my @keys = keys %{$data->{$section}};
+            if (defined $meta) {
+                @keys = sort {
+                    $meta->{$section}{$a} <=> $meta->{$section}{$b}
+                } @keys
+            }
+            foreach my $key (@keys) {
+                my $val = $data->{$section}{$key};
+                say RCF "$key=$val";
+            }
+        }
+        say RCF "";
+    }
+    close (RCF);
+}
+
+sub backup_resource {
+    my $rc = shift;
+    my $rcbak = "$rc.backup";
+    do {
+        my $emsg = _("Unable to create backup file '{name}'\n", name => $rcbak);
+        log_message ($emsg);
+        error_dialog ($emsg);
+        return FALSE;
+    } unless rename ($rc, $rcbak);
+    return TRUE;
+}
+
+# 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_preferences {
+sub load_rc_preferences {
     my $rc = get_rc_filename ();
     log_message ("Loading preferences from $rc\n");
     return FALSE unless check_rc_file ($rc);
-    return FALSE unless check_claws_not_running ();
-    open (RCF, "<$rc");
-    while (<RCF>) {
-        chomp;
-        if (/^([8a-z_]+)=(.*)$/) {
-            $PREFS{$1} = decode('UTF-8', $2);
+    ($CONFIGDATA, $CONFIGMETA) = load_resource ($rc);
+    foreach (keys %{$CONFIGDATA->{'Common'}}) {
+        $PREFS{$_} = $CONFIGDATA->{'Common'}{$_};
+    }
+    foreach my $plugin (@PLUGINS) {
+        if (defined $CONFIGDATA->{$plugin}) {
+            push (@AVPLUGINS, $plugin);
+            foreach (keys %{$CONFIGDATA->{$plugin}}) {
+                $PLPREFS{$plugin}{$_} = $CONFIGDATA->{$plugin}{$_};
+            }
         }
     }
-    close (RCF);
     return TRUE;
 }
 
@@ -2018,49 +2529,52 @@ sub load_ac_preferences {
     my $rc = get_ac_rc_filename ();
     log_message ("Loading account preferences from $rc\n");
     return FALSE unless check_rc_file ($rc);
-    return FALSE unless check_claws_not_running ();
-    open (RCF, "<$rc");
-    my $akey;
-    while (<RCF>) {
-        chomp;
-        if (/^\[Account: (\d+)\]$/) {
-          $akey = $1;
-          next;
-        }
-        if (/^([8a-z_]+)=(.*)$/) {
-            $ACPREFS{$akey}{$1} = decode('UTF-8', $2);
+    ($ACCOUNTDATA, $ACCOUNTMETA) = load_resource ($rc);
+    foreach my $asect (keys %$ACCOUNTDATA) {
+        if ($asect =~ /^Account: (\d+)$/) {
+            foreach (keys %{$ACCOUNTDATA->{$asect}}) {
+                $ACPREFS{$1}{$_} = $ACCOUNTDATA->{$asect}{$_};
+            }
         }
     }
-    close (RCF);
     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 ()
+        and load_hk_preferences ()
+    );
+}
+
 # save current preferences to disc
-sub save_preferences {
+sub save_rc_preferences {
     my $rc = get_rc_filename ();
     log_message ("Saving preferences to $rc\n");
     return FALSE unless check_rc_file ($rc);
     return FALSE unless check_claws_not_running ();
-    my $rcbak = "$rc.backup";
-    rename ($rc, $rcbak);
-    open (RCF, ">$rc");
-    open (RCB, "<$rcbak");
-    while (<RCB>) {
-        chomp;
-        if (/^([8a-z_]+)=(.*)$/) {
-            if (defined($HPVALUE{$1})) {
-                print RCF $1 . "=" . $HPVALUE{$1} . "\n";
-            }
-            else {
-                print RCF $_ . "\n";
-            }
+    return FALSE unless backup_resource ($rc);
+    foreach (keys %PREFS) {
+        if (defined $HPVALUE{$_}) {
+            $CONFIGDATA->{'Common'}{$_} = $HPVALUE{$_}[VALUE];
         }
-        else {
-            print RCF $_ . "\n";
+    }
+    foreach my $plugin (@AVPLUGINS) {
+        foreach (keys %{$CONFIGDATA->{$plugin}}) {
+            if (defined $PLHPVALUE{$plugin}{$_}) {
+                $CONFIGDATA->{$plugin}{$_} = $PLHPVALUE{$plugin}{$_}[VALUE];
+            }
         }
     }
-    close (RCB);
-    close (RCF);
+    save_resource ($rc, $CONFIGDATA, $CONFIGMETA);
     return TRUE;
 }
 
@@ -2069,45 +2583,60 @@ sub save_ac_preferences {
     log_message ("Saving account preferences to $rc\n");
     return FALSE unless check_rc_file ($rc);
     return FALSE unless check_claws_not_running ();
-    my $rcbak = "$rc.backup";
-    rename ($rc, $rcbak);
-    open (RCF, ">$rc");
-    open (RCB, "<$rcbak");
-    my $akey;
-    while (<RCB>) {
-        chomp;
-        if (/^\[Account: (\d+)\]$/) {
-          $akey = $1;
-          print RCF $_ . "\n";
-          next;
-        }
-        if (/^([8a-z_]+)=(.*)$/) {
-            if (defined($ACHPVALUE{$akey}{$1})) {
-                print RCF $1 . "=" . $ACHPVALUE{$akey}{$1} . "\n";
+    return FALSE unless backup_resource ($rc);
+    foreach my $asect (keys %$ACCOUNTDATA) {
+        if ($asect =~ /^Account: (\d+)$/) {
+            foreach (keys %{$ACCOUNTDATA->{$asect}}) {
+                if (defined $ACHPVALUE{$1}{$_}) {
+                    $ACCOUNTDATA->{$asect}{$_} = $ACHPVALUE{$1}{$_}[VALUE];
+                }
             }
-            else {
-                print RCF $_ . "\n";
-            }
-        }
-        else {
-            print RCF $_ . "\n";
         }
     }
-    close (RCB);
-    close (RCF);
+    save_resource ($rc, $ACCOUNTDATA, $ACCOUNTMETA);
     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 ();
+    return FALSE unless backup_resource ($rc);
+    save_menurc ($rc, $HOTKEYS);
+    return TRUE;
+}
+
+sub save_preferences {
+    my $result = save_rc_preferences ()
+        and save_ac_preferences ()
+        and save_hk_preferences ();
+    $MODIFIED = 0 if $result;
+    return $result;
+}
+
 # create notebook
 sub new_notebook {
-    my $nb = Gtk2::Notebook->new;
+    my $nb = Gtk3::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});
-    $nb->append_page (new_winpos_page (), $xl::s{tab_winpos});
-    $nb->append_page (new_accounts_page (), $xl::s{tab_accounts});
+    $nb->append_page (new_behaviour_page (),
+        Gtk3::Label->new ($xl::s{tab_behaviour}));
+    $nb->append_page (new_colours_page (),
+        Gtk3::Label->new ($xl::s{tab_colours}));
+    $nb->append_page (new_gui_page (),
+        Gtk3::Label->new ($xl::s{tab_gui}));
+    $nb->append_page (new_other_page (),
+        Gtk3::Label->new ($xl::s{tab_other}));
+    $nb->append_page (new_winpos_page (),
+        Gtk3::Label->new ($xl::s{tab_winpos}));
+    $nb->append_page (new_accounts_page (),
+        Gtk3::Label->new ($xl::s{tab_accounts}));
+    $nb->append_page (new_plugins_page (),
+        Gtk3::Label->new ($xl::s{tab_plugins}));
+    $nb->append_page (new_hotkeys_page (),
+        Gtk3::Label->new ($xl::s{tab_hotkeys}));
+    $nb->append_page (new_info_page (),
+        Gtk3::Label->new ($xl::s{tab_info}));
 
     return $nb;
 }
@@ -2115,56 +2644,84 @@ sub new_notebook {
 # 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 &lt;http://www.gnu.org/licenses/&gt;.";
-    my $year = "2007-2016";
-    my $holder = "Ricardo Mones &lt;ricardo\@mones.org&gt;";
-    my $url = "http://www.claws-mail.org/clawsker.php";
-
-    my $dialog = Gtk2::MessageDialog->new_with_markup ($parent,
-                    [qw/modal destroy-with-parent/],
-                    'info', 'close',
-                    "<span size=\"x-large\" weight=\"bold\">$title</span>\n"
-                    . "<span size=\"large\">$vers</span>\n\n"
-                    . "<span color=\"blue\" size=\"large\">$url</span>\n\n"
-                    . "<span>Copyright $year by $holder</span>\n\n"
-                    . "<span size=\"large\">$lic</span>\n\n"
-                    . "<span size=\"small\">$license</span>");
-    $dialog->set_title ($xl::s{about});
-    #
+    my $year = '2007-2018';
+    my $holder = 'Ricardo Mones <ricardo@mones.org>';
+    my $url = 'http://www.claws-mail.org/clawsker.php';
+    my $icons = &get_app_icons;
+
+    my $dialog = Gtk3::AboutDialog->new;
+    $dialog->set_transient_for ($parent);
+    $dialog->set_program_name ('Clawsker');
+    $dialog->set_version ($VERSION);
+    $dialog->set_copyright ("Copyright © $year $holder");
+    $dialog->set_license_type ('gpl-3-0');
+    $dialog->set_website ($url);
+    $dialog->set_website_label ($xl::s{about_web_label});
+    # committers, by number of commits
+    $dialog->set_authors ([
+        $holder,
+        'Tristan Chabredier (wwp) <subscript@free.fr>',
+        'Andreas Rönnquist <andreas@ronnquist.net>',
+        'Christian Hesse <mail@eworm.de>',
+    ]);
+    $dialog->set_artists ([
+        'Jesper Schultz <jesper@schultz-net.dk>',
+        $holder,
+    ]);
+    $dialog->set_documenters ([
+        $holder,
+        'Paul Mangan <paul@claws-mail.org>',
+    ]);
+    # active translators, in alphabetical order
+    $dialog->set_translator_credits (join ("\n",
+        'Andreas Rönnquist <andreas@ronnquist.net>',
+        'Axel Köllhofer <AxelKoellhofer@web.de>',
+        'David Medina <opensusecatala@gmail.com>',
+        'Erik P. Olsen <erik@epo.dk>',
+        'Frederico Goncalves Guimaraes <frederico@teia.bio.br>',
+        'Marcel Pol <marcel@timelord.nl>',
+        'Mark Chang <mark.cyj@gmail.com>',
+        'M. Sulchan Darmawan <bleketux@gmail.com>',
+        'Numan Demirdöğen <if.gnu.linux@gmail.com>',
+        'Petter Adsen <petter@synth.no>',
+        $holder,
+        'Tristan Chabredier (wwp) <subscript@free.fr>',
+    ));
+    $dialog->set_title ($xl::s{about_title});
+    $dialog->set_logo ($icons->[-1]);
+
     return $dialog;
 }
 
+sub exit_handler {
+  my ($parent) = @_;
+  if ($MODIFIED != 0 and not $READONLY) {
+    my $markup = "<span>" . $xl::s{exit_fact} . "</span>\n\n"
+        . "<span weight=\"bold\">" . $xl::s{exit_question} . "</span>\n";
+    my $dialog = message_dialog (
+        $parent, $xl::s{exit_title}, $markup, 'question',
+        [ 'gtk-no', 1, 'gtk-yes', 0 ]
+    );
+    my $resp = $dialog->run;
+    $dialog->hide;
+    return TRUE if ($resp == 1);
+  }
+  Gtk3->main_quit;
+}
+
 # create buttons box
 sub new_button_box {
     my ($parent, $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');
+    my $b_about = Gtk3::Button->new_from_stock ('gtk-about');
+    my $b_exit = Gtk3::Button->new_from_stock ('gtk-quit');
+    my $b_apply = Gtk3::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);
+    # my $b_undo = Gtk3::Button->new_from_stock ('gtk-undo');
+    my $hbox = Gtk3::HBox->new (FALSE, 5);
     # signal handlers
-    $b_exit->signal_connect (clicked => sub { Gtk2->main_quit });
+    $b_exit->signal_connect (clicked => sub { exit_handler($parent) });
     $b_apply->set_sensitive (not $READONLY);
-    $b_apply->signal_connect (clicked => sub {
-        save_preferences ($parent);
-        save_ac_preferences ($parent);
-    });
+    $b_apply->signal_connect (clicked => sub { save_preferences($parent) });
     # $b_undo->signal_connect (clicked => sub { undo_current_changes });
     $b_about->signal_connect (clicked => sub { $adlg->run; $adlg->hide });
     # package them
@@ -2177,37 +2734,51 @@ sub new_button_box {
 }
 
 sub get_app_icons {
-    my $dir = $DATADIR . '/icons/hicolor';
-    my @names = map {
-      join ('/', ($dir, , $_ . 'x' . $_, 'apps', $NAME . '.png'))
-    } (64, 128);
-    my @icons = ();
+    return \@APPICONS if (@APPICONS);
+    my @names;
+    if (-d $DATADIR) { # installed
+        my $dir = $DATADIR . '/icons/hicolor';
+        @names = map {
+            join ('/', ($dir, $_ . 'x' . $_, 'apps', $NAME . '.png'))
+        } (48, 64, 128);
+    } else { # unpacked tarball or git clone
+        @names = map {
+            join ('/', ('./icons', $NAME . '-' . $_ . '.png'));
+        } (48, 64, 128);
+    }
     foreach (@names) {
         my $icon = undef;
-        $icon = Gtk2::Gdk::Pixbuf->new_from_file($_) if (-f $_);
-        push @icons, $icon if ($icon);
+        $icon = Gtk3::Gdk::Pixbuf->new_from_file($_) if (-f $_);
+        push @APPICONS, $icon if ($icon);
+    }
+    return \@APPICONS;
+}
+
+sub escape_key_handler {
+    my ($widget, $event) = @_;
+    if ($event->keyval == Gtk3::Gdk::keyval_from_name('Escape')) {
+        exit_handler($widget);
     }
-    return @icons;
 }
 
 # initialise
 exit unless parse_command_line ();
-Gtk2->init;
-$main_window = Gtk2::Window->new ('toplevel');
+Gtk3->init;
+$main_window = Gtk3::Window->new ('toplevel');
 exit unless load_preferences ();
-exit unless load_ac_preferences ();
 exit unless init_hidden_preferences ();
-exit unless init_ac_hidden_preferences ();
 # create main GUI
-my $box = Gtk2::VBox->new (FALSE, 5);
+my $box = Gtk3::VBox->new (FALSE, 5);
 $box->set_border_width(3);
-my $about = new_about_dialog ();
-$box->pack_start (new_notebook (), FALSE, FALSE, 0);
+my $about = new_about_dialog ($main_window);
+$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->signal_connect (delete_event => sub { exit_handler($main_window) });
+$main_window->signal_connect (key_press_event => \&escape_key_handler);
 $main_window->set_title ($xl::s{win_title});
 $main_window->set_icon_list (get_app_icons ());
 $main_window->add ($box);
 $main_window->show_all;
-Gtk2->main;
+$MODIFIED = 0;
+Gtk3->main;