Support for editing history files
[clawsker.git] / clawsker
index 97cefa2f9508981698fb7247eec746cd452676e4..69fa96eb06c38a09877a014d55020dc20b336be1 100755 (executable)
--- a/clawsker
+++ b/clawsker
@@ -1,7 +1,7 @@
-#!/usr/bin/perl -w
+#!/usr/bin/perl
 #
 # Clawsker :: A Claws Mail Tweaker
-# Copyright 2007-2017 Ricardo Mones <ricardo@mones.org>
+# Copyright 2007-2023 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
 # See COPYING file for license details.
 # See AUTHORS file for a complete list of contributors.
 #
-
-binmode STDOUT, ":encoding(utf8)";
-
+package Clawsker;
 use 5.010_000;
 use strict;
+use warnings;
 use utf8;
 use version 0.77;
 use Glib qw(TRUE FALSE);
-use Gtk2;
+use Gtk3;
+use File::Which;
+use File::Spec::Functions;
 use POSIX qw(setlocale);
 use Locale::gettext;
 use Encode;
 use Digest::MD5 qw(md5_hex);
-use Getopt::Long;
+use Getopt::Long qw(GetOptionsFromArray);
 
 my $NAME = 'clawsker';
 my $PREFIX = '@PREFIX@';
@@ -33,17 +34,21 @@ my $DATADIR = '@DATADIR@';
 my $VERSION = '@VERSION@';
 my $VERBOSE = FALSE;
 my $READONLY = FALSE;
+my $IGNOREV = FALSE;
+my $HIDEDK = FALSE;
+my $LOWRES = FALSE;
 my $CLAWSV = undef;
 my $main_window = undef;
 
-my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG});
-$locale = "C" unless defined($locale);
-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 initialise {
+    binmode STDOUT, ":encoding(utf8)";
+    my $locale = (defined($ENV{LC_MESSAGES}) ? $ENV{LC_MESSAGES} : $ENV{LANG});
+    $locale = "C" unless defined($locale);
+    setlocale (LC_ALL, $locale);
+    bindtextdomain ($NAME, catdir ($PREFIX, 'share', 'locale'));
+    textdomain ($NAME);
+}
+initialise;
 
 sub _ {
     my $str = shift;
@@ -59,44 +64,7 @@ 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:'),
-
-    exit_title => _('Clawsker warning'),
-    exit_fact => _('There are unapplied modifications.'),
-    exit_question => _('Do you really want to quit?'),
-
-    tab_colours => _('Colours'),
-    tab_behaviour => _('Behaviour'),
-    tab_gui => _('GUI'),
-    tab_other => _('Other'),
-    tab_winpos => _('Windows'),
-    tab_accounts => _('Accounts'),
-    tab_plugins => _('Plugins'),
-    tab_hotkeys => _('Hotkeys'),
-    tab_info => _('Info'),
-
-    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'),
-    compose_frame => _('Compose window'),
-    qs_frame => _('Quick search'),
 
     l_oth_use_dlg => _('Use detached address book edit dialogue'),
     h_oth_use_dlg => _('If true use a separate dialogue to edit a person\'s details. Otherwise will use a form embedded in the address book\'s main window.'),
@@ -110,9 +78,13 @@ sub _ {
     h_oth_use_netm => _('Use NetworkManager to switch offline automatically.'),
     l_oth_mp_rounds => _('Rounds for PBKDF2 function'),
     h_oth_mp_rounds => _('Specify the number of iterations the key derivation function will be applied on master passphrase computation. Does not modify currently stored passphrase, only master passphrases computed after changing this value are affected.'),
+    l_oth_imap_recl => _('Maximum depth scanning folders'),
+    h_oth_imap_recl => _('Maximum number of nested folders to be scanned on your configured IMAP servers.'),
 
     l_gui_b_unread => _('Show unread messages with bold font'),
     h_gui_b_unread => _('Show unread messages in the Message List using a bold font.'),
+    l_gui_b_marked => _('Show marked messages with bold font'),
+    h_gui_b_marked => _('Show marked messages in the Message List using a bold font.'),
     l_gui_no_markup => _('Don\'t use markup'),
     h_gui_no_markup => _('Don\'t use bold and italic text in Compose dialogue\'s account selector.'),
     l_gui_dot_lines => _('Use dotted lines in tree view components'),
@@ -192,6 +164,14 @@ sub _ {
     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_beh_hide_tz => _('Hide time zone information'),
+    h_beh_hide_tz => _('On outgoing messages, sets time zone of date headers to the unknown timezone value "-0000", as specified by RFC 5322'),
+    l_beh_qs_press_t => _('Quick search type-ahead delay'),
+    l_beh_qs_press_t_units => _('milliseconds'),
+    h_beh_qs_press_t => _('When type-ahead mode of Quick search is enabled, this is the delay after key presses in the search entry before starting a new search with the changes made.'),
+    l_beh_nav_hlen => _('Navigation history length'),
+    l_beh_nav_hlen_units => _('items'),
+    h_beh_nav_hlen => _('Maximum number of messages to keep track of when navigating with the “Next opened message” and “Previously opened message” menu actions'),
 
     l_col_emphasis => _('X-Mailer header'),
     h_col_emphasis => _('The colour used for the X-Mailer line when its value is Claws Mail.'),
@@ -250,11 +230,20 @@ sub _ {
     h_acc_gtls_set => _('Enables using user provided GnuTLS priority string.'),
     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_acc_tls_sni => _('Use TLS SNI extension'),
+    h_acc_tls_sni => _('Enables sending your hostname, if available, so the server can select the appropriate certificate for your domain. Useful for servers which host multiple domains on the same IP address.'),
 
     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_lav_maxr => _('Default redirects'),
+    h_plu_lav_maxr => _('Maximum default value for HTTP redirects, if enabled'),
+    l_plu_lav_maxrmm => _('Mistery man redirects'),
+    h_plu_lav_maxrmm => _('Maximum value for HTTP redirects when using "Mistery man" mode.'),
+    l_plu_lav_maxrurl => _('Custom URL redirects'),
+    h_plu_lav_maxrurl => _('Maximum value for HTTP redirects when using a custom URL'),
+
     l_plu_prl_flvb => _('Log level'),
     h_plu_prl_flvb => _('Verbosity level of log, accumulative.'),
     l_plu_prl_none => _('None'),
@@ -263,6 +252,19 @@ sub _ {
     l_plu_prl_match => _('Matches'),
 );
 
+# labels associated to history file names
+%hi::s = (
+    'command_history' => _('Commands'),
+    'compose_save_to_history' => _('Compose'),
+    'messagesearch_history' => _('Message search'),
+    'quicksearch_history' => _('Quick search'),
+    'summarysearch_adv_history' => _('Quick search (advanced)'),
+    'summary_searchbody_history' => _('Summary (body)'),
+    'summarysearch_from_history' => _('Summary (from)'),
+    'summarysearch_subject_history' => _('Summary (subject)'),
+    'summarysearch_to_history' => _('Summary (to)'),
+);
+
 # data and metadata of resource files
 my $CONFIGDATA;
 my $CONFIGMETA;
@@ -272,13 +274,14 @@ my $ACCOUNTMETA;
 my %PREFS = ();
 my %ACPREFS = ();
 my %PLPREFS = ();
+my %HISTORY = ();
 # 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 $CONFIGDIR = catdir ($ENV{HOME}, '.claws-mail');
 my $CONFIGRC = 'clawsrc';
 my $ACCOUNTRC = 'accountrc';
 # supported and available plugins lists
@@ -293,23 +296,27 @@ my @APPICONS = ();
 # modification flag
 my $MODIFIED = 0;
 
-# 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
-use constant PLUGIN => 6; # plugin section (only in plugin preferences)
-
-# constants for GUI spacing
-use constant HBOX_PAD => 5;
-use constant FRAME_SPC => 2;
-use constant PAGE_SPC => 5;
-
-# for data references indexing
-use constant VALUE => 0;
-use constant IVALUE => 1;
+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 => 9,
+    # for data references indexing
+    VALUE => 0,
+    IVALUE => 1,
+    # hotkey list store columns
+    C_LABEL => 0,
+    C_HOTKEY => 1,
+    C_GROUP => 2,
+    C_ACCEL => 3,
+};
 
 # version functions
 
@@ -322,24 +329,16 @@ sub version_greater_or_equal {
 }
 
 sub get_claws_version {
-    $_ = qx/which claws-mail/;
-    chomp;
-    return "" unless ($_); # not installed
-    my $res = "";
-    $_ = qx/$_ -v/;
-    chomp;
-    my @fver = split (/ /);
-    die "Invalid version string" unless ($fver[2] eq "version");
-    my @ver = split (/\./, $fver[3]);
-    $res .= "$ver[0].";
-    $res .= "$ver[1].";
-    if ($ver[2] =~ /(\d+)git(\d+)/) {
-        $res .= "$1.$2";
-    }
-    else {
-        $res .= "$ver[2].0";
-    }
-    return $res;
+    my $cm_path = which ('claws-mail') or return ""; # not found
+    open my $ph, "-|", $cm_path, "-v"  or return ""; # no pipe
+    chomp (my $v = <$ph>);
+    close $ph;
+    # Claws Mail version 3.17.2git17
+    $v =~ m/\bversion\s+(\d[\w.]+)/ or die "Invalid version string: '$v'";
+    my $cmv = $1;
+    my @ver = split m/(?:\.|git)/, $cmv;
+    @ver < 4 and push @ver, 0;
+    return join ".", @ver;
 }
 
 # data handlers and auxiliar functions
@@ -348,7 +347,7 @@ sub handle_bool_value {
     my ($widget, $event, $dataref) = @_;
     $$dataref->[VALUE] = ($widget->get_active ())? '1': '0';
     $MODIFIED += $$dataref->[VALUE] != $$dataref->[IVALUE]? 1: -1
-        if $$dataref->[IVALUE];
+        if defined $$dataref->[IVALUE];
 }
 
 sub handle_int_value {
@@ -364,14 +363,7 @@ sub handle_int_value {
         $widget->set_text ($$dataref->[VALUE]);
     }
     $MODIFIED += $$dataref->[VALUE] != $$dataref->[IVALUE]? 1: -1
-        if $$dataref->[IVALUE];
-}
-
-sub handle_string_value {
-    my ($widget, $event, $dataref) = @_;
-    $$dataref->[VALUE] = $widget->get_text ();
-    $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
-        if $$dataref->[IVALUE];
+        if defined $$dataref->[IVALUE];
 }
 
 sub handle_nchar_value {
@@ -380,64 +372,64 @@ sub handle_nchar_value {
     $widget->set_text ($_);
     $$dataref->[VALUE] = $_;
     $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
-        if $$dataref->[IVALUE];
+        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) / 255;
+        $gg = hex($2) / 255;
+        $bb = hex($3) / 255;
     }
-    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 * 255;
+    my $gg = $color->green * 255;
+    my $bb = $color->blue * 255;
     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->[VALUE] = 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 $$dataref->[IVALUE];
+        if defined $$dataref->[IVALUE];
 }
 
 sub handle_selection_value {
     my ($widget, $event, $dataref) = @_;
     $$dataref->[VALUE] = $widget->get_active;
     $MODIFIED += $$dataref->[VALUE] ne $$dataref->[IVALUE]? 1: -1
-        if $$dataref->[IVALUE];
+        if defined $$dataref->[IVALUE];
 }
 
 sub get_rc_filename {
-    return $CONFIGDIR . $CONFIGRC;
+    return catfile ($CONFIGDIR, $CONFIGRC);
 }
 
 sub get_ac_rc_filename {
-    return $CONFIGDIR . $ACCOUNTRC;
+    return catfile ($CONFIGDIR, $ACCOUNTRC);
 }
 
 sub get_menurc_filename {
-    return $CONFIGDIR . "menurc";
+    return catfile ($CONFIGDIR, "menurc");
 }
 
 sub set_rc_filename {
     my ($fullname) = @_;
-    my @parts = split ('/', $fullname);
+    my @parts = splitpath ($fullname);
     $CONFIGRC = $parts[$#parts];
     $parts[$#parts] = '';
-    $CONFIGDIR = join ('/', @parts);
+    $CONFIGDIR = catpath (@parts);
 }
 
 sub log_message {
@@ -450,11 +442,39 @@ 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 {
+    return unless (defined $ENV{'DISPLAY'} and $ENV{'DISPLAY'} ne '');
     my ($emsg) = @_;
+    $emsg =~ s/&/&amp;/g;
     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, _('Clawsker error'), $markup, 'error', [ 'gtk-cancel', 0 ]
+    );
     $errordlg->run;
     $errordlg->destroy;
 }
@@ -466,17 +486,27 @@ sub claws_is_running {
     return FALSE;
 }
 
-sub check_claws_not_running {
-    return TRUE if $READONLY;
-    my $tmpdir = (defined $ENV{TMPDIR})? $ENV{TMPDIR}: '/tmp';
-    $tmpdir = '/tmp' if ($tmpdir eq '');
-    my $lockdir = "$tmpdir/claws-mail-$<";
+sub has_claws_socket {
+    my $lockdir = shift;
     -d $lockdir and do {
         $_ = $CONFIGDIR;
         s/\/$//;
-        my $socket = "$lockdir/" . md5_hex ($_);
-        -S $socket and return claws_is_running ();
+        my $socket = catfile ($lockdir, md5_hex ($_));
+        -S $socket and return TRUE;
+    };
+    return FALSE;
+}
+
+sub check_claws_not_running {
+    return TRUE if $READONLY;
+    my $rundir = $ENV{XDG_RUNTIME_DIR} // $ENV{XDG_CACHE_HOME};
+    defined $rundir and do {
+        my $lockdir = catfile ($rundir, "claws-mail");
+        return claws_is_running () if has_claws_socket ($lockdir);
     };
+    my $tmpdir = File::Spec->tmpdir ();
+    my $lockdir = catfile ($tmpdir, "claws-mail-$<");
+    return claws_is_running () if has_claws_socket ($lockdir);
     return TRUE;
 }
 
@@ -492,15 +522,14 @@ 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 {
     my ($wdgt, $versions) = @_;
+    return if $IGNOREV;
     my @ver = split(/,/, $versions);
     if ($#ver == 1) {
       $wdgt->set_sensitive (
@@ -515,7 +544,7 @@ sub set_widget_sens {
 # graphic element creation
 
 sub new_hbox_spaced_pack {
-    my $hbox = Gtk2::HBox->new (FALSE);
+    my $hbox = Gtk3::HBox->new (FALSE);
     foreach (@_) {
         $hbox->pack_start ($_, FALSE, FALSE, HBOX_PAD);
     }
@@ -527,7 +556,7 @@ sub new_check_button_for($$$) {
     my $name = $$hash{$key}[NAME];
     my $label = $$hash{$key}[LABEL];
     #
-    my $cb = Gtk2::CheckButton->new ($label);
+    my $cb = Gtk3::CheckButton->new ($label);
     my $value = $$vhash{$name}[VALUE];
     $value //= $$hash{$key}[CMDEF];
     $cb->set_active ($value eq '1');
@@ -550,12 +579,12 @@ sub new_text_box_for_int($$$) {
     #
     my $gunits = undef;
     if (ref $label eq 'ARRAY') {
-        $gunits = Gtk2::Label->new ($label->[1]);
+        $gunits = Gtk3::Label->new ($label->[1]);
         $label = $label->[0];
     }
-    my $glabel = Gtk2::Label->new ($label);
+    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 $gentry = Gtk3::SpinButton->new_with_range ($type[1], $type[2], $pagei);
     my $value = $$vhash{$name}[VALUE];
     $value //= $$hash{$key}[CMDEF];
     $gentry->set_numeric (TRUE);
@@ -566,8 +595,8 @@ 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);
-    $gunits->set_sensitive ($gentry->sensitive) if ($gunits);
+    $glabel->set_sensitive ($gentry->get_sensitive);
+    $gunits->set_sensitive ($gentry->get_sensitive) if ($gunits);
     #
     return new_hbox_spaced_pack ($glabel, $gentry, $gunits) if ($gunits);
     return new_hbox_spaced_pack ($glabel, $gentry);
@@ -578,8 +607,8 @@ 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 $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];
@@ -593,7 +622,7 @@ 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);
+    $glabel->set_sensitive ($gentry->get_sensitive);
     #
     return new_hbox_spaced_pack ($glabel, $gentry);
 }
@@ -605,9 +634,9 @@ sub new_color_button_for($$$) {
     #
     my $value = $$vhash{$name}[VALUE];
     $value //= $$hash{$key}[CMDEF];
-    my $col = gdk_color_from_str ($value);
-    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 {
@@ -616,7 +645,7 @@ sub new_color_button_for($$$) {
         });
     set_widget_hint ($button, $$hash{$key}[DESC]);
     set_widget_sens ($button, $$hash{$key}[CMVER]);
-    $glabel->set_sensitive ($button->sensitive);
+    $glabel->set_sensitive ($button->get_sensitive);
     #
     return new_hbox_spaced_pack ($button, $glabel);
 }
@@ -626,12 +655,12 @@ sub new_selection_box_for($$$) {
     my $name = $$hash{$key}[NAME];
     my $label = $$hash{$key}[LABEL];
     #
-    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) = @_;
@@ -642,55 +671,76 @@ sub new_selection_box_for($$$) {
     $combo->set_active ($value);
     set_widget_hint ($combo, $$hash{$key}[DESC]);
     set_widget_sens ($combo, $$hash{$key}[CMVER]);
-    $glabel->set_sensitive ($combo->sensitive);
+    $glabel->set_sensitive ($combo->get_sensitive);
     #
     return new_hbox_spaced_pack ($glabel, $combo);
 }
 
 # more graphic helpers
 
-sub new_hbox_pack {
-    my $hbox = Gtk2::HBox->new (FALSE);
-    $hbox->set_border_width (PAGE_SPC);
-    foreach (@_) {
-        $hbox->pack_start ($_, FALSE, FALSE, 0);
-    }
-    return $hbox;
-}
-
-sub new_hbox_pack_compact {
-    my $hbox = Gtk2::HBox->new (FALSE);
-    $hbox->set_border_width (0);
-    foreach (@_) {
-        $hbox->pack_start ($_, FALSE, FALSE, 0);
-    }
-    return $hbox;
-}
-
-sub new_vbox_pack {
-    my $vbox = Gtk2::VBox->new (FALSE, 5);
-    $vbox->set_border_width (PAGE_SPC);
-    foreach (@_) {
-        $vbox->pack_start ($_, FALSE, FALSE, 0);
+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_label {
+    my $text = shift;
+    $text //= '';
+    my $label = Gtk3::Label->new ($text);
+    $label->set_alignment (0, 0.5);
+    return $label;
+}
+
+sub new_title {
+    my $text = shift;
+    $text //= '';
+    $text =~ s/&/&amp;/g;
+    my $label = Gtk3::Label->new ('<b>' . $text . '</b>');
+    $label->set_use_markup (TRUE);
+    $label->set_alignment (0, 0.5);
+    return $label;
+}
+
+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 $vbox;
-}
-
-sub new_vbox_pack_compact {
-    my $vbox = Gtk2::VBox->new (FALSE, 0);
-    $vbox->set_border_width (0);
-    foreach (@_) {
-        $vbox->pack_start ($_, FALSE, FALSE, 0);
+    if ($LOWRES) {
+        my $swin = Gtk3::ScrolledWindow->new;
+        $swin->set_border_width (0);
+        $swin->set_shadow_type ('none');
+        $swin->set_policy ('automatic', 'automatic');
+        $swin->add ($grid);
+        return $swin;
     }
-    return $vbox;
-}
-
-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;
+    return $grid;
 }
 
 # preference maps and corresponding page creation subs
@@ -736,28 +786,34 @@ sub new_subpage_frame {
         '3.13.2.110',
         '50000',
     ],
+    imap_recl => [
+        'imap_scan_tree_recurs_limit',
+        $xl::s{l_oth_imap_recl},
+        $xl::s{h_oth_imap_recl},
+        'int,2,1024',
+        '3.17.6.10',
+        '64',
+    ],
 );
 
 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, 15, [
+        [ _('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) ],
+        [ '--' ],
+        [ _('IMAP') ],
+        [ new_text_box_for_int(\%pr::oth, 'imap_recl', \%HPVALUE) ]
+    ]);
 }
 
 %pr::gui = ( # gui bells and whistles
@@ -769,6 +825,14 @@ sub new_other_page() {
         '0.5.3',
         '1',
     ],
+    b_marked => [
+        'bold_marked',
+        $xl::s{l_gui_b_marked},
+        $xl::s{h_gui_b_marked},
+        'bool',
+        '4.1.0.1365',
+        '0',
+    ],
     no_markup => [
         'compose_no_markup',
         $xl::s{l_gui_no_markup},
@@ -814,7 +878,7 @@ sub new_other_page() {
         $xl::s{l_gui_from_show},
         $xl::s{h_gui_from_show},
         '0=l_gui_from_show_name;1=l_gui_from_show_addr;2=l_gui_from_show_both',
-        '3.7.10',
+        '3.7.10,3.17.4.44',
         '0',
     ],
     strip_off => [
@@ -916,55 +980,39 @@ 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_vbox_pack (
-                             new_hbox_pack_compact (
-                                 new_check_button_for (\%pr::gui, 'no_markup', \%HPVALUE),
-                                 new_check_button_for (\%pr::gui, 'margin_co', \%HPVALUE),
-                                 new_check_button_for (\%pr::gui, 'type_any', \%HPVALUE)),
-                             new_text_box_for_int (\%pr::gui, 'warn_send_multi', \%HPVALUE)),
-                         $xl::s{compo_frame}, 'not-packed'),
-                     FALSE, FALSE, FRAME_SPC);
-    $gf->pack_start ($cb_dot_lines, FALSE, FALSE, 0);
-    $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, 'b_marked', \%HPVALUE),
+            new_check_button_for (\%pr::gui, 'two_linev', \%HPVALUE) ],
+        [ new_selection_box_for (\%pr::gui, 'from_show', \%HPVALUE),
+            new_check_button_for (\%pr::gui, 'next_del', \%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
@@ -1096,48 +1144,64 @@ sub new_gui_page() {
         '3.14.0.94',
         '0',
     ],
+    hide_tz => [
+        'hide_timezone',
+        $xl::s{l_beh_hide_tz},
+        $xl::s{h_beh_hide_tz},
+        'bool',
+        '3.14.0.22',
+        '0',
+    ],
+    qs_press_t => [
+        'qs_press_timeout',
+        [ $xl::s{l_beh_qs_press_t}, $xl::s{l_beh_qs_press_t_units} ],
+        $xl::s{h_beh_qs_press_t},
+        'int,50,5000', # 0.05 seconds - 5 seconds
+        '4.0.0.411',
+        '500',
+
+    ],
+    nav_hlen => [
+        'nav_history_length',
+        [ $xl::s{l_beh_nav_hlen}, $xl::s{l_beh_nav_hlen_units} ],
+        $xl::s{h_beh_nav_hlen},
+        'int,0,500000',
+        '3.8.0.26',
+        '50',
+    ],
 );
 
 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),
-                             new_check_button_for (\%pr::beh, 'rewrite_ff', \%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, 22, [
+        [ _('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) ],
+        [ new_check_button_for (\%pr::beh, 'hide_tz', \%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) ],
+        [ new_text_box_for_int (\%pr::beh, 'qs_press_t', \%HPVALUE) ],
+        [ new_text_box_for_int (\%pr::beh, 'nav_hlen', \%HPVALUE) ]
+    ]);
 }
 
 %pr::col = ( # a variety of colours
@@ -1194,7 +1258,7 @@ sub new_behaviour_page() {
         $xl::s{l_col_diff_add},
         $xl::s{h_col_diff_add},
         'color',
-        '3.8.0.54',
+        '3.8.0.54,3.17.4.35',
         '#008b8b',
     ],
     diff_del => [
@@ -1202,7 +1266,7 @@ sub new_behaviour_page() {
         $xl::s{l_col_diff_del},
         $xl::s{h_col_diff_del},
         'color',
-        '3.8.0.54',
+        '3.8.0.54,3.17.4.35',
         '#6a5acd',
     ],
     diff_hunk => [
@@ -1210,7 +1274,7 @@ sub new_behaviour_page() {
         $xl::s{l_col_diff_hunk},
         $xl::s{h_col_diff_hunk},
         'color',
-        '3.8.0.54',
+        '3.8.0.54,3.17.4.35',
         '#a52a2a',
     ],
     tags_bg => [
@@ -1280,45 +1344,34 @@ sub new_behaviour_page() {
 );
 
 sub new_colours_page() {
-    return new_vbox_pack (
-               new_subpage_frame (
-                   new_vbox_pack (
-                       new_color_button_for (\%pr::col, 'emphasis', \%HPVALUE),
-                       new_hbox_pack_compact (
-                           new_color_button_for (\%pr::col, 'tags_text', \%HPVALUE),
-                           new_color_button_for (\%pr::col, 'tags_bg', \%HPVALUE))),
-                   $xl::s{msgview_frame}, 'not-packed'),
-               new_subpage_frame (
-                   new_hbox_pack (
-                       new_vbox_pack_compact (
-                           new_color_button_for (\%pr::col, 'log_err', \%HPVALUE),
-                           new_color_button_for (\%pr::col, 'log_warn', \%HPVALUE),
-                           new_color_button_for (\%pr::col, 'log_msg', \%HPVALUE)),
-                       new_vbox_pack_compact (
-                           new_color_button_for (\%pr::col, 'log_in', \%HPVALUE),
-                           new_color_button_for (\%pr::col, 'log_out', \%HPVALUE))),
-                   $xl::s{log_frame}, 'not-packed'),
-               new_subpage_frame (
-                   new_vbox_pack (
-                       new_color_button_for (\%pr::col, 'diff_add', \%HPVALUE),
-                       new_color_button_for (\%pr::col, 'diff_del', \%HPVALUE),
-                       new_color_button_for (\%pr::col, 'diff_hunk', \%HPVALUE)),
-                   $xl::s{diff_frame}, 'not-packed'),
-               new_subpage_frame (
-                   new_hbox_pack (
-                       new_color_button_for (\%pr::col, 'default_header_text', \%HPVALUE),
-                       new_color_button_for (\%pr::col, 'default_header_bg', \%HPVALUE)),
-                   $xl::s{compose_frame}, 'not-packed'),
-               new_subpage_frame (
-                   new_hbox_pack (
-                       new_vbox_pack_compact (
-                           new_color_button_for (\%pr::col, 'qs_active_text', \%HPVALUE),
-                           new_color_button_for (\%pr::col, 'qs_error_text', \%HPVALUE)),
-                       new_vbox_pack_compact (
-                           new_color_button_for (\%pr::col, 'qs_active_bg', \%HPVALUE),
-                           new_color_button_for (\%pr::col, 'qs_error_bg', \%HPVALUE))),
-                   $xl::s{qs_frame}, 'not-packed')
-           );
+    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
@@ -1402,6 +1455,23 @@ sub new_colours_page() {
         '0.0.0',
         '540',
     ],
+    urio_w => [
+        'uriopenerwin_width',
+        $xl::s{l_win_w},
+        $xl::s{h_win_w},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.17.3.1',
+        '-1',
+    ],
+    urio_h => [
+        'uriopenerwin_height',
+        $xl::s{l_win_h},
+        $xl::s{h_win_h},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.17.3.1',
+        '-1',
+    ],
+
     send_w => [
         'sendwin_width',
         $xl::s{l_win_w},
@@ -1482,6 +1552,22 @@ sub new_colours_page() {
         '0.0.0',
         '-1',
     ],
+    fsor_w => [
+        'foldersortwin_width',
+        $xl::s{l_win_w},
+        $xl::s{h_win_w},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.17.3.1',
+        '400',
+    ],
+    fsor_h => [
+        'foldersortwin_height',
+        $xl::s{l_win_h},
+        $xl::s{h_win_h},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.17.3.1',
+        '300',
+    ],
     sour_w => [
         'sourcewin_width',
         $xl::s{l_win_w},
@@ -1754,6 +1840,22 @@ sub new_colours_page() {
         '0.0.0',
         '-1',
     ],
+    sslman_w => [
+        'sslmanwin_width',
+        $xl::s{l_win_w},
+        $xl::s{h_win_w},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '0.0.0',
+        '486',
+    ],
+    sslman_h => [
+        'sslmanwin_height',
+        $xl::s{l_win_h},
+        $xl::s{h_win_h},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '0.0.0',
+        '-1',
+    ],
     plug_w => [
         'pluginswin_width',
         $xl::s{l_win_w},
@@ -1802,214 +1904,207 @@ sub new_colours_page() {
         '0.0.0',
         '-1',
     ],
+    about_w => [
+        'aboutwin_width',
+        $xl::s{l_win_w},
+        $xl::s{h_win_w},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.7.1.44',
+        '450',
+    ],
+    about_h => [
+        'aboutwin_height',
+        $xl::s{l_win_h},
+        $xl::s{h_win_h},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.7.1.44',
+        '500',
+    ],
+
 );
 
 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, 7, [
+        [ _('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) ],
+        [ '--' ],
+        [ _('Open URLs window') ],
+        [ new_text_box_for_int (\%pr::win, 'urio_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'urio_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, 10, [
+        [ _('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) ],
+        [ '--' ],
+        [ _('Folder sorting window') ],
+        [ new_text_box_for_int (\%pr::win, 'fsor_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'fsor_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_vbox_pack (
-                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)),
-                     _('User Actions prefs window'), 'not-packed'),
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::win, 'acio_w', \%HPVALUE),
-                          new_text_box_for_int (\%pr::win, 'acio_h', \%HPVALUE)),
-                     _('User Actions I/O window'), 'not-packed')
-           );
+    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, '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 (4, 14, [
+        [ _('About window') ],
+        [ new_text_box_for_int (\%pr::win, 'about_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'about_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('Log window') ],
+        [ new_text_box_for_int (\%pr::win, 'logw_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'logw_h', \%HPVALUE) ],
+        [ '--' ],
+        [ _('SSL manager') ],
+        [ new_text_box_for_int (\%pr::win, 'sslman_w', \%HPVALUE),
+            new_text_box_for_int (\%pr::win, 'sslman_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_useractions, _('User Actions'));
-    $winbook->append_page (new_winpos_subpage_prefs, _('Preferences'));
-    $winbook->append_page (new_winpos_subpage_misc, _('Other'));
+    $winbook->append_page (&new_winpos_subpage_main, new_label (_('Main')));
+    $winbook->append_page (&new_winpos_subpage_msgs, new_label (_('Message')));
+    $winbook->append_page (&new_winpos_subpage_sendrecv, new_label (_('Send/Receive')));
+    $winbook->append_page (&new_winpos_subpage_fold, new_label (_('Folder')));
+    $winbook->append_page (&new_winpos_subpage_addrbook, new_label (_('Addressbook')));
+    $winbook->append_page (&new_winpos_subpage_accounts, new_label (_('Accounts')));
+    $winbook->append_page (&new_winpos_subpage_filtering, new_label (_('Filtering')));
+    $winbook->append_page (&new_winpos_subpage_useractions, new_label (_('User Actions')));
+    $winbook->append_page (&new_winpos_subpage_prefs, new_label (_('Preferences')));
+    $winbook->append_page (&new_winpos_subpage_misc, new_label (_('Other')));
     return $winbook;
 }
 
@@ -2030,21 +2125,29 @@ sub new_winpos_page() {
         '3.9.0.181',
         '0',
     ],
+    tls_sni => [
+        'use_tls_sni',
+        $xl::s{l_acc_tls_sni},
+        $xl::s{h_acc_tls_sni},
+        'bool',
+        '3.17.2.16',
+        '0',
+    ],
 );
 
 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, 5, [
+        [ _('GnuTLS priority') ],
+        [ new_check_button_for (\%pr::acc, 'tls_set', $ACHPVALUE{$akey}) ],
+        [ new_text_box_for_nchar (\%pr::acc, 'tls_pri', $ACHPVALUE{$akey}) ],
+        [ _('Server Name Indication') ],
+        [ new_check_button_for (\%pr::acc, 'tls_sni', $ACHPVALUE{$akey}) ],
+    ]);
 }
 
 sub new_accounts_page() {
-    my $accbook = Gtk2::Notebook->new;
+    my $accbook = Gtk3::Notebook->new;
     $accbook->set_tab_pos ('right');
     my @akeys = sort {
         $ACPREFS{$a}{'account_name'} cmp $ACPREFS{$b}{'account_name'}
@@ -2053,11 +2156,10 @@ sub new_accounts_page() {
         my $name = $ACPREFS{$_}{'account_name'};
         my $isdef = ($ACPREFS{$_}{'is_default'} eq '1');
         my $page = new_account_subpage ($_);
-        $accbook->append_page ($page, $isdef? '<b>' . $name . '</b>': $name);
-        if ($isdef) {
-            my $label = $accbook->get_tab_label ($page);
-            $label->set_use_markup (TRUE);
-        }
+        $name =~ s/&/&amp;/g;
+        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;
@@ -2122,6 +2224,33 @@ sub new_accounts_page() {
         'http://cdn.libravatar.org/avatar',
         'Libravatar',
     ],
+    lav_maxr => [
+        'max_redirects',
+        $xl::s{l_plu_lav_maxr},
+        $xl::s{h_plu_lav_maxr},
+        'int,1,100',
+        '3.17.5.23',
+        '3',
+        'Libravatar',
+    ],
+    lav_maxrmm => [
+        'max_redirects_mm',
+        $xl::s{l_plu_lav_maxrmm},
+        $xl::s{h_plu_lav_maxrmm},
+        'int,1,100',
+        '3.17.5.23',
+        '5',
+        'Libravatar',
+    ],
+    lav_maxrurl => [
+        'max_redirects_url',
+        $xl::s{l_plu_lav_maxrurl},
+        $xl::s{h_plu_lav_maxrurl},
+        'int,1,100',
+        '3.17.5.23',
+        '7',
+        'Libravatar',
+    ],
     # perl
     prl_flvb => [
         'filter_log_verbosity',
@@ -2132,73 +2261,83 @@ sub new_accounts_page() {
         '2',
         'PerlPlugin',
     ],
+    # python
+    py_consw => [
+        'console_win_width',
+        $xl::s{l_win_w},
+        $xl::s{h_win_w},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.17.3.1',
+        '-1',
+        'Python',
+    ],
+    py_consh => [
+        'console_win_height',
+        $xl::s{l_win_h},
+        $xl::s{h_win_h},
+        'int,0,3000', # 0 pixels - 3000 pixels
+        '3.17.3.1',
+        '-1',
+        'Python',
+    ],
 );
 
 sub new_plugins_page() {
-    my %frame = ();
-    $frame{'AttRemover'} =
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::plu, 'arm_winw', $PLHPVALUE{'AttRemover'}),
-                          new_text_box_for_int (\%pr::plu, 'arm_winh', $PLHPVALUE{'AttRemover'})),
-                     _('Attachment remover'), 'not-packed');
-    $frame{'GPG'} =
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::plu, 'gpg_alimit', $PLHPVALUE{'GPG'})),
-                     _('GPG'), 'not-packed');
-    $frame{'ManageSieve'} =
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_int (\%pr::plu, 'msv_winw', $PLHPVALUE{'ManageSieve'}),
-                          new_text_box_for_int (\%pr::plu, 'msv_winh', $PLHPVALUE{'ManageSieve'})),
-                     _('Sieve manager'), 'not-packed');
-    $frame{'Libravatar'} =
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_text_box_for_nchar (\%pr::plu, 'lav_burl', $PLHPVALUE{'Libravatar'})),
-                     _('Libravatar'), 'not-packed');
-    $frame{'PerlPlugin'} =
-                new_subpage_frame (
-                     new_hbox_pack (
-                          new_selection_box_for (\%pr::plu, 'prl_flvb', $PLHPVALUE{'PerlPlugin'})),
-                     _('Perl'), 'not-packed');
-    foreach (@PLUGINS) {
-        $frame{$_}->set_sensitive (defined $PLHPVALUE{$_});
+    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_grid_pack (2, 4, [
+            [ new_text_box_for_nchar (\%pr::plu, 'lav_burl', $PLHPVALUE{'Libravatar'}) ],
+            [ new_text_box_for_int (\%pr::plu, 'lav_maxr', $PLHPVALUE{'Libravatar'}) ],
+            [ new_text_box_for_int (\%pr::plu, 'lav_maxrmm', $PLHPVALUE{'Libravatar'}) ],
+            [ new_text_box_for_int (\%pr::plu, 'lav_maxrurl', $PLHPVALUE{'Libravatar'}) ]
+        ])],
+        'PerlPlugin' => [
+            new_selection_box_for (\%pr::plu, 'prl_flvb', $PLHPVALUE{'PerlPlugin'})
+        ],
+        'Python' => [
+            new_text_box_for_int (\%pr::plu, 'py_consw', $PLHPVALUE{'Python'}),
+            new_text_box_for_int (\%pr::plu, 'py_consh', $PLHPVALUE{'Python'})
+        ],
+    );
+    foreach my $pk (@PLUGINS) {
+        foreach my $wg (@{$widget{$pk}}) {
+            $wg->set_sensitive (defined $PLHPVALUE{$pk});
+        }
     }
-    return new_vbox_pack (
-                $frame{'AttRemover'},
-                $frame{'GPG'},
-                $frame{'ManageSieve'},
-                $frame{'Libravatar'},
-                $frame{'PerlPlugin'});
+    return new_grid_pack (3, 18, [
+        [ _('Attachment remover') ], $widget{'AttRemover'}, [ '--' ],
+        [ _('GPG') ], $widget{'GPG'}, [ '--' ],
+        [ _('Sieve manager') ], $widget{'ManageSieve'}, [ '--' ],
+        [ _('Libravatar') ], $widget{'Libravatar'}, [ '--' ],
+        [ _('Perl') ], $widget{'PerlPlugin'}, [ '--' ],
+        [ _('Python console') ], $widget{'Python'}
+    ]);
 }
 
-use constant {
-    C_LABEL => 0,
-    C_HOTKEY => 1,
-    C_GROUP => 2,
-    C_ACCEL => 3,
-    C_BCOLOR => 4,
-    # cell backgrounds
-    BG_LIGHTER => '#ffffff',
-    BG_DARKER => '#eeeeee'
-};
-
 sub new_hotkeys_list_label {
-    my $renderer = Gtk2::CellRendererText->new ();
+    my $renderer = Gtk3::CellRendererText->new;
     $renderer->set_property('alignment' => 'left');
     $renderer->set_property('editable' => FALSE);
     return $renderer;
 }
 
 sub new_hotkeys_list_hotkey {
-    my $renderer = Gtk2::CellRendererAccel->new ();
+    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 = Gtk2::Accelerator->name ($key, $mods);
+        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);
@@ -2220,35 +2359,45 @@ sub new_hotkeys_list_hotkey {
     return $renderer;
 }
 
+sub store_sort {
+    my ($store, $itera, $iterb, $sortkey) = @_;
+    my $a = $store->get($itera, $sortkey);
+    my $b = $store->get($iterb, $sortkey);
+    return $a cmp $b;
+}
+
 sub new_hotkeys_list {
     my ($gkey, $group) = @_;
-    my $store = Gtk2::ListStore->new(
-        qw/Glib::String Glib::String Glib::String Glib::String Glib::String/);
-    my $even = FALSE;
+    my $store = Gtk3::ListStore->new(
+        qw/Glib::String Glib::String Glib::String Glib::String/);
     foreach my $akey (sort keys %$group) {
         my $iter = $store->append ();
         my $hotkey = $group->{$akey}->{'key'};
         my $label = $akey;
         $label =~ s/[<>]//g; # <rrsyl> and <IMAPFolder> !?
-        my $bgcol = $even ? BG_DARKER: BG_LIGHTER;
         $store->set ($iter, C_LABEL, $label, C_HOTKEY, $hotkey,
-            C_GROUP, $gkey, C_ACCEL, $akey, C_BCOLOR, $bgcol);
-        $even = not $even;
+            C_GROUP, $gkey, C_ACCEL, $akey);
     }
-    my $treeview = Gtk2::TreeView->new_with_model ($store);
+    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) = @_;
+            my $hkey = $model->get_value ($iter, C_HOTKEY);
+            my $label = $model->get_value ($iter, C_LABEL);
+            $label =~ s/&/&amp;/g;
+            my $weight = ($hkey ne '""')? 'weight="bold"': '';
             $renderer->set_property (
-                'markup' => '<span size="smaller">'
-                            . $model->get_value ($iter, C_LABEL)
-                            . '</span>');
-            $renderer->set_property (
-                'background' => $model->get_value ($iter, C_BCOLOR));
+                'markup' => "<span size=\"smaller\" $weight>$label</span>'"
+            );
         }
     );
+    my $stylectx = $treeview->get_style_context;
+    my $discol = $HIDEDK
+        ? $stylectx->get_background_color ('normal')
+        : $stylectx->get_color ('insensitive');
+    my $enacol = $stylectx->get_color ('normal');
     # hotkeys column
     $treeview->insert_column_with_data_func (
         1, _('Hotkey'), new_hotkeys_list_hotkey (),
@@ -2256,83 +2405,91 @@ sub new_hotkeys_list {
             my ($col, $renderer, $model, $iter, $data) = @_;
             my $hkey = $model->get_value ($iter, C_HOTKEY);
             $hkey =~ s/\"//g;
-            my ($acckey, $accmod) = Gtk2::Accelerator->parse ($hkey);
+            my ($acckey, $accmod) = Gtk3::accelerator_parse ($hkey);
             $renderer->set_property ('accel-key' => $acckey);
             $renderer->set_property ('accel-mods' => $accmod);
             $renderer->set_property (
-                'background' => $model->get_value ($iter, C_BCOLOR));
+                'foreground-rgba' => ($acckey == 0? $discol: $enacol)
+            );
         }
     );
+    for (0, 1) {
+        $store->set_sort_func($_, \&store_sort, $_);
+        my $col = $treeview->get_column($_);
+        $col->set_sort_column_id($_);
+    }
     # callback for saving current selection
     my $selection = $treeview->get_selection ();
     $selection->signal_connect ('changed' => sub { $SELHOTKEY = shift });
+    $treeview->set_property('enable-grid-lines', 'horizontal');
     return $treeview;
 }
 
 sub new_hotkeys_page() {
-    my $swin = Gtk2::ScrolledWindow->new ();
-    my $vbox = Gtk2::VBox->new (FALSE, 5);
+    my $hkbook = Gtk3::Notebook->new;
+    $hkbook->set_tab_pos ('right');
     foreach my $gkey (sort keys %$HOTKEYS) {
         my $group = $HOTKEYS->{$gkey};
-        # group title
-        my $glabel = Gtk2::Label->new ('<b>' . $gkey . '</b>');
-        $glabel->set_use_markup (TRUE);
-        $glabel->set_alignment (0, 0.5);
-        $glabel->set_padding (5, 1);
-        $vbox->pack_start ($glabel, FALSE, FALSE, 0);
-        # group key list
         my $keylist = new_hotkeys_list ($gkey, $group);
-        $vbox->pack_start ($keylist, FALSE, FALSE, 0);
+        # prepare scrolled window
+        my $swin = Gtk3::ScrolledWindow->new;
+        $swin->set_border_width (5);
+        $swin->set_shadow_type ('none');
+        $swin->set_policy ('automatic', 'automatic');
+        # add list of keys
+        $swin->add ($keylist);
+        $hkbook->append_page ($swin, new_label ($gkey));
     }
+    return $hkbook;
+}
+
+sub new_history_subpage($) {
+    my ($key) = @_;
+    my $fname = catfile ($CONFIGDIR, $key);
+    my $buffer = Gtk3::TextBuffer->new;
+    $buffer->set_text($HISTORY{$key});
+    $buffer->signal_connect ('changed' => sub {
+        my ($hbuf, $hkey) = @_;
+        my $start = $hbuf->get_iter_at_offset(0);
+        my $end = $hbuf->get_iter_at_offset(-1);
+        $HISTORY{$hkey} = $hbuf->get_text($start, $end, FALSE)
+    }, $key);
+    my $textview = Gtk3::TextView->new_with_buffer($buffer);
+    $textview->set_editable(not $READONLY);
+    my $swin = Gtk3::ScrolledWindow->new;
     $swin->set_border_width (5);
     $swin->set_shadow_type ('none');
-    $swin->set_policy ('automatic', 'always');
-    $swin->add_with_viewport ($vbox);
+    $swin->set_policy ('automatic', 'automatic');
+    $swin->add ($textview);
     return $swin;
 }
 
+sub new_histories_page() {
+    my $hisbook = Gtk3::Notebook->new;
+    $hisbook->set_tab_pos ('right');
+    foreach my $name (sort { $hi::s{$a} cmp $hi::s{$b} } keys %hi::s) {
+        $hisbook->append_page (&new_history_subpage($name), new_label ($hi::s{$name}));
+    }
+    return $hisbook;
+}
+
 sub new_info_page() {
-    my $t0 = Gtk2::Table->new (7, 2, FALSE);
     my $v = get_toolkit_versions ();
-    my %labels = (
-        'glib' => 'Perl-GLib',
-        'glib-r' => _('GLib runtime'),
-        'glib-b' => _('GLib built'),
-        'gtk2' => 'Perl-GTK2',
-        'gtk2-r' => _('GTK2 runtime'),
-        'gtk2-b' => _('GTK2 built'),
-    );
-    my $row = 0;
-    foreach (sort keys %$v) {
-        if (defined $v->{$_}) {
-            my $label = Gtk2::Label->new ($labels{$_});
-            my $value = Gtk2::Label->new ('<b>' . $v->{$_} . '</b>');
-            $label->set_alignment (0, 0.5);
-            $value->set_alignment (0, 0.5);
-            $value->set_use_markup (TRUE);
-            $t0->attach ($label, 0, 1, $row, $row + 1, 'fill', 'shrink', 8, 6);
-            $t0->attach ($value, 1, 2, $row, $row + 1, 'fill', 'shrink', 8, 6);
-            ++$row;
-        }
-    }
-    my $t1 = Gtk2::Table->new (2, 2, FALSE);
-    my @lbl = map { $_->set_alignment (0, 0.5); $_ } (
-        Gtk2::Label->new (_('Binary')),
-        Gtk2::Label->new (_('Configuration'))
-    );
     my $cfgv = $CONFIGDATA->{'Common'}{'config_version'};
     $cfgv //= '';
-    my @val = map { $_->set_alignment (0, 0.5); $_->set_use_markup (TRUE); $_ } (
-        Gtk2::Label->new ('<b>' . $CLAWSV . '</b>'),
-        Gtk2::Label->new ('<b>' . $cfgv . '</b>')
-    );
-    for (my $i = 0; $i <= $#lbl; ++$i) {
-        $t1->attach ($lbl[$i], 0, 1, $i, $i + 1, 'fill', 'shrink', 8, 6);
-        $t1->attach ($val[$i], 1, 2, $i, $i + 1, 'fill', 'shrink', 8, 6);
-    }
-    return new_vbox_pack (
-                new_subpage_frame ($t0, _('Library versions'), 'not-packed'),
-                new_subpage_frame ($t1, _('Claws Mail versions'), 'not-packed'));
+    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'} // _('not available')) ],
+        [ new_label (_('GLib built')), new_title ($v->{'glib-b'} // _('not available')) ],
+        [ 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
@@ -2345,41 +2502,40 @@ sub get_toolkit_versions {
         $versions{'glib-r'} = join('.',
             &Glib::major_version, &Glib::minor_version, &Glib::micro_version);
     }
-    $versions{'gtk2'} = $Gtk2::VERSION;
-    if ($Gtk2::VERSION >= 1.040) {
-        $versions{'gtk2-b'} = join('.', Gtk2->GET_VERSION_INFO);
-        $versions{'gtk2-r'} = join('.',
-            &Gtk2::major_version, &Gtk2::minor_version, &Gtk2::micro_version);
+    $versions{'gtk'} = $Gtk3::VERSION;
+    if ($Gtk3::VERSION >= 0.034) {
+        $versions{'gtk-b'} = join('.', &Gtk3::GET_VERSION_INFO);
+    } else {
+        $versions{'gtk-b'} = _('Not available')
     }
+    $versions{'gtk-r'} = join('.', &Gtk3::get_version_info);
     return \%versions;
 }
 
 sub print_version() {
-    print $xl::s{about_title} . "\n";
-    print $xl::s{about_version} . " $VERSION\n";
+    say $xl::s{about_title};
+    say _('Version:') . " $VERSION";
     my $v = get_toolkit_versions ();
     if ($v->{'glib-b'}) {
-        print _("Perl-GLib version {glibv}, built for {glibb}, running with {glibr}.",
+        say _("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'});
+        say _("Perl-GLib version {glibv}.", glibv => $v->{'glib'});
     }
-    print "\n";
-    if ($v->{'gtk2-b'}) {
-        print _("Perl-GTK2 version {gtkv}, built for {gtkb}, running with {gtkr}.",
-                gtkv => $v->{'gtk2'},
-                gtkb => $v->{'gtk2-b'},
-                gtkr => $v->{'gtk2-r'});
+    if ($v->{'gtk-b'}) {
+        say _("Perl-GTK3 version {gtkv}, built for {gtkb}, running with {gtkr}.",
+                gtkv => $v->{'gtk'},
+                gtkb => $v->{'gtk-b'},
+                gtkr => $v->{'gtk-r'});
     } else {
-        print _("Perl-GTK2 version {gtkv}.", gtkv => $v->{'gtk2'});
+        say _("Perl-GTK3 version {gtkv}.", gtkv => $v->{'gtk'});
     }
-    print "\n";
     my $clawsver = ($CLAWSV eq "") ?
                 _("Claws Mail was not found!") :
                 _("Claws Mail returned version {cmv}.", cmv => $CLAWSV);
-    print $clawsver . "\n";
+    say $clawsver;
 }
 
 # the command line help
@@ -2396,20 +2552,29 @@ sub print_help() {
         _("  -b|--verbose                     More messages on standard output."),
         _("  -c|--clawsrc <file>              Uses <file> as full resource name."),
         _("  -h|--help                        Prints this help screen and exits."),
+        _("  -i|--ignore-versions             Allows setting almost everything."),
+        _("  -k|--hide-disabled-keys          Hides unassigned hotkeys in key columns."),
         _("  -r|--read-only                   Disables writing changes to disk."),
+        _("  -s|--small-screen                Forces low resolution UI adjustments."),
+        _("  -u|--use-claws-version <ver>     Uses <ver> instead of detected version."),
         _("  -v|--version                     Prints version information and exits.")
     );
     foreach (@help) { say $_ }
 }
 
 sub parse_command_line {
+    my $argv = shift;
     my $cont = TRUE;
     $CLAWSV = get_claws_version ();
     eval {
-        GetOptions('h|help' => sub { print_help (); $cont = FALSE },
+        GetOptionsFromArray($argv,
+            'h|help' => sub { print_help (); $cont = FALSE },
             'v|version' => sub { print_version (); $cont = FALSE },
             'b|verbose' => sub { $VERBOSE = TRUE },
             'r|read-only' => sub { $READONLY = TRUE },
+            's|small-screen' => sub { $LOWRES = TRUE },
+            'i|ignore-versions' => sub { $IGNOREV = TRUE },
+            'k|hide-disabled-keys' => sub { $HIDEDK = TRUE },
             'u|use-claws-version=s' => \&opt_use_claws_version,
             'a|alternate-config-dir=s' => \&opt_alternate_config_dir,
             'c|clawsrc=s' => \&opt_clawsrc)
@@ -2418,7 +2583,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;
@@ -2438,7 +2603,6 @@ sub opt_alternate_config_dir {
     die _("Error: '{dir}' is not a directory or does not exist\n", dir => $value)
         unless -d $value;
     $CONFIGDIR = $value;
-    $CONFIGDIR .= "/" unless ($CONFIGDIR =~ /.*\/$/);
     $ALTCONFIGDIR = TRUE;
 }
 
@@ -2523,23 +2687,35 @@ sub save_resource {
             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";
-            }
+            delete $data->{$section}{'_'};
+        }
+        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;
@@ -2587,6 +2763,48 @@ sub save_menurc {
     close (RCF);
 }
 
+# history files
+sub load_history {
+    my $history = shift;
+    open (HIF, '<:encoding(utf8)', $history)
+        or die _("Error: opening '{file}' for reading", file => $history) . ": $!\n";
+    my @lines = ();
+    while (<HIF>) { chomp; s/^\s*//; push @lines, $_ if $_ }
+    close (HIF);
+    return join("\n", @lines);
+}
+
+sub save_history {
+    my ($history, $text) = @_;
+    open (HIF, '>:utf8', $history)
+        or die _("Error: opening '{file}' for writing", file => $history) . ": $!\n";
+    say HIF $text;
+    close (HIF);
+}
+
+sub load_hi_preferences {
+    foreach my $key (keys %hi::s) {
+        my $fname = catfile ($CONFIGDIR, $key);
+        my $history = '';
+        if (-f $fname) {
+            $history = load_history($fname)
+        }
+        $HISTORY{$key} = $history;
+    }
+    return TRUE;
+}
+
+sub save_hi_preferences {
+    foreach my $key (keys %HISTORY) {
+        my $fname = catfile ($CONFIGDIR, $key);
+        if (-f $fname) {
+            return FALSE unless backup_resource ($fname)
+        }
+        save_history($fname, $HISTORY{$key});
+    }
+    return TRUE;
+}
+
 # load current status from disc
 sub load_rc_preferences {
     my $rc = get_rc_filename ();
@@ -2634,6 +2852,7 @@ sub load_preferences {
     return (load_rc_preferences ()
         and load_ac_preferences ()
         and load_hk_preferences ()
+        and load_hi_preferences ()
     );
 }
 
@@ -2642,9 +2861,7 @@ 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);
+    return FALSE unless backup_resource ($rc);
     foreach (keys %PREFS) {
         if (defined $HPVALUE{$_}) {
             $CONFIGDATA->{'Common'}{$_} = $HPVALUE{$_}[VALUE];
@@ -2665,9 +2882,7 @@ sub save_ac_preferences {
     my $rc = get_ac_rc_filename ();
     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);
+    return FALSE unless backup_resource ($rc);
     foreach my $asect (keys %$ACCOUNTDATA) {
         if ($asect =~ /^Account: (\d+)$/) {
             foreach (keys %{$ACCOUNTDATA->{$asect}}) {
@@ -2685,32 +2900,35 @@ 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 {
+    return FALSE unless check_claws_not_running ();
     my $result = save_rc_preferences ()
         and save_ac_preferences ()
-        and save_hk_preferences ();
+        and save_hk_preferences ()
+        and save_hi_preferences ();
     $MODIFIED = 0 if $result;
     return $result;
 }
 
 # create notebook
 sub new_notebook {
-    my $nb = Gtk2::Notebook->new;
-    #
-    $nb->append_page (new_behaviour_page (), $xl::s{tab_behaviour});
-    $nb->append_page (new_colours_page (), $xl::s{tab_colours});
-    $nb->append_page (new_gui_page (), $xl::s{tab_gui});
-    $nb->append_page (new_other_page (), $xl::s{tab_other});
-    $nb->append_page (new_winpos_page (), $xl::s{tab_winpos});
-    $nb->append_page (new_accounts_page (), $xl::s{tab_accounts});
-    $nb->append_page (new_plugins_page (), $xl::s{tab_plugins});
-    $nb->append_page (new_hotkeys_page (), $xl::s{tab_hotkeys});
-    $nb->append_page (new_info_page (), $xl::s{tab_info});
+    my $nb = Gtk3::Notebook->new;
+
+    $nb->append_page (&new_behaviour_page, Gtk3::Label->new (_('Behaviour')));
+    $nb->append_page (&new_colours_page, Gtk3::Label->new (_('Colours')));
+    $nb->append_page (&new_gui_page, Gtk3::Label->new (_('GUI')));
+    $nb->append_page (&new_other_page, Gtk3::Label->new (_('Other')));
+    $nb->append_page (&new_winpos_page, Gtk3::Label->new (_('Windows')));
+    $nb->append_page (&new_accounts_page, Gtk3::Label->new (_('Accounts')));
+    $nb->append_page (&new_plugins_page, Gtk3::Label->new (_('Plugins')));
+    $nb->append_page (&new_hotkeys_page, Gtk3::Label->new (_('Hotkeys')));
+    $nb->append_page (&new_histories_page, Gtk3::Label->new (_('Histories')));
+    $nb->append_page (&new_info_page, Gtk3::Label->new (_('Info')));
 
     return $nb;
 }
@@ -2718,74 +2936,83 @@ 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-2017";
-    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});
-    if (Gtk2->CHECK_VERSION (2, 10, 0)) {
-        my @icons = get_app_icons ();
-        my $image = Gtk2::Image->new_from_pixbuf ($icons[-1]);
-        $image->show ();
-        $image->set_alignment (0, 0);
-        $dialog->set_image ($image);
-    }
-    #
+    my $year = '2007-2023';
+    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 (_("Visit Clawsker's web page"));
+    # 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>',
+        'Pedro Albuquerque <pmra@gmx.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 $fact = $xl::s{exit_fact};
-    my $question = $xl::s{exit_question};
-    my $dialog = Gtk2::MessageDialog->new_with_markup ($parent,
-                    [qw/modal destroy-with-parent/],
-                    'warning', 'yes-no',
-                    "<span>$fact</span>\n\n"
-                    . "<span weight=\"bold\">$question</span>");
-    $dialog->set_title ($xl::s{exit_title});
+    my $markup = "<span>" . _('There are unapplied modifications.')
+        . "</span>\n\n<span weight=\"bold\">"
+        . _('Do you really want to quit?') . "</span>\n";
+    $markup =~ s/&/&amp;/g;
+    my $dialog = message_dialog (
+        $parent, _('Clawsker warning'), $markup, 'question',
+        [ 'gtk-no', 1, 'gtk-yes', 0 ]
+    );
     my $resp = $dialog->run;
     $dialog->hide;
-    return TRUE if ($resp eq 'no');
+    return TRUE if $resp;
   }
-  Gtk2->main_quit;
+  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 { exit_handler($parent) });
     $b_apply->set_sensitive (not $READONLY);
@@ -2802,50 +3029,63 @@ sub new_button_box {
 }
 
 sub get_app_icons {
-    return @APPICONS if (@APPICONS);
+    return \@APPICONS if (@APPICONS);
     my @names;
     if (-d $DATADIR) { # installed
-        my $dir = $DATADIR . '/icons/hicolor';
+        my $dir = catdir ($DATADIR, 'icons', 'hicolor');
         @names = map {
-            join ('/', ($dir, $_ . 'x' . $_, 'apps', $NAME . '.png'))
+            catfile ($dir, $_ . 'x' . $_, 'apps', $NAME . '.png')
         } (48, 64, 128);
     } else { # unpacked tarball or git clone
         @names = map {
-            join ('/', ('./icons', $NAME . '-' . $_ . '.png'));
+            catfile ('.', 'icons', $NAME . '-' . $_ . '.png');
         } (48, 64, 128);
     }
     foreach (@names) {
         my $icon = undef;
-        $icon = Gtk2::Gdk::Pixbuf->new_from_file($_) if (-f $_);
+        $icon = Gtk3::Gdk::Pixbuf->new_from_file($_) if (-f $_);
         push @APPICONS, $icon if ($icon);
     }
-    return @APPICONS;
+    return \@APPICONS;
 }
 
 sub escape_key_handler {
     my ($widget, $event) = @_;
-    if ($event->keyval == Gtk2::Gdk->keyval_from_name('Escape')) {
+    if ($event->keyval == Gtk3::Gdk::keyval_from_name('Escape')) {
         exit_handler($widget);
     }
 }
 
-# initialise
-exit unless parse_command_line ();
-Gtk2->init;
-$main_window = Gtk2::Window->new ('toplevel');
-exit unless load_preferences ();
-exit unless init_hidden_preferences ();
-# create main GUI
-my $box = Gtk2::VBox->new (FALSE, 5);
-$box->set_border_width(3);
-my $about = new_about_dialog ();
-$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 { 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;
-
+sub get_screen_height {
+    my $display = Gtk3::Gdk::Display::get_default();
+    my $monitor = Gtk3::Gdk::Display::get_primary_monitor($display);
+    my $area = Gtk3::Gdk::Monitor::get_workarea($monitor);
+    return $area->{'height'};
+}
+
+sub main {
+    my $args = shift;
+    exit unless parse_command_line ($args);
+    Gtk3->init;
+    $main_window = Gtk3::Window->new ('toplevel');
+    exit unless load_preferences ();
+    exit unless init_hidden_preferences ();
+    # create main GUI
+    $LOWRES = TRUE unless get_screen_height() > 720;
+    my $box = Gtk3::VBox->new (FALSE, 5);
+    $box->set_border_width(3);
+    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 { exit_handler($main_window) });
+    $main_window->signal_connect (key_press_event => \&escape_key_handler);
+    $main_window->set_title (_('Claws Mail Hidden Preferences'));
+    $main_window->set_icon_list (get_app_icons ());
+    $main_window->add ($box);
+    $main_window->show_all;
+    $MODIFIED = 0;
+    Gtk3->main;
+    return 0;
+}
+
+exit Clawsker::main(\@ARGV) unless caller;