diff --git a/CMakeLists.txt b/CMakeLists.txt
index 0b9e5b8c..f4b9833f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,148 +1,148 @@
 # SPDX-License-Identifier: CC0-1.0
 # SPDX-FileCopyrightText: none
 cmake_minimum_required(VERSION 3.16 FATAL_ERROR)
-set(PIM_VERSION "6.0.41")
+set(PIM_VERSION "6.0.42")
 
 project(libkleo VERSION ${PIM_VERSION})
 
 set(KF_MIN_VERSION "5.248.0")
 
 if (WIN32)
   set(KF6_WANT_VERSION "5.104.0")
   add_compile_definitions(GPG_ERR_ENABLE_GETTEXT_MACROS=1)
 else ()
   set(KF6_WANT_VERSION ${KF_MIN_VERSION})
 endif ()
 
 find_package(ECM ${KF6_WANT_VERSION} CONFIG REQUIRED)
 set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
 set(CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules ${CMAKE_MODULE_PATH})
 
 include(KDEInstallDirs)
 include(KDECMakeSettings)
 include(KDECompilerSettings NO_POLICY_SCOPE)
 
 include(ECMGenerateExportHeader)
 include(ECMSetupVersion)
 include(ECMGenerateHeaders)
 
 include(FeatureSummary)
 include(ECMQtDeclareLoggingCategory)
 include(ECMDeprecationSettings)
 include(ECMAddQch)
 include(KDEClangFormat)
 include(KDEGitCommitHooks)
 
 option(BUILD_QCH "Build API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)" OFF)
 add_feature_info(QCH ${BUILD_QCH} "API documentation in QCH format (for e.g. Qt Assistant, Qt Creator & KDevelop)")
 
 set(LIBKLEO_LIB_VERSION ${PIM_VERSION})
 set(QT_REQUIRED_VERSION "6.6.0")
 set(GPGME_REQUIRED_VERSION "1.20.0")
 set(KTEXTADDONS_MIN_VERSION "1.5.1")
 set(GPG_ERROR_REQUIRED_VERSION "1.36")
 
 find_package(Qt6 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets)
 find_package(KF6I18n ${KF6_WANT_VERSION} CONFIG REQUIRED)
 find_package(KF6Config ${KF6_WANT_VERSION} CONFIG REQUIRED)
 find_package(KF6WidgetsAddons ${KF6_WANT_VERSION} CONFIG REQUIRED)
 find_package(KF6ColorScheme ${KF6_WANT_VERSION} CONFIG REQUIRED)
 find_package(KF6Completion ${KF6_WANT_VERSION} CONFIG REQUIRED)
 find_package(KF6CoreAddons ${KF6_WANT_VERSION} CONFIG REQUIRED)
 find_package(KF6Codecs ${KF6_WANT_VERSION} CONFIG REQUIRED)
 find_package(KF6ItemModels ${KF6_WANT_VERSION} CONFIG REQUIRED)
 find_package(KF6TextCustomEditor ${KTEXTADDONS_MIN_VERSION} CONFIG)
 
 find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
 set_package_properties(Gpgmepp PROPERTIES DESCRIPTION "GpgME++ Library" URL "https://www.gnupg.org" TYPE REQUIRED PURPOSE "GpgME++ is required for OpenPGP support")
 message(STATUS "GpgME++ Version ${Gpgmepp_VERSION}")
 set(QGPGME_NAME "QGpgmeQt6")
 if (Gpgmepp_VERSION VERSION_GREATER_EQUAL "1.23.0")
     set(GPGMEPP_KEY_HAS_HASCERTIFY_SIGN_ENCRYPT_AUTHENTICATE 1)
 endif()
 find_package(${QGPGME_NAME} ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED)
 
 find_package(LibGpgError ${GPG_ERROR_REQUIRED_VERSION} REQUIRED)
 set_package_properties(LibGpgError PROPERTIES
   TYPE REQUIRED
 )
 
 find_package(Boost 1.34.0)
 set_package_properties(Boost PROPERTIES DESCRIPTION "Boost C++ Libraries" URL "https://www.boost.org" TYPE REQUIRED PURPOSE "Boost is required for building most KDEPIM applications")
 ecm_setup_version(PROJECT VARIABLE_PREFIX LIBKLEO
                         VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/libkleo_version.h"
                         PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KPim6LibkleoConfigVersion.cmake"
                         SOVERSION 6
 )
 
 ########### Targets ###########
 ecm_set_disabled_deprecation_versions(QT 5.15.2  KF 5.248.0)
 
 remove_definitions(-DQT_NO_FOREACH)
 add_definitions(-DQT_NO_EMIT)
 ########### CMake Config Files ###########
 set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KPim6Libkleo")
 
 
 install(FILES
    ${CMAKE_CURRENT_BINARY_DIR}/libkleo_version.h
   DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim6/Libkleo COMPONENT Devel
 )
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 
 option(USE_UNITY_CMAKE_SUPPORT "Use UNITY cmake support (speedup compile time)" OFF)
 
 set(COMPILE_WITH_UNITY_CMAKE_SUPPORT OFF)
 if (USE_UNITY_CMAKE_SUPPORT)
     set(COMPILE_WITH_UNITY_CMAKE_SUPPORT ON)
     set(UNITY_BUILD ON)
 endif()
 
 configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-libkleo.h.in ${CMAKE_CURRENT_BINARY_DIR}/config-libkleo.h)
 
 add_subdirectory(src)
 if (BUILD_TESTING)
     add_subdirectory(autotests)
     add_subdirectory(tests)
 endif()
 
 ecm_qt_install_logging_categories(
         EXPORT LIBKLEO
         FILE libkleo.categories
         DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR}
         )
 
 ki18n_install(po)
 if (BUILD_QCH)
     ecm_install_qch_export(
         TARGETS KPim6Libkleo_QCH
         FILE KPim6LibkleoQchTargets.cmake
         DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
         COMPONENT Devel
     )
     set(PACKAGE_INCLUDE_QCHTARGETS "include(\"\${CMAKE_CURRENT_LIST_DIR}/KPim6LibkleoQchTargets.cmake\")")
 endif()
 
 configure_package_config_file(
   "${CMAKE_CURRENT_SOURCE_DIR}/KPimLibkleoConfig.cmake.in"
   "${CMAKE_CURRENT_BINARY_DIR}/KPim6LibkleoConfig.cmake"
   INSTALL_DESTINATION  ${CMAKECONFIG_INSTALL_DIR}
 )
 
 install(FILES
   "${CMAKE_CURRENT_BINARY_DIR}/KPim6LibkleoConfig.cmake"
   "${CMAKE_CURRENT_BINARY_DIR}/KPim6LibkleoConfigVersion.cmake"
   DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
   COMPONENT Devel
 )
 
 install(EXPORT KPim6LibkleoTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KPim6LibkleoTargets.cmake NAMESPACE KPim6::)
 
 feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
 
 # add clang-format target for all our real source files
 file(GLOB_RECURSE ALL_CLANG_FORMAT_SOURCE_FILES *.cpp *.h *.c)
 kde_clang_format(${ALL_CLANG_FORMAT_SOURCE_FILES})
 kde_configure_git_pre_commit_hook(CHECKS CLANG_FORMAT)
 
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index ab6249dd..bc348c52 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,397 +1,397 @@
 # SPDX-License-Identifier: CC0-1.0
 # SPDX-FileCopyrightText: none
 # target_include_directories does not handle empty include paths
 include_directories(
     ${Boost_INCLUDE_DIRS}
     ${GPGME_INCLUDES}
 )
 add_definitions(-DTRANSLATION_DOMAIN=\"libkleopatra6\")
 
 #add_definitions( -DQT_NO_CAST_FROM_ASCII )
 #add_definitions( -DQT_NO_CAST_TO_ASCII )
 
 kde_enable_exceptions()
 
 add_definitions( -DGPGMEPP_ERR_SOURCE_DEFAULT=13 ) # 13 is GPG_ERR_SOURCE_KLEO, even if gpg-error's too old to know about
 
 add_subdirectory( pics )
 add_library(KPim6Libkleo)
 add_library(KPim6::Libkleo ALIAS KPim6Libkleo)
 
 ########### next target ###############
 target_sources(KPim6Libkleo PRIVATE
    kleo/auditlogentry.cpp
    kleo/auditlogentry.h
    kleo/checksumdefinition.cpp
    kleo/checksumdefinition.h
    kleo/debug.cpp
    kleo/debug.h
    kleo/defaultkeyfilter.cpp
    kleo/defaultkeyfilter.h
    kleo/defaultkeygenerationjob.cpp
    kleo/defaultkeygenerationjob.h
    kleo/docaction.cpp kleo/docaction.h
    kleo/dn.cpp
    kleo/dn.h
    kleo/enum.cpp
    kleo/enum.h
    kleo/expirychecker.cpp
    kleo/expirychecker.h
    kleo/expirycheckerconfig.cpp
    kleo/expirycheckerconfig.h
    kleo/expirycheckersettings.cpp
    kleo/expirycheckersettings.h
    kleo/kconfigbasedkeyfilter.cpp
    kleo/kconfigbasedkeyfilter.h
    kleo/keyfilter.h
    kleo/keyfiltermanager.cpp
    kleo/keyfiltermanager.h
    kleo/keygroup.cpp
    kleo/keygroup.h
    kleo/keygroupconfig.cpp
    kleo/keygroupconfig.h
    kleo/keygroupimportexport.cpp
    kleo/keygroupimportexport.h
    kleo/keyresolver.cpp
    kleo/keyresolver.h
    kleo/keyresolvercore.cpp
    kleo/keyresolvercore.h
    kleo/keyserverconfig.cpp
    kleo/keyserverconfig.h
    kleo/kleoexception.cpp
    kleo/kleoexception.h
    kleo/oidmap.cpp
    kleo/oidmap.h
    kleo/predicates.h
    kleo/stl_util.h
    models/keycache.cpp
    models/keycache.h
    models/keycache_p.h
    models/keylist.h
    models/keylistmodel.cpp
    models/keylistmodel.h
    models/keylistmodelinterface.cpp
    models/keylistmodelinterface.h
    models/keylistsortfilterproxymodel.cpp
    models/keylistsortfilterproxymodel.h
    models/keyrearrangecolumnsproxymodel.cpp
    models/keyrearrangecolumnsproxymodel.h
    models/subkeylistmodel.cpp
    models/subkeylistmodel.h
    models/useridlistmodel.cpp
    models/useridlistmodel.h
    models/useridproxymodel.cpp
    models/useridproxymodel.h
    utils/algorithm.h
    utils/assuan.cpp
    utils/assuan.h
    utils/chrono.h
    utils/classify.cpp
    utils/classify.h
    utils/compat.cpp
    utils/compat.h
    utils/compliance.cpp
    utils/compliance.h
    utils/cryptoconfig.cpp
    utils/cryptoconfig.h
    utils/cryptoconfig_p.h
    utils/filesystemwatcher.cpp
    utils/filesystemwatcher.h
    utils/formatting.cpp
    utils/formatting.h
    utils/gnupg-registry.c
    utils/gnupg-registry.h
    utils/gnupg.cpp
    utils/gnupg.h
    utils/hex.cpp
    utils/hex.h
    utils/keyhelpers.cpp
    utils/keyhelpers.h
    utils/keyusage.h
    utils/qtstlhelpers.cpp
    utils/qtstlhelpers.h
    utils/scdaemon.cpp
    utils/scdaemon.h
    utils/stringutils.cpp
    utils/stringutils.h
    utils/systeminfo.cpp
    utils/systeminfo.h
    utils/test.cpp
    utils/test.h
    utils/uniquelock.cpp
    utils/uniquelock.h
    )
 ecm_qt_declare_logging_category(KPim6Libkleo HEADER libkleo_debug.h IDENTIFIER LIBKLEO_LOG CATEGORY_NAME org.kde.pim.libkleo
         DESCRIPTION "libkleo (kleo_core)"
         EXPORT LIBKLEO
     )
 
 
 target_sources(KPim6Libkleo PRIVATE
    ui/adjustingscrollarea.cpp
    ui/adjustingscrollarea.h
    ui/auditlogviewer.cpp
    ui/auditlogviewer.h
    ui/cryptoconfigentryreaderport.cpp
    ui/cryptoconfigentryreaderport_p.h
    ui/cryptoconfigmodule.cpp
    ui/cryptoconfigmodule.h
    ui/cryptoconfigmodule_p.h
    ui/directoryserviceswidget.cpp
    ui/directoryserviceswidget.h
    ui/dnattributeorderconfigwidget.cpp
    ui/dnattributeorderconfigwidget.h
    ui/editdirectoryservicedialog.cpp
    ui/editdirectoryservicedialog.h
    ui/filenamerequester.cpp
    ui/filenamerequester.h
    ui/messagebox.cpp
    ui/messagebox.h
-   ui/navigatabletreeview.cpp
-   ui/navigatabletreeview.h
-   ui/navigatabletreewidget.cpp
-   ui/navigatabletreewidget.h
+   ui/treeview.cpp
+   ui/treeview.h
+   ui/treewidget.cpp
+   ui/treewidget.h
    ui/progressbar.cpp
    ui/progressbar.h
    ui/progressdialog.cpp
    ui/progressdialog.h
    ui/readerportselection.cpp
    ui/readerportselection.h
 )
 
 ecm_qt_declare_logging_category(KPim6Libkleo HEADER kleo_ui_debug.h IDENTIFIER KLEO_UI_LOG CATEGORY_NAME org.kde.pim.kleo_ui
         DESCRIPTION "libkleo (kleo_ui)"
         OLD_CATEGORY_NAMES log_kleo_ui
         EXPORT LIBKLEO
     )
 
 
 target_sources(KPim6Libkleo PRIVATE    # make this a separate lib.
     ui/keyapprovaldialog.cpp
     ui/keyapprovaldialog.h
     ui/keylistview.cpp
     ui/keylistview.h
     ui/keyrequester.cpp
     ui/keyrequester.h
     ui/keyselectioncombo.cpp
     ui/keyselectioncombo.h
     ui/keyselectiondialog.cpp
     ui/keyselectiondialog.h
     ui/newkeyapprovaldialog.cpp
     ui/newkeyapprovaldialog.h
     )
 
 if(MINGW)
     # we do not care about different signedness of passed pointer arguments
     set_source_files_properties(utils/gnupg-registry.c PROPERTIES COMPILE_OPTIONS "-Wno-pointer-sign")
 endif()
 
 target_link_libraries(KPim6Libkleo PUBLIC Gpgmepp PRIVATE Qt::Widgets
                                                 KF6::I18n
                                                 KF6::Completion
                                                 KF6::ConfigCore
                                                 KF6::ColorScheme
                                                 KF6::ConfigGui
                                                 KF6::CoreAddons
                                                 KF6::WidgetsAddons
                                                 KF6::ItemModels
                                                 KF6::Codecs
                                                 LibGpgError::LibGpgError)
 
 target_link_libraries(KPim6Libkleo PUBLIC QGpgmeQt6)
 # Boost::headers may not be available for old versions of Boost
 if (TARGET Boost::headers)
   target_link_libraries(KPim6Libkleo PRIVATE Boost::headers)
 endif()
 
 if (TARGET KF6::TextCustomEditor)
   add_definitions(-DHAVE_PIMTEXTEDIT)
   target_link_libraries(KPim6Libkleo PRIVATE KF6::TextCustomEditor)
 endif()
 
 if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
     set_target_properties(KPim6Libkleo PROPERTIES UNITY_BUILD ON)
 endif()
 
 ecm_generate_export_header(KPim6Libkleo
     BASE_NAME kleo
     VERSION ${PIM_VERSION}
     DEPRECATED_BASE_VERSION 0
     DEPRECATION_VERSIONS 5.23
 )
 
 if(WIN32)
     target_link_libraries(KPim6Libkleo ${GPGME_VANILLA_LIBRARIES} )
 endif()
 
 set_target_properties(KPim6Libkleo PROPERTIES
     VERSION ${LIBKLEO_VERSION}
     SOVERSION ${LIBKLEO_SOVERSION}
     EXPORT_NAME Libkleo
 )
 
 install(TARGETS
     KPim6Libkleo
     EXPORT KPim6LibkleoTargets ${KDE_INSTALL_TARGETS_DEFAULT_ARGS}
 )
 
 target_include_directories(KPim6Libkleo INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR}/KPim6/Libkleo>")
 target_include_directories(KPim6Libkleo PUBLIC "$<BUILD_INTERFACE:${libkleo_SOURCE_DIR}/src;${libkleo_BINARY_DIR}/src>")
 
 ecm_generate_headers(libkleo_CamelCase_HEADERS
   HEADER_NAMES
   AuditLogEntry
   ChecksumDefinition
   Debug
   DefaultKeyFilter
   DefaultKeyGenerationJob
   DocAction
   Dn
   Enum
   ExpiryChecker
   ExpiryCheckerConfig
   ExpiryCheckerSettings
   KConfigBasedKeyFilter
   KeyFilter
   KeyFilterManager
   KeyGroup
   KeyGroupConfig
   KeyGroupImportExport
   KeyResolver
   KeyResolverCore
   KeyserverConfig
   KleoException
   OidMap
   Predicates
   Stl_Util
   REQUIRED_HEADERS libkleo_HEADERS
   PREFIX Libkleo
   RELATIVE kleo
 )
 
 ecm_generate_headers(libkleo_CamelCase_models_HEADERS
   HEADER_NAMES
   KeyCache
   KeyList
   KeyListModel
   KeyListModelInterface
   KeyListSortFilterProxyModel
   KeyRearrangeColumnsProxyModel
   SubkeyListModel
   UserIDListModel
   UserIDProxyModel
   REQUIRED_HEADERS libkleo_models_HEADERS
   PREFIX Libkleo
   RELATIVE models
 )
 
 ecm_generate_headers(libkleo_CamelCase_utils_HEADERS
   HEADER_NAMES
   Algorithm
   Assuan
   Chrono
   Classify
   Compat
   Compliance
   CryptoConfig
   FileSystemWatcher
   Formatting
   GnuPG
   Hex
   KeyHelpers
   KeyUsage
   QtStlHelpers
   SCDaemon
   StringUtils
   SystemInfo
   Test
   UniqueLock
   REQUIRED_HEADERS libkleo_utils_HEADERS
   PREFIX Libkleo
   RELATIVE utils
 )
 
 ecm_generate_headers(libkleo_CamelCase_ui_HEADERS
   HEADER_NAMES
   AdjustingScrollArea
   AuditLogViewer
   CryptoConfigModule
   DNAttributeOrderConfigWidget
   DirectoryServicesWidget
   EditDirectoryServiceDialog
   FileNameRequester
   KeyApprovalDialog
   KeyListView
   KeyRequester
   KeySelectionCombo
   KeySelectionDialog
   MessageBox
-  NavigatableTreeView
-  NavigatableTreeWidget
+  TreeView
+  TreeWidget
   NewKeyApprovalDialog
   ProgressDialog
   ReaderPortSelection
   REQUIRED_HEADERS libkleo_ui_HEADERS
   PREFIX Libkleo
   RELATIVE ui
 )
 
 kconfig_add_kcfg_files(KPim6Libkleo
   kcfg/expirycheckerconfigbase.kcfgc
   kcfg/classifyconfig.kcfgc
 )
 
 install(FILES
     ${libkleo_CamelCase_HEADERS}
     ${libkleo_CamelCase_models_HEADERS}
     ${libkleo_CamelCase_ui_HEADERS}
     ${libkleo_CamelCase_utils_HEADERS}
     DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim6/Libkleo/Libkleo
     COMPONENT Devel
 )
 
 install(FILES
     ${CMAKE_CURRENT_BINARY_DIR}/expirycheckerconfigbase.h
     ${CMAKE_CURRENT_BINARY_DIR}/kleo_export.h
     ${libkleo_HEADERS}
     ${libkleo_models_HEADERS}
     ${libkleo_ui_HEADERS}
     ${libkleo_utils_HEADERS}
     ${CMAKE_CURRENT_BINARY_DIR}/classifyconfig.h
     DESTINATION ${KDE_INSTALL_INCLUDEDIR}/KPim6/Libkleo/libkleo
     COMPONENT Devel
 )
 
 if ( WIN32 )
     install ( FILES libkleopatrarc-win32.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc )
 else ()
     install ( FILES libkleopatrarc.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc )
 endif ()
 
 if (BUILD_QCH)
     ecm_add_qch(
         KPim6Libkleo_QCH
         NAME KPim6Libkleo
         BASE_NAME KPim6Libkleo
         VERSION ${PIM_VERSION}
         ORG_DOMAIN org.kde
         SOURCES # using only public headers, to cover only public API
         ${libkleo_HEADERS}
         ${libkleo_models_HEADERS}
         ${libkleo_ui_HEADERS}
         ${libkleo_utils_HEADERS}
         #MD_MAINPAGE "${CMAKE_SOURCE_DIR}/README.md"
         #IMAGE_DIRS "${CMAKE_SOURCE_DIR}/docs/pics"
         LINK_QCHS
             Qt6Core_QCH
             Qt6Gui_QCH
             Qt6Widgets_QCH
         INCLUDE_DIRS
             ${CMAKE_CURRENT_BINARY_DIR}
         BLANK_MACROS
             KLEO_EXPORT
         TAGFILE_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
         QCH_INSTALL_DESTINATION ${KDE_INSTALL_QTQCHDIR}
         COMPONENT Devel
     )
 endif()
diff --git a/src/ui/keylistview.cpp b/src/ui/keylistview.cpp
index 0c838a46..874e332e 100644
--- a/src/ui/keylistview.cpp
+++ b/src/ui/keylistview.cpp
@@ -1,566 +1,566 @@
 /*
     keylistview.cpp
 
     This file is part of libkleopatra, the KDE keymanagement library
     SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include <config-libkleo.h>
 
 #include "keylistview.h"
 
 #include <kleo_ui_debug.h>
 
 #include <QColor>
 #include <QFont>
 #include <QFontMetrics>
 #include <QKeyEvent>
 #include <QPoint>
 #include <QTimer>
 #include <QToolTip>
 
 #include <gpgme++/key.h>
 
 #include <map>
 #include <vector>
 
 using namespace Kleo;
 
 static const int updateDelayMilliSecs = 500;
 
 class Q_DECL_HIDDEN KeyListView::KeyListViewPrivate
 {
 public:
     KeyListViewPrivate()
         : updateTimer(nullptr)
     {
     }
 
     std::vector<GpgME::Key> keyBuffer;
     QTimer *updateTimer = nullptr;
     std::map<QByteArray, KeyListViewItem *> itemMap;
 };
 
 // a list of signals where we want to replace QListViewItem with
 // Kleo:KeyListViewItem:
 static const struct {
     const char *source;
     const char *target;
 } signalReplacements[] = {
     {
         SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)),
         SLOT(slotEmitDoubleClicked(QTreeWidgetItem *, int)),
     },
     {
         SIGNAL(itemSelectionChanged()),
         SLOT(slotEmitSelectionChanged()),
     },
     {
         SIGNAL(customContextMenuRequested(QPoint)),
         SLOT(slotEmitContextMenu(QPoint)),
     },
 };
 static const int numSignalReplacements = sizeof signalReplacements / sizeof *signalReplacements;
 
 KeyListView::KeyListView(const ColumnStrategy *columnStrategy, const DisplayStrategy *displayStrategy, QWidget *parent, Qt::WindowFlags f)
-    : NavigatableTreeWidget(parent)
+    : TreeWidget(parent)
     , mColumnStrategy(columnStrategy)
     , mDisplayStrategy(displayStrategy)
     , mHierarchical(false)
     , d(new KeyListViewPrivate())
 {
     setWindowFlags(f);
     setContextMenuPolicy(Qt::CustomContextMenu);
 
     d->updateTimer = new QTimer(this);
     d->updateTimer->setSingleShot(true);
     connect(d->updateTimer, &QTimer::timeout, this, &KeyListView::slotUpdateTimeout);
     if (!columnStrategy) {
         qCWarning(KLEO_UI_LOG) << "Kleo::KeyListView: need a column strategy to work with!";
         return;
     }
 
     const QFontMetrics fm = fontMetrics();
 
     for (int col = 0; !columnStrategy->title(col).isEmpty(); ++col) {
         headerItem()->setText(col, columnStrategy->title(col));
         header()->resizeSection(col, columnStrategy->width(col, fm));
         header()->setSectionResizeMode(col, columnStrategy->resizeMode(col));
     }
 
     setAllColumnsShowFocus(false);
 
     for (int i = 0; i < numSignalReplacements; ++i) {
         connect(this, signalReplacements[i].source, signalReplacements[i].target);
     }
 
     this->setToolTip(QString());
     viewport()->setToolTip(QString()); // make double sure :)
 }
 
 KeyListView::~KeyListView()
 {
     d->updateTimer->stop();
     // need to clear here, since in ~QListView, our children won't have
     // a valid listView() pointing to us anymore, and their dtors try to
     // unregister from us.
     clear();
     Q_ASSERT(d->itemMap.size() == 0);
     // need to delete the tooltip ourselves, as ~QToolTip isn't virtual :o
     delete mColumnStrategy;
     mColumnStrategy = nullptr;
     delete mDisplayStrategy;
     mDisplayStrategy = nullptr;
 }
 
 void KeyListView::takeItem(QTreeWidgetItem *qlvi)
 {
     // qCDebug(KLEO_UI_LOG) <<"Kleo::KeyListView::takeItem(" << qlvi <<" )";
     if (auto *item = lvi_cast<KeyListViewItem>(qlvi)) {
         deregisterItem(item);
     }
     takeTopLevelItem(indexOfTopLevelItem(qlvi));
 }
 
 void KeyListView::setHierarchical(bool hier)
 {
     if (hier == mHierarchical) {
         return;
     }
     mHierarchical = hier;
     if (hier) {
         gatherScattered();
     } else {
         scatterGathered(firstChild());
     }
 }
 
 void KeyListView::slotAddKey(const GpgME::Key &key)
 {
     if (key.isNull()) {
         return;
     }
 
     d->keyBuffer.push_back(key);
     if (!d->updateTimer->isActive()) {
         d->updateTimer->start(updateDelayMilliSecs);
     }
 }
 
 void KeyListView::slotUpdateTimeout()
 {
     if (d->keyBuffer.empty()) {
         return;
     }
 
     const bool wasUpdatesEnabled = viewport()->updatesEnabled();
     if (wasUpdatesEnabled) {
         viewport()->setUpdatesEnabled(false);
     }
     qCDebug(KLEO_UI_LOG) << "Kleo::KeyListView::slotUpdateTimeout(): processing" << d->keyBuffer.size() << "items en block";
     if (hierarchical()) {
         for (std::vector<GpgME::Key>::const_iterator it = d->keyBuffer.begin(); it != d->keyBuffer.end(); ++it) {
             doHierarchicalInsert(*it);
         }
         gatherScattered();
     } else {
         for (std::vector<GpgME::Key>::const_iterator it = d->keyBuffer.begin(); it != d->keyBuffer.end(); ++it) {
             (void)new KeyListViewItem(this, *it);
         }
     }
     if (wasUpdatesEnabled) {
         viewport()->setUpdatesEnabled(true);
     }
     d->keyBuffer.clear();
 }
 
 void KeyListView::clear()
 {
     d->updateTimer->stop();
     d->keyBuffer.clear();
     while (QTreeWidgetItem *item = topLevelItem(0)) {
         delete item;
     }
     QTreeWidget::clear();
 }
 
 void KeyListView::registerItem(KeyListViewItem *item)
 {
     // qCDebug(KLEO_UI_LOG) <<"registerItem(" << item <<" )";
     if (!item) {
         return;
     }
     const QByteArray fpr = item->key().primaryFingerprint();
     if (!fpr.isEmpty()) {
         d->itemMap.insert(std::make_pair(fpr, item));
     }
 }
 
 void KeyListView::deregisterItem(const KeyListViewItem *item)
 {
     // qCDebug(KLEO_UI_LOG) <<"deregisterItem( KeyLVI:" << item <<" )";
     if (!item) {
         return;
     }
     auto it = d->itemMap.find(item->key().primaryFingerprint());
     if (it == d->itemMap.end()) {
         return;
     }
     // This Q_ASSERT triggers, though it shouldn't. Print some more
     // information when it happens.
     // Q_ASSERT( it->second == item );
     if (it->second != item) {
         qCWarning(KLEO_UI_LOG) << "deregisterItem:"
                                << "item      " << item->key().primaryFingerprint() //
                                << "it->second" << (it->second ? it->second->key().primaryFingerprint() : "is null");
         return;
     }
     d->itemMap.erase(it);
 }
 
 void KeyListView::doHierarchicalInsert(const GpgME::Key &key)
 {
     const QByteArray fpr = key.primaryFingerprint();
     if (fpr.isEmpty()) {
         return;
     }
     KeyListViewItem *item = nullptr;
     if (!key.isRoot()) {
         if (KeyListViewItem *parent = itemByFingerprint(key.chainID())) {
             item = new KeyListViewItem(parent, key);
             parent->setExpanded(true);
         }
     }
     if (!item) {
         item = new KeyListViewItem(this, key); // top-level (for now)
     }
 
     d->itemMap.insert(std::make_pair(fpr, item));
 }
 
 void KeyListView::gatherScattered()
 {
     KeyListViewItem *item = firstChild();
     while (item) {
         KeyListViewItem *cur = item;
         item = item->nextSibling();
         if (cur->key().isRoot()) {
             continue;
         }
         if (KeyListViewItem *parent = itemByFingerprint(cur->key().chainID())) {
             // found a new parent...
             // ### todo: optimize by suppressing removing/adding the item to the itemMap...
             takeTopLevelItem(indexOfTopLevelItem(cur));
             parent->addChild(cur);
             parent->setExpanded(true);
         }
     }
 }
 
 void KeyListView::scatterGathered(KeyListViewItem *start)
 {
     KeyListViewItem *item = start;
     while (item) {
         KeyListViewItem *cur = item;
         item = item->nextSibling();
 
         scatterGathered(lvi_cast<KeyListViewItem>(cur->child(0)));
         Q_ASSERT(cur->childCount() == 0);
 
         // ### todo: optimize by suppressing removing/adding the item to the itemMap...
         if (cur->parent()) {
             static_cast<KeyListViewItem *>(cur->parent())->takeItem(cur);
         } else {
             takeItem(cur);
         }
         addTopLevelItem(cur);
     }
 }
 
 KeyListViewItem *KeyListView::itemByFingerprint(const QByteArray &s) const
 {
     if (s.isEmpty()) {
         return nullptr;
     }
     const std::map<QByteArray, KeyListViewItem *>::const_iterator it = d->itemMap.find(s);
     if (it == d->itemMap.end()) {
         return nullptr;
     }
     return it->second;
 }
 
 void KeyListView::slotRefreshKey(const GpgME::Key &key)
 {
     const char *fpr = key.primaryFingerprint();
     if (!fpr) {
         return;
     }
     if (KeyListViewItem *item = itemByFingerprint(fpr)) {
         item->setKey(key);
     } else {
         // none found -> add it
         slotAddKey(key);
     }
 }
 
 // slots for the emission of covariant Q_SIGNALS:
 
 void KeyListView::slotEmitDoubleClicked(QTreeWidgetItem *item, int col)
 {
     if (!item || lvi_cast<KeyListViewItem>(item)) {
         Q_EMIT doubleClicked(static_cast<KeyListViewItem *>(item), col);
     }
 }
 
 void KeyListView::slotEmitReturnPressed(QTreeWidgetItem *item)
 {
     if (!item || lvi_cast<KeyListViewItem>(item)) {
         Q_EMIT returnPressed(static_cast<KeyListViewItem *>(item));
     }
 }
 
 void KeyListView::slotEmitSelectionChanged()
 {
     Q_EMIT selectionChanged(selectedItem());
 }
 
 void KeyListView::slotEmitContextMenu(const QPoint &pos)
 {
     QTreeWidgetItem *item = itemAt(pos);
     if (!item || lvi_cast<KeyListViewItem>(item)) {
         Q_EMIT contextMenu(static_cast<KeyListViewItem *>(item), viewport()->mapToGlobal(pos));
     }
 }
 
 //
 //
 // KeyListViewItem
 //
 //
 
 KeyListViewItem::KeyListViewItem(KeyListView *parent, const GpgME::Key &key)
     : QTreeWidgetItem(parent, RTTI)
 {
     Q_ASSERT(parent);
     setKey(key);
 }
 
 KeyListViewItem::KeyListViewItem(KeyListView *parent, KeyListViewItem *after, const GpgME::Key &key)
     : QTreeWidgetItem(parent, after, RTTI)
 {
     Q_ASSERT(parent);
     setKey(key);
 }
 
 KeyListViewItem::KeyListViewItem(KeyListViewItem *parent, const GpgME::Key &key)
     : QTreeWidgetItem(parent, RTTI)
 {
     Q_ASSERT(parent && parent->listView());
     setKey(key);
 }
 
 KeyListViewItem::KeyListViewItem(KeyListViewItem *parent, KeyListViewItem *after, const GpgME::Key &key)
     : QTreeWidgetItem(parent, after, RTTI)
 {
     Q_ASSERT(parent && parent->listView());
     setKey(key);
 }
 
 KeyListViewItem::~KeyListViewItem()
 {
     // delete the children first... When children are deleted in the
     // QLVI dtor, they don't have listView() anymore, thus they don't
     // call deregister( this ), leading to stale entries in the
     // itemMap...
     while (QTreeWidgetItem *item = child(0)) {
         delete item;
     }
     // better do this here, too, since deletion is top-down and thus
     // we're deleted when our parent item is no longer a
     // KeyListViewItem, but a mere QListViewItem, so our takeItem()
     // overload is gone by that time...
     if (KeyListView *lv = listView()) {
         lv->deregisterItem(this);
     }
 }
 
 void KeyListViewItem::setKey(const GpgME::Key &key)
 {
     KeyListView *lv = listView();
     if (lv) {
         lv->deregisterItem(this);
     }
     mKey = key;
     if (lv) {
         lv->registerItem(this);
     }
 
     // the ColumnStrategy operations might be very slow, so cache their
     // result here, where we're non-const :)
     const KeyListView::ColumnStrategy *cs = lv ? lv->columnStrategy() : nullptr;
     if (!cs) {
         return;
     }
     const KeyListView::DisplayStrategy *ds = lv->displayStrategy();
     const int numCols = lv ? lv->columnCount() : 0;
     for (int i = 0; i < numCols; ++i) {
         setText(i, cs->text(key, i));
         const auto accessibleText = cs->accessibleText(key, i);
         if (!accessibleText.isEmpty()) {
             setData(i, Qt::AccessibleTextRole, accessibleText);
         }
         setToolTip(i, cs->toolTip(key, i));
         const QIcon icon = cs->icon(key, i);
         if (!icon.isNull()) {
             setIcon(i, icon);
         }
         if (ds) {
             setForeground(i, QBrush(ds->keyForeground(key, foreground(i).color())));
             setBackground(i, QBrush(ds->keyBackground(key, background(i).color())));
             setFont(i, ds->keyFont(key, font(i)));
         }
     }
 }
 
 QString KeyListViewItem::toolTip(int col) const
 {
     return listView() && listView()->columnStrategy() ? listView()->columnStrategy()->toolTip(key(), col) : QString();
 }
 
 bool KeyListViewItem::operator<(const QTreeWidgetItem &other) const
 {
     if (other.type() != RTTI || !listView() || !listView()->columnStrategy()) {
         return QTreeWidgetItem::operator<(other);
     }
     const auto that = static_cast<const KeyListViewItem *>(&other);
     return listView()->columnStrategy()->compare(this->key(), that->key(), treeWidget()->sortColumn()) < 0;
 }
 
 void KeyListViewItem::takeItem(QTreeWidgetItem *qlvi)
 {
     // qCDebug(KLEO_UI_LOG) <<"Kleo::KeyListViewItem::takeItem(" << qlvi <<" )";
     if (auto *item = lvi_cast<KeyListViewItem>(qlvi)) {
         listView()->deregisterItem(item);
     }
     takeChild(indexOfChild(qlvi));
 }
 
 //
 //
 // ColumnStrategy
 //
 //
 
 KeyListView::ColumnStrategy::~ColumnStrategy()
 {
 }
 
 int KeyListView::ColumnStrategy::compare(const GpgME::Key &key1, const GpgME::Key &key2, const int col) const
 {
     return QString::localeAwareCompare(text(key1, col), text(key2, col));
 }
 
 int KeyListView::ColumnStrategy::width(int col, const QFontMetrics &fm) const
 {
     return fm.horizontalAdvance(title(col)) * 2;
 }
 
 QString KeyListView::ColumnStrategy::toolTip(const GpgME::Key &key, int col) const
 {
     return text(key, col);
 }
 
 //
 //
 // DisplayStrategy
 //
 //
 
 KeyListView::DisplayStrategy::~DisplayStrategy()
 {
 }
 
 // font
 QFont KeyListView::DisplayStrategy::keyFont(const GpgME::Key &, const QFont &font) const
 {
     return font;
 }
 
 // foreground
 QColor KeyListView::DisplayStrategy::keyForeground(const GpgME::Key &, const QColor &fg) const
 {
     return fg;
 }
 
 // background
 QColor KeyListView::DisplayStrategy::keyBackground(const GpgME::Key &, const QColor &bg) const
 {
     return bg;
 }
 
 //
 //
 // Collection of covariant return reimplementations of QListView(Item)
 // members:
 //
 //
 
 KeyListView *KeyListViewItem::listView() const
 {
     return static_cast<KeyListView *>(QTreeWidgetItem::treeWidget());
 }
 
 KeyListViewItem *KeyListViewItem::nextSibling() const
 {
     if (parent()) {
         const int myIndex = parent()->indexOfChild(const_cast<KeyListViewItem *>(this));
         return static_cast<KeyListViewItem *>(parent()->child(myIndex + 1));
     }
     const int myIndex = treeWidget()->indexOfTopLevelItem(const_cast<KeyListViewItem *>(this));
     return static_cast<KeyListViewItem *>(treeWidget()->topLevelItem(myIndex + 1));
 }
 
 KeyListViewItem *KeyListView::firstChild() const
 {
     return static_cast<KeyListViewItem *>(topLevelItem(0));
 }
 
 KeyListViewItem *KeyListView::selectedItem() const
 {
     QList<KeyListViewItem *> selection = selectedItems();
     if (selection.isEmpty()) {
         return nullptr;
     }
     return selection.first();
 }
 
 QList<KeyListViewItem *> KeyListView::selectedItems() const
 {
     QList<KeyListViewItem *> result;
     const auto selectedItems = QTreeWidget::selectedItems();
     for (QTreeWidgetItem *selectedItem : selectedItems) {
         if (auto *i = lvi_cast<KeyListViewItem>(selectedItem)) {
             result.append(i);
         }
     }
     return result;
 }
 
 bool KeyListView::isMultiSelection() const
 {
     return selectionMode() == ExtendedSelection || selectionMode() == MultiSelection;
 }
 
 void KeyListView::keyPressEvent(QKeyEvent *event)
 {
     if (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter) {
         if (selectedItem()) {
             slotEmitReturnPressed(selectedItem());
         }
     }
     QTreeView::keyPressEvent(event);
 }
 
 #include "moc_keylistview.cpp"
diff --git a/src/ui/keylistview.h b/src/ui/keylistview.h
index 5469672a..fd5134b0 100644
--- a/src/ui/keylistview.h
+++ b/src/ui/keylistview.h
@@ -1,199 +1,199 @@
 /*
     keylistview.h
 
     This file is part of libkleopatra, the KDE keymanagement library
     SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #pragma once
 
 #include "kleo_export.h"
 
-#include "navigatabletreewidget.h"
+#include "treewidget.h"
 
 #include <QByteArray>
 #include <QHeaderView>
 #include <QIcon>
 
 #include <gpgme++/key.h>
 
 class QFont;
 class QColor;
 
 namespace Kleo
 {
 
 // work around moc parser bug...
 #define TEMPLATE_TYPENAME(T) template<typename T>
 TEMPLATE_TYPENAME(T)
 inline T *lvi_cast(QTreeWidgetItem *item)
 {
     return item && (item->type() == T::RTTI) ? static_cast<T *>(item) : nullptr;
 }
 
 TEMPLATE_TYPENAME(T)
 inline const T *lvi_cast(const QTreeWidgetItem *item)
 {
     return item && (item->type() == T::RTTI) ? static_cast<const T *>(item) : nullptr;
 }
 #undef TEMPLATE_TYPENAME
 
 class KeyListView;
 
 class KeyListViewItem : public QTreeWidgetItem
 {
 public:
     KeyListViewItem(KeyListView *parent, const GpgME::Key &key);
     KeyListViewItem(KeyListView *parent, KeyListViewItem *after, const GpgME::Key &key);
     KeyListViewItem(KeyListViewItem *parent, const GpgME::Key &key);
     KeyListViewItem(KeyListViewItem *parent, KeyListViewItem *after, const GpgME::Key &key);
     ~KeyListViewItem() override;
 
     void setKey(const GpgME::Key &key);
     const GpgME::Key &key() const
     {
         return mKey;
     }
 
     enum { RTTI = QTreeWidgetItem::UserType + 1 };
 
     //
     // only boring stuff below:
     //
     virtual QString toolTip(int column) const;
 
     /*! \reimp for covariant return */
     KeyListView *listView() const;
     /*! \reimp for covariant return */
     KeyListViewItem *nextSibling() const;
     /*! \reimp */
     bool operator<(const QTreeWidgetItem &other) const override;
     /*! \reimp */
     void takeItem(QTreeWidgetItem *item);
 
 private:
     GpgME::Key mKey;
 };
 
-class KLEO_EXPORT KeyListView : public NavigatableTreeWidget
+class KLEO_EXPORT KeyListView : public TreeWidget
 {
     Q_OBJECT
     friend class KeyListViewItem;
 
 public:
     class KLEO_EXPORT ColumnStrategy
     {
     public:
         virtual ~ColumnStrategy();
         virtual QString title(int column) const = 0;
         virtual int width(int column, const QFontMetrics &fm) const;
         virtual QHeaderView::ResizeMode resizeMode(int) const
         {
             return QHeaderView::Interactive;
         }
 
         virtual QString text(const GpgME::Key &key, int column) const = 0;
         virtual QString accessibleText(const GpgME::Key &key, int column) const = 0;
         virtual QString toolTip(const GpgME::Key &key, int column) const;
         virtual QIcon icon(const GpgME::Key &, int) const
         {
             return QIcon();
         }
         virtual int compare(const GpgME::Key &key1, const GpgME::Key &key2, const int column) const;
     };
 
     class KLEO_EXPORT DisplayStrategy
     {
     public:
         virtual ~DisplayStrategy();
         // font
         virtual QFont keyFont(const GpgME::Key &, const QFont &) const;
         // foreground
         virtual QColor keyForeground(const GpgME::Key &, const QColor &) const;
         // background
         virtual QColor keyBackground(const GpgME::Key &, const QColor &) const;
     };
 
     explicit KeyListView(const ColumnStrategy *strategy, const DisplayStrategy *display = nullptr, QWidget *parent = nullptr, Qt::WindowFlags f = {});
 
     ~KeyListView() override;
 
     const ColumnStrategy *columnStrategy() const
     {
         return mColumnStrategy;
     }
     const DisplayStrategy *displayStrategy() const
     {
         return mDisplayStrategy;
     }
 
     bool hierarchical() const
     {
         return mHierarchical;
     }
     virtual void setHierarchical(bool hier);
 
     void flushKeys()
     {
         slotUpdateTimeout();
     }
 
     bool isMultiSelection() const;
 
     KeyListViewItem *itemByFingerprint(const QByteArray &) const;
 
 public:
     using QTreeWidget::selectionChanged; // for below, but moc doesn't like it to be in the Q_SIGNALS: section
 Q_SIGNALS:
     void doubleClicked(Kleo::KeyListViewItem *, int);
     void returnPressed(Kleo::KeyListViewItem *);
     void selectionChanged(Kleo::KeyListViewItem *);
     void contextMenu(Kleo::KeyListViewItem *, const QPoint &);
 
 protected:
     void keyPressEvent(QKeyEvent *event) override;
 
 public Q_SLOTS:
     virtual void slotAddKey(const GpgME::Key &key);
     virtual void slotRefreshKey(const GpgME::Key &key);
 
     //
     // Only boring stuff below:
     //
 private Q_SLOTS:
     void slotEmitDoubleClicked(QTreeWidgetItem *, int);
     void slotEmitReturnPressed(QTreeWidgetItem *);
     void slotEmitSelectionChanged();
     void slotEmitContextMenu(const QPoint &pos);
     void slotUpdateTimeout();
 
 public:
     /*! \reimp for covariant return */
     KeyListViewItem *selectedItem() const;
     /*! \reimp */
     QList<KeyListViewItem *> selectedItems() const;
     /*! \reimp for covariant return */
     KeyListViewItem *firstChild() const;
     /*! \reimp */
     void clear();
     /*! \reimp */
     void takeItem(QTreeWidgetItem *);
 
 private:
     void doHierarchicalInsert(const GpgME::Key &);
     void gatherScattered();
     void scatterGathered(KeyListViewItem *);
     void registerItem(KeyListViewItem *);
     void deregisterItem(const KeyListViewItem *);
 
 private:
     const ColumnStrategy *mColumnStrategy = nullptr;
     const DisplayStrategy *mDisplayStrategy = nullptr;
     bool mHierarchical = false;
 
     class KeyListViewPrivate;
     std::unique_ptr<KeyListViewPrivate> const d;
 };
 }
diff --git a/src/ui/navigatabletreeview.cpp b/src/ui/navigatabletreeview.cpp
deleted file mode 100644
index c3268bcf..00000000
--- a/src/ui/navigatabletreeview.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-    ui/navigatabletreeview.cpp
-
-    This file is part of libkleopatra
-    SPDX-FileCopyrightText: 2022 g10 Code GmbH
-    SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
-    SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include <config-libkleo.h>
-
-#include "navigatabletreeview.h"
-
-using namespace Kleo;
-
-QModelIndex NavigatableTreeView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
-{
-    // make column by column keyboard navigation with Left/Right possible by switching
-    // the selection behavior to SelectItems before calling the parent class's moveCursor,
-    // because it ignores MoveLeft/MoveRight if the selection behavior is SelectRows;
-    // moreover, temporarily disable exanding of items to prevent expanding/collapsing
-    // on MoveLeft/MoveRight
-    if ((cursorAction != MoveLeft) && (cursorAction != MoveRight)) {
-        return QTreeView::moveCursor(cursorAction, modifiers);
-    }
-
-    const auto savedSelectionBehavior = selectionBehavior();
-    setSelectionBehavior(SelectItems);
-    const auto savedItemsExpandable = itemsExpandable();
-    setItemsExpandable(false);
-
-    const auto result = QTreeView::moveCursor(cursorAction, modifiers);
-
-    setItemsExpandable(savedItemsExpandable);
-    setSelectionBehavior(savedSelectionBehavior);
-
-    return result;
-}
-
-#include "moc_navigatabletreeview.cpp"
diff --git a/src/ui/navigatabletreewidget.cpp b/src/ui/navigatabletreewidget.cpp
deleted file mode 100644
index b9391abb..00000000
--- a/src/ui/navigatabletreewidget.cpp
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
-    ui/navigatabletreewidget.cpp
-
-    This file is part of libkleopatra
-    SPDX-FileCopyrightText: 2022 g10 Code GmbH
-    SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
-
-    SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include <config-libkleo.h>
-
-#include "navigatabletreewidget.h"
-
-using namespace Kleo;
-
-QModelIndex NavigatableTreeWidget::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
-{
-    // make column by column keyboard navigation with Left/Right possible by switching
-    // the selection behavior to SelectItems before calling the parent class's moveCursor,
-    // because it ignores MoveLeft/MoveRight if the selection behavior is SelectRows;
-    // moreover, temporarily disable exanding of items to prevent expanding/collapsing
-    // on MoveLeft/MoveRight
-    if ((cursorAction != MoveLeft) && (cursorAction != MoveRight)) {
-        return QTreeWidget::moveCursor(cursorAction, modifiers);
-    }
-
-    const auto savedSelectionBehavior = selectionBehavior();
-    setSelectionBehavior(SelectItems);
-    const auto savedItemsExpandable = itemsExpandable();
-    setItemsExpandable(false);
-
-    const auto result = QTreeWidget::moveCursor(cursorAction, modifiers);
-
-    setItemsExpandable(savedItemsExpandable);
-    setSelectionBehavior(savedSelectionBehavior);
-
-    return result;
-}
-
-#include "moc_navigatabletreewidget.cpp"
diff --git a/src/ui/treeview.cpp b/src/ui/treeview.cpp
new file mode 100644
index 00000000..bbe57851
--- /dev/null
+++ b/src/ui/treeview.cpp
@@ -0,0 +1,110 @@
+/*
+    ui/treeview.cpp
+
+    This file is part of libkleopatra
+    SPDX-FileCopyrightText: 2022 g10 Code GmbH
+    SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include <config-libkleo.h>
+
+#include "treeview.h"
+
+#include <KLocalizedString>
+
+#include <QContextMenuEvent>
+#include <QHeaderView>
+#include <QMenu>
+
+using namespace Kleo;
+
+class TreeView::Private
+{
+public:
+    Private()
+    {
+    }
+    QMenu *mHeaderPopup = nullptr;
+    QList<QAction *> mColumnActions;
+};
+
+TreeView::TreeView(QWidget *parent)
+    : QTreeView::QTreeView(parent)
+    , d{new Private}
+{
+    header()->installEventFilter(this);
+}
+
+TreeView::~TreeView() = default;
+
+bool TreeView::eventFilter(QObject *watched, QEvent *event)
+{
+    Q_UNUSED(watched)
+    if (event->type() == QEvent::ContextMenu) {
+        auto e = static_cast<QContextMenuEvent *>(event);
+
+        if (!d->mHeaderPopup) {
+            d->mHeaderPopup = new QMenu(this);
+            d->mHeaderPopup->setTitle(i18n("View Columns"));
+            for (int i = 0; i < model()->columnCount(); ++i) {
+                QAction *tmp = d->mHeaderPopup->addAction(model()->headerData(i, Qt::Horizontal).toString());
+                tmp->setData(QVariant(i));
+                tmp->setCheckable(true);
+                d->mColumnActions << tmp;
+            }
+
+            connect(d->mHeaderPopup, &QMenu::triggered, this, [this](QAction *action) {
+                const int col = action->data().toInt();
+                if (action->isChecked()) {
+                    showColumn(col);
+                } else {
+                    hideColumn(col);
+                }
+
+                if (action->isChecked()) {
+                    Q_EMIT columnEnabled(col);
+                } else {
+                    Q_EMIT columnDisabled(col);
+                }
+            });
+        }
+
+        for (QAction *action : std::as_const(d->mColumnActions)) {
+            const int column = action->data().toInt();
+            action->setChecked(!isColumnHidden(column));
+        }
+
+        d->mHeaderPopup->popup(mapToGlobal(e->pos()));
+        return true;
+    }
+
+    return false;
+}
+
+QModelIndex TreeView::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+{
+    // make column by column keyboard navigation with Left/Right possible by switching
+    // the selection behavior to SelectItems before calling the parent class's moveCursor,
+    // because it ignores MoveLeft/MoveRight if the selection behavior is SelectRows;
+    // moreover, temporarily disable exanding of items to prevent expanding/collapsing
+    // on MoveLeft/MoveRight
+    if ((cursorAction != MoveLeft) && (cursorAction != MoveRight)) {
+        return QTreeView::moveCursor(cursorAction, modifiers);
+    }
+
+    const auto savedSelectionBehavior = selectionBehavior();
+    setSelectionBehavior(SelectItems);
+    const auto savedItemsExpandable = itemsExpandable();
+    setItemsExpandable(false);
+
+    const auto result = QTreeView::moveCursor(cursorAction, modifiers);
+
+    setItemsExpandable(savedItemsExpandable);
+    setSelectionBehavior(savedSelectionBehavior);
+
+    return result;
+}
+
+#include "moc_treeview.cpp"
diff --git a/src/ui/navigatabletreeview.h b/src/ui/treeview.h
similarity index 68%
rename from src/ui/navigatabletreeview.h
rename to src/ui/treeview.h
index 1b10408c..5d20c7ba 100644
--- a/src/ui/navigatabletreeview.h
+++ b/src/ui/treeview.h
@@ -1,48 +1,59 @@
 /*
-    ui/navigatabletreeview.h
+    ui/treeview.h
 
     This file is part of libkleopatra
     SPDX-FileCopyrightText: 2022 g10 Code GmbH
     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #pragma once
 
 #include "kleo_export.h"
 
 #include <QTreeView>
 
 namespace Kleo
 {
 
 /**
- * A tree view that allows accessible column by column keyboard navigation.
+ * A tree view that allows accessible column by column keyboard navigation
+ * and that has customizable columns through a context menu in the header.
  *
  * Column by column navigation is required to make a tree view accessible.
  *
- * The NavigatableTreeView allows column by column keyboard navigation even if
+ * The TreeView allows column by column keyboard navigation even if
  * the selection behavior is set to SelectRows and users can expand/collapse
  * list items. To achieve this it deactivates the standard behavior of QTreeView
  * to expand/collapse items if the left/right arrow keys are used.
  *
  * Additionally, you may want to disable parent-child navigation in tree views
  * with left/right arrow keys because this also interferes with column by column
  * navigation. You can do this by setting
  * "QTreeView { arrow-keys-navigate-into-children: 0; }"
  * as application style sheet.
  *
- * \sa NavigatableTreeWidget
+ * \sa TreeWidget
  */
-class KLEO_EXPORT NavigatableTreeView : public QTreeView
+class KLEO_EXPORT TreeView : public QTreeView
 {
     Q_OBJECT
 public:
-    using QTreeView::QTreeView;
+    TreeView(QWidget *parent = nullptr);
+    ~TreeView();
+
+Q_SIGNALS:
+    void columnEnabled(int column);
+    void columnDisabled(int column);
 
 protected:
+    bool eventFilter(QObject *watched, QEvent *event) override;
+
     QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
-};
 
+private:
+    class Private;
+    const std::unique_ptr<Private> d;
+};
 }
diff --git a/src/ui/treewidget.cpp b/src/ui/treewidget.cpp
new file mode 100644
index 00000000..17fd69ed
--- /dev/null
+++ b/src/ui/treewidget.cpp
@@ -0,0 +1,107 @@
+/*
+    ui/treewidget.cpp
+
+    This file is part of libkleopatra
+    SPDX-FileCopyrightText: 2022 g10 Code GmbH
+    SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include <config-libkleo.h>
+
+#include "treewidget.h"
+
+#include <KLocalizedString>
+
+#include <QContextMenuEvent>
+#include <QHeaderView>
+#include <QMenu>
+
+using namespace Kleo;
+
+class TreeWidget::Private
+{
+public:
+    QMenu *mHeaderPopup = nullptr;
+    QList<QAction *> mColumnActions;
+};
+
+TreeWidget::TreeWidget(QWidget *parent)
+    : QTreeWidget::QTreeWidget(parent)
+    , d{new Private}
+{
+    header()->installEventFilter(this);
+}
+
+TreeWidget::~TreeWidget() = default;
+
+bool TreeWidget::eventFilter(QObject *watched, QEvent *event)
+{
+    Q_UNUSED(watched)
+    if (event->type() == QEvent::ContextMenu) {
+        auto e = static_cast<QContextMenuEvent *>(event);
+
+        if (!d->mHeaderPopup) {
+            d->mHeaderPopup = new QMenu(this);
+            d->mHeaderPopup->setTitle(i18n("View Columns"));
+            for (int i = 0; i < model()->columnCount(); ++i) {
+                QAction *tmp = d->mHeaderPopup->addAction(model()->headerData(i, Qt::Horizontal).toString());
+                tmp->setData(QVariant(i));
+                tmp->setCheckable(true);
+                d->mColumnActions << tmp;
+            }
+
+            connect(d->mHeaderPopup, &QMenu::triggered, this, [this](QAction *action) {
+                const int col = action->data().toInt();
+                if (action->isChecked()) {
+                    showColumn(col);
+                } else {
+                    hideColumn(col);
+                }
+
+                if (action->isChecked()) {
+                    Q_EMIT columnEnabled(col);
+                } else {
+                    Q_EMIT columnDisabled(col);
+                }
+            });
+        }
+
+        for (QAction *action : std::as_const(d->mColumnActions)) {
+            const int column = action->data().toInt();
+            action->setChecked(!isColumnHidden(column));
+        }
+
+        d->mHeaderPopup->popup(mapToGlobal(e->pos()));
+        return true;
+    }
+
+    return false;
+}
+
+QModelIndex TreeWidget::moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
+{
+    // make column by column keyboard navigation with Left/Right possible by switching
+    // the selection behavior to SelectItems before calling the parent class's moveCursor,
+    // because it ignores MoveLeft/MoveRight if the selection behavior is SelectRows;
+    // moreover, temporarily disable exanding of items to prevent expanding/collapsing
+    // on MoveLeft/MoveRight
+    if ((cursorAction != MoveLeft) && (cursorAction != MoveRight)) {
+        return QTreeWidget::moveCursor(cursorAction, modifiers);
+    }
+
+    const auto savedSelectionBehavior = selectionBehavior();
+    setSelectionBehavior(SelectItems);
+    const auto savedItemsExpandable = itemsExpandable();
+    setItemsExpandable(false);
+
+    const auto result = QTreeWidget::moveCursor(cursorAction, modifiers);
+
+    setItemsExpandable(savedItemsExpandable);
+    setSelectionBehavior(savedSelectionBehavior);
+
+    return result;
+}
+
+#include "moc_treewidget.cpp"
diff --git a/src/ui/navigatabletreewidget.h b/src/ui/treewidget.h
similarity index 50%
rename from src/ui/navigatabletreewidget.h
rename to src/ui/treewidget.h
index a4eacf1e..8e6d9a7a 100644
--- a/src/ui/navigatabletreewidget.h
+++ b/src/ui/treewidget.h
@@ -1,37 +1,49 @@
 /*
-    ui/navigatabletreewidget.h
+    ui/treewidget.h
 
     This file is part of libkleopatra
     SPDX-FileCopyrightText: 2022 g10 Code GmbH
     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #pragma once
 
 #include "kleo_export.h"
 
 #include <QTreeWidget>
 
 namespace Kleo
 {
 
 /**
- * A tree widget that allows accessible column by column keyboard navigation.
+ * A tree widget that allows accessible column by column keyboard navigation
+ * and that has customizable columns through a context menu in the header.
  *
- * This is the QTreeWidget-derived variant of NavigatableTreeView.
+ * This is the QTreeWidget-derived variant of TreeView.
  *
- * \sa NavigatableTreeView
+ * \sa TreeView
  */
-class KLEO_EXPORT NavigatableTreeWidget : public QTreeWidget
+class KLEO_EXPORT TreeWidget : public QTreeWidget
 {
     Q_OBJECT
 public:
-    using QTreeWidget::QTreeWidget;
+    TreeWidget(QWidget *parent = nullptr);
+    ~TreeWidget();
+
+Q_SIGNALS:
+    void columnEnabled(int column);
+    void columnDisabled(int column);
 
 protected:
+    bool eventFilter(QObject *watched, QEvent *event) override;
+
     QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
+
+private:
+    class Private;
+    const std::unique_ptr<Private> d;
 };
 
 }