Support for editing history files
[clawsker.git] / clawsker
index fecd4d1cb24f5b7dc41b4f2bfa1273677880b62f..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-2018 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);
@@ -26,7 +25,7 @@ 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@';
@@ -35,14 +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, catdir ($PREFIX, 'share', 'locale'));
-textdomain ($NAME);
+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;
@@ -58,15 +64,7 @@ sub _ {
 
 # default messages
 %xl::s = (
-    win_title => _('Claws Mail Hidden Preferences'),
     about_title => _('Clawsker :: A Claws Mail Tweaker'),
-    about_web_label => _("Visit Clawsker's web page"),
-
-    error_title => _('Clawsker error'),
-
-    exit_title => _('Clawsker warning'),
-    exit_fact => _('There are unapplied modifications.'),
-    exit_question => _('Do you really want to quit?'),
 
     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.'),
@@ -80,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'),
@@ -162,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.'),
@@ -220,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'),
@@ -233,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;
@@ -242,6 +274,7 @@ my $ACCOUNTMETA;
 my %PREFS = ();
 my %ACPREFS = ();
 my %PLPREFS = ();
+my %HISTORY = ();
 # values of all preferences handled by clawsker
 my %HPVALUE = ();
 my %ACHPVALUE = ();
@@ -274,7 +307,7 @@ use constant {
     PLUGIN => 6, # plugin section (only in plugin preferences)
     # constants for GUI spacing
     HBOX_PAD => 5,
-    GRID_SPC => 10,
+    GRID_SPC => 9,
     # for data references indexing
     VALUE => 0,
     IVALUE => 1,
@@ -283,7 +316,6 @@ use constant {
     C_HOTKEY => 1,
     C_GROUP => 2,
     C_ACCEL => 3,
-    C_ODDITY => 4,
 };
 
 # version functions
@@ -297,23 +329,16 @@ sub version_greater_or_equal {
 }
 
 sub get_claws_version {
-    $_ = which ('claws-mail');
-    return "" unless defined $_; # 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
@@ -355,9 +380,9 @@ sub gdk_rgba_from_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 = Gtk3::Gdk::RGBA->new ($rr, $gg, $bb, 1.0);
     return $color;
@@ -365,9 +390,9 @@ sub gdk_rgba_from_str {
 
 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;
 }
@@ -443,10 +468,12 @@ sub message_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 = message_dialog (
-        $main_window, $xl::s{error_title}, $markup, 'error', [ 'gtk-cancel', 0 ]
+        $main_window, _('Clawsker error'), $markup, 'error', [ 'gtk-cancel', 0 ]
     );
     $errordlg->run;
     $errordlg->destroy;
@@ -459,16 +486,27 @@ sub claws_is_running {
     return FALSE;
 }
 
-sub check_claws_not_running {
-    return TRUE if $READONLY;
-    my $tmpdir = File::Spec->tmpdir ();
-    my $lockdir = catfile ($tmpdir, "claws-mail-$<");
+sub has_claws_socket {
+    my $lockdir = shift;
     -d $lockdir and do {
         $_ = $CONFIGDIR;
         s/\/$//;
         my $socket = catfile ($lockdir, md5_hex ($_));
-        -S $socket and return claws_is_running ();
+        -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;
 }
 
@@ -491,6 +529,7 @@ sub set_widget_hint {
 
 sub set_widget_sens {
     my ($wdgt, $versions) = @_;
+    return if $IGNOREV;
     my @ver = split(/,/, $versions);
     if ($#ver == 1) {
       $wdgt->set_sensitive (
@@ -569,7 +608,7 @@ sub new_text_box_for_nchar($$$) {
     my $label = $$hash{$key}[LABEL];
     my @type = split (/,/, $$hash{$key}[TYPE]); # char,minlen,maxlen,width
     my $glabel = Gtk3::Label->new ($label);
-    my $gentry = Gtk3::Entry->new ();
+    my $gentry = Gtk3::Entry->new;
     $gentry->set_max_length($type[2]) if defined ($type[2]);
     my $width = $type[3];
     $width //= $type[2];
@@ -662,6 +701,7 @@ sub new_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);
@@ -692,6 +732,14 @@ sub new_grid_pack {
             }
         }
     }
+    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 $grid;
 }
 
@@ -738,10 +786,18 @@ sub new_grid_pack {
         '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_grid_pack (1, 12, [
+    return new_grid_pack (1, 15, [
         [ _('Addressbook') ],
         [ new_check_button_for(\%pr::oth, 'use_dlg', \%HPVALUE) ],
         [ '--' ],
@@ -753,7 +809,10 @@ sub new_other_page() {
         [ new_check_button_for(\%pr::oth, 'use_netm', \%HPVALUE) ],
         [ '--' ],
         [ _('Master passphrase') ],
-        [ new_text_box_for_int(\%pr::oth, 'mp_rounds', \%HPVALUE) ]
+        [ new_text_box_for_int(\%pr::oth, 'mp_rounds', \%HPVALUE) ],
+        [ '--' ],
+        [ _('IMAP') ],
+        [ new_text_box_for_int(\%pr::oth, 'imap_recl', \%HPVALUE) ]
     ]);
 }
 
@@ -766,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},
@@ -811,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 => [
@@ -922,9 +989,10 @@ sub new_gui_page() {
         [ _('Message List') ],
         [ new_check_button_for (\%pr::gui, 'b_unread', \%HPVALUE),
             new_check_button_for (\%pr::gui, 'swp_from', \%HPVALUE) ],
-        [ new_check_button_for (\%pr::gui, 'two_linev', \%HPVALUE),
+        [ new_check_button_for (\%pr::gui, '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) ],
-        [ new_selection_box_for (\%pr::gui, 'from_show', \%HPVALUE) ],
         [ '--' ],
         [ _('Message View') ],
         [ new_check_button_for (\%pr::gui, 'cursor_v', \%HPVALUE),
@@ -933,12 +1001,12 @@ sub new_gui_page() {
         [ _('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) ],
+        [ 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) ],
+        [ 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),
@@ -1076,13 +1144,38 @@ 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() {
-    return new_grid_pack (2, 20, [
+    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) ],
+        [ 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),
@@ -1097,14 +1190,17 @@ sub new_behaviour_page() {
             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) ],
+        [ 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, 'thread_a', \%HPVALUE) ],
+        [ new_text_box_for_int (\%pr::beh, 'qs_press_t', \%HPVALUE) ],
+        [ new_text_box_for_int (\%pr::beh, 'nav_hlen', \%HPVALUE) ]
     ]);
 }
 
@@ -1162,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 => [
@@ -1170,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 => [
@@ -1178,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 => [
@@ -1359,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},
@@ -1439,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},
@@ -1711,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},
@@ -1759,6 +1904,23 @@ 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() {
@@ -1774,12 +1936,16 @@ sub new_winpos_subpage_main() {
 }
 
 sub new_winpos_subpage_msgs() {
-    return new_grid_pack (3, 4, [
+    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) ]
+            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) ]
     ]);
 }
 
@@ -1796,7 +1962,7 @@ sub new_winpos_subpage_sendrecv() {
 }
 
 sub new_winpos_subpage_fold() {
-    return new_grid_pack (3, 7, [
+    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) ],
@@ -1805,7 +1971,11 @@ sub new_winpos_subpage_fold() {
         [ '--' ],
         [ _('Folder selection window') ],
         [ new_text_box_for_int (\%pr::win, 'fsel_w', \%HPVALUE),
-            new_text_box_for_int (\%pr::win, 'fsel_h', \%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) ],
     ]);
 }
 
@@ -1859,7 +2029,7 @@ sub new_winpos_subpage_filtering() {
         [ new_text_box_for_int (\%pr::win, 'fild_w', \%HPVALUE),
             new_text_box_for_int (\%pr::win, 'fild_h', \%HPVALUE) ],
         [ '--' ],
-        [ ('Matcher window') ],
+        [ _('Matcher window') ],
         [ new_text_box_for_int (\%pr::win, 'matc_w', \%HPVALUE),
             new_text_box_for_int (\%pr::win, 'matc_h', \%HPVALUE) ]
 
@@ -1899,11 +2069,19 @@ sub new_winpos_subpage_prefs() {
 }
 
 sub new_winpos_subpage_misc() {
-    return new_grid_pack (3, 8, [
+    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) ],
@@ -1947,14 +2125,24 @@ 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_grid_pack (1, 3, [
+    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}) ]
+        [ new_text_box_for_nchar (\%pr::acc, 'tls_pri', $ACHPVALUE{$akey}) ],
+        [ _('Server Name Indication') ],
+        [ new_check_button_for (\%pr::acc, 'tls_sni', $ACHPVALUE{$akey}) ],
     ]);
 }
 
@@ -1968,6 +2156,7 @@ sub new_accounts_page() {
         my $name = $ACPREFS{$_}{'account_name'};
         my $isdef = ($ACPREFS{$_}{'is_default'} eq '1');
         my $page = new_account_subpage ($_);
+        $name =~ s/&/&amp;/g;
         my $label = new_label ($isdef? '<u>' . $name . '</u>': $name);
         $label->set_use_markup (TRUE);
         $accbook->append_page ($page, $label);
@@ -2035,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',
@@ -2045,6 +2261,25 @@ 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() {
@@ -2060,41 +2295,49 @@ sub new_plugins_page() {
             new_text_box_for_int (\%pr::plu, 'msv_winw', $PLHPVALUE{'ManageSieve'}),
             new_text_box_for_int (\%pr::plu, 'msv_winh', $PLHPVALUE{'ManageSieve'})
         ],
-        'Libravatar' => [
-            new_text_box_for_nchar (\%pr::plu, 'lav_burl', $PLHPVALUE{'Libravatar'})
-        ],
+        '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_grid_pack (3, 14, [
+    return new_grid_pack (3, 18, [
         [ _('Attachment remover') ], $widget{'AttRemover'}, [ '--' ],
         [ _('GPG') ], $widget{'GPG'}, [ '--' ],
         [ _('Sieve manager') ], $widget{'ManageSieve'}, [ '--' ],
         [ _('Libravatar') ], $widget{'Libravatar'}, [ '--' ],
-        [ _('Perl') ], $widget{'PerlPlugin'}
+        [ _('Perl') ], $widget{'PerlPlugin'}, [ '--' ],
+        [ _('Python console') ], $widget{'Python'}
     ]);
 }
 
 sub new_hotkeys_list_label {
-    my $renderer = Gtk3::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 = Gtk3::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 = Gtk3::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);
@@ -2116,28 +2359,24 @@ sub new_hotkeys_list_hotkey {
     return $renderer;
 }
 
-sub row_background_color {
-    my ($column, $isodd) = @_;
-    my $treeview = $column->get_tree_view;
-    my $stylectx = $treeview->get_style_context;
-    return $isodd
-        ? $stylectx->get_background_color ('normal')
-        : $stylectx->get_background_color ('insensitive');
+sub 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 = Gtk3::ListStore->new(
-        qw/Glib::String Glib::String Glib::String Glib::String Glib::Boolean/);
-    my $even = TRUE;
+        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> !?
         $store->set ($iter, C_LABEL, $label, C_HOTKEY, $hotkey,
-            C_GROUP, $gkey, C_ACCEL, $akey, C_ODDITY, $even);
-        $even = not $even;
+            C_GROUP, $gkey, C_ACCEL, $akey);
     }
     my $treeview = Gtk3::TreeView->new_with_model ($store);
     # labels column
@@ -2145,15 +2384,20 @@ sub new_hotkeys_list {
         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>');
-            my $bgcol = row_background_color (
-                $col, $model->get_value ($iter, C_ODDITY));
-            $renderer->set_property ('cell-background-rgba' => $bgcol);
+                '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 (),
@@ -2164,39 +2408,71 @@ sub new_hotkeys_list {
             my ($acckey, $accmod) = Gtk3::accelerator_parse ($hkey);
             $renderer->set_property ('accel-key' => $acckey);
             $renderer->set_property ('accel-mods' => $accmod);
-            my $bgcol = row_background_color (
-                $col, $model->get_value ($iter, C_ODDITY));
-            $renderer->set_property ('cell-background-rgba' => $bgcol);
+            $renderer->set_property (
+                '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 = Gtk3::ScrolledWindow->new ();
-    my $vbox = Gtk3::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 = Gtk3::Label->new ('<b>' . $gkey . '</b>');
-        $glabel->set_use_markup (TRUE);
-        $glabel->set_alignment (0, 0.5);
-        $glabel->set_padding (5, 1);
-        $vbox->pack_start ($glabel, FALSE, FALSE, 0);
-        # group key list
         my $keylist = new_hotkeys_list ($gkey, $group);
-        $vbox->pack_start ($keylist, FALSE, FALSE, 0);
+        # 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 $v = get_toolkit_versions ();
     my $cfgv = $CONFIGDATA->{'Common'}{'config_version'};
@@ -2204,8 +2480,8 @@ sub new_info_page() {
     return new_grid_pack (4, 11, [
         [ _('Library versions') ],
         [ new_label ('Perl-GLib'), new_title ($v->{'glib'}) ],
-        [ new_label (_('GLib runtime')), new_title ($v->{'glib-r'}) ],
-        [ new_label (_('GLib built')), new_title ($v->{'glib-b'}) ],
+        [ new_label (_('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'}) ],
@@ -2228,41 +2504,38 @@ sub get_toolkit_versions {
     }
     $versions{'gtk'} = $Gtk3::VERSION;
     if ($Gtk3::VERSION >= 0.034) {
-        $versions{'gtk-b'} = &Gtk3::GET_VERSION_INFO
+        $versions{'gtk-b'} = join('.', &Gtk3::GET_VERSION_INFO);
     } else {
         $versions{'gtk-b'} = _('Not available')
     }
-    $versions{'gtk-r'} = join('.',
-        &Gtk3::get_major_version, &Gtk3::get_minor_version, &Gtk3::get_micro_version);
+    $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->{'gtk-b'}) {
-        print _("Perl-GTK3 version {gtkv}, built for {gtkb}, running with {gtkr}.",
+        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-GTK3 version {gtkv}.", gtkv => $v->{'gtk'});
+        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
@@ -2279,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)
@@ -2405,17 +2687,17 @@ 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 "";
     }
@@ -2481,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 ();
@@ -2528,6 +2852,7 @@ sub load_preferences {
     return (load_rc_preferences ()
         and load_ac_preferences ()
         and load_hk_preferences ()
+        and load_hi_preferences ()
     );
 }
 
@@ -2536,7 +2861,6 @@ 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 ();
     return FALSE unless backup_resource ($rc);
     foreach (keys %PREFS) {
         if (defined $HPVALUE{$_}) {
@@ -2558,7 +2882,6 @@ 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 ();
     return FALSE unless backup_resource ($rc);
     foreach my $asect (keys %$ACCOUNTDATA) {
         if ($asect =~ /^Account: (\d+)$/) {
@@ -2577,16 +2900,17 @@ 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;
 }
@@ -2603,6 +2927,7 @@ sub new_notebook {
     $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;
@@ -2611,7 +2936,7 @@ sub new_notebook {
 # create an about dialog
 sub new_about_dialog {
     my ($parent) = @_;
-    my $year = '2007-2018';
+    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;
@@ -2623,7 +2948,7 @@ sub new_about_dialog {
     $dialog->set_copyright ("Copyright © $year $holder");
     $dialog->set_license_type ('gpl-3-0');
     $dialog->set_website ($url);
-    $dialog->set_website_label ($xl::s{about_web_label});
+    $dialog->set_website_label (_("Visit Clawsker's web page"));
     # committers, by number of commits
     $dialog->set_authors ([
         $holder,
@@ -2650,6 +2975,7 @@ sub new_about_dialog {
         '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>',
@@ -2663,15 +2989,17 @@ sub new_about_dialog {
 sub exit_handler {
   my ($parent) = @_;
   if ($MODIFIED != 0 and not $READONLY) {
-    my $markup = "<span>" . $xl::s{exit_fact} . "</span>\n\n"
-        . "<span weight=\"bold\">" . $xl::s{exit_question} . "</span>\n";
+    my $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, $xl::s{exit_title}, $markup, 'question',
+        $parent, _('Clawsker warning'), $markup, 'question',
         [ 'gtk-no', 1, 'gtk-yes', 0 ]
     );
     my $resp = $dialog->run;
     $dialog->hide;
-    return TRUE if ($resp == 1);
+    return TRUE if $resp;
   }
   Gtk3->main_quit;
 }
@@ -2728,24 +3056,36 @@ sub escape_key_handler {
     }
 }
 
-# initialise
-exit unless parse_command_line ();
-Gtk3->init;
-$main_window = Gtk3::Window->new ('toplevel');
-exit unless load_preferences ();
-exit unless init_hidden_preferences ();
-# create main GUI
-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 ($xl::s{win_title});
-$main_window->set_icon_list (get_app_icons ());
-$main_window->add ($box);
-$main_window->show_all;
-$MODIFIED = 0;
-Gtk3->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;