diff --git a/CMakeLists.txt b/CMakeLists.txt index 8966e5f8e..8d7414815 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,266 +1,266 @@ # SPDX-FileCopyrightText: none # SPDX-License-Identifier: BSD-3-Clause cmake_minimum_required(VERSION 3.16 FATAL_ERROR) set(RELEASE_SERVICE_VERSION_MAJOR "23") set(RELEASE_SERVICE_VERSION_MINOR "03") set(RELEASE_SERVICE_VERSION_MICRO "70") # The RELEASE_SERVICE_VERSION is used by Gpg4win to add the Gpg4win version if (NOT RELEASE_SERVICE_VERSION) set(RELEASE_SERVICE_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}.${RELEASE_SERVICE_VERSION_MINOR}.${RELEASE_SERVICE_VERSION_MICRO}") endif() if(RELEASE_SERVICE_VERSION_MICRO LESS 10) set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}0${RELEASE_SERVICE_VERSION_MICRO}") else() set(KDE_APPLICATIONS_COMPACT_VERSION "${RELEASE_SERVICE_VERSION_MAJOR}${RELEASE_SERVICE_VERSION_MINOR}${RELEASE_SERVICE_VERSION_MICRO}") endif() set(KLEOPATRA_VERSION_MAJOR "3") set(KLEOPATRA_VERSION_MINOR "1") set(KLEOPATRA_VERSION_MICRO "26") set(kleopatra_version "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}.${KDE_APPLICATIONS_COMPACT_VERSION}") # The following is for Windows set(kleopatra_version_win "${KLEOPATRA_VERSION_MAJOR}.${KLEOPATRA_VERSION_MINOR}.${KLEOPATRA_VERSION_MICRO}") set(kleopatra_fileversion_win "${KLEOPATRA_VERSION_MAJOR},${KLEOPATRA_VERSION_MINOR},${KLEOPATRA_VERSION_MICRO},0") if (NOT KLEOPATRA_DISTRIBUTION_TEXT) # This is only used on Windows for the file attributes of Kleopatra set(KLEOPATRA_DISTRIBUTION_TEXT "KDE") endif() project(kleopatra VERSION ${kleopatra_version}) option(DISABLE_KWATCHGNUPG "Don't build the kwatchgnupg tool [default=OFF]" OFF) # Standalone build. Find / include everything necessary. set(KF_MIN_VERSION "5.102.0") set(KIDENTITYMANAGEMENT_VERSION "5.22.40") set(KMAILTRANSPORT_VERSION "5.22.40") set(KMIME_VERSION "5.22.40") set(LIBKLEO_VERSION "5.22.45") set(QT_REQUIRED_VERSION "5.15.2") if (QT_MAJOR_VERSION STREQUAL "6") set(QT_REQUIRED_VERSION "6.4.0") endif() set(GPGME_REQUIRED_VERSION "1.16.0") set(LIBASSUAN_REQUIRED_VERSION "2.4.2") set(GPG_ERROR_REQUIRED_VERSION "1.36") if (WIN32) set(KF5_WANT_VERSION "5.70.0") set(KMIME_WANT_VERSION "5.12.0") else () set(KF5_WANT_VERSION ${KF_MIN_VERSION}) set(KMIME_WANT_VERSION ${KMIME_VERSION}) endif () set(CMAKE_CXX_STANDARD 20) set(CMAKE_CXX_STANDARD_REQUIRED ON) find_package(ECM ${KF5_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(ECMInstallIcons) include(ECMSetupVersion) include(ECMAddTests) include(GenerateExportHeader) include(ECMGenerateHeaders) include(FeatureSummary) include(CheckFunctionExists) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMAddAppIcon) include(ECMQtDeclareLoggingCategory) include(ECMDeprecationSettings) include(KDEClangFormat) if (QT_MAJOR_VERSION STREQUAL "6") set(QT_REQUIRED_VERSION "6.4.0") set(KF_MIN_VERSION "5.240.0") set(KF_MAJOR_VERSION "6") else() set(KF_MIN_VERSION "5.100.0") set(KF_MAJOR_VERSION "5") endif() # Find KF5 packages find_package(KF${KF_MAJOR_VERSION}WidgetsAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}ConfigWidgets ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}CoreAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}Codecs ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}Config ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}I18n ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}IconThemes ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}ItemModels ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}XmlGui ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}WindowSystem ${KF5_WANT_VERSION} CONFIG REQUIRED) find_package(KF${KF_MAJOR_VERSION}DocTools ${KF5_WANT_VERSION} CONFIG) find_package(KF${KF_MAJOR_VERSION}Crash ${KF5_WANT_VERSION} REQUIRED) set_package_properties(KF${KF_MAJOR_VERSION}DocTools PROPERTIES DESCRIPTION "Documentation tools" PURPOSE "Required to generate Kleopatra documentation." TYPE OPTIONAL) # Optional packages if (WIN32) # Only a replacement available for Windows so this # is required on other platforms. find_package(KF${KF_MAJOR_VERSION}DBusAddons ${KF5_WANT_VERSION} CONFIG) set_package_properties(KF5DBusAddons PROPERTIES DESCRIPTION "Support library to work with DBus" PURPOSE "DBus session integration" URL "https://inqlude.org/libraries/kdbusaddons.html" TYPE OPTIONAL) else() find_package(KF${KF_MAJOR_VERSION}DBusAddons ${KF5_WANT_VERSION} CONFIG REQUIRED) set(_kleopatra_dbusaddons_libs KF${KF_MAJOR_VERSION}::DBusAddons) endif() set(HAVE_QDBUS ${Qt${QT_MAJOR_VERSION}DBus_FOUND}) find_package(Gpgmepp ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) if (QT_MAJOR_VERSION STREQUAL "6") find_package(QGpgmeQt6 ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) else() find_package(QGpgme ${GPGME_REQUIRED_VERSION} CONFIG REQUIRED) endif() if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.17.0") set(QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY 1) set(QGPGME_CRYPTOCONFIGENTRY_HAS_DEFAULT_VALUE 1) set(QGPGME_SUPPORTS_WKDLOOKUP 1) set(QGPGME_SUPPORTS_IMPORT_WITH_FILTER 1) set(QGPGME_SUPPORTS_IMPORT_WITH_KEY_ORIGIN 1) set(QGPGME_SUPPORTS_SECRET_KEY_EXPORT 1) set(QGPGME_SUPPORTS_SECRET_SUBKEY_EXPORT 1) set(QGPGME_SUPPORTS_RECEIVING_KEYS_BY_KEY_ID 1) endif() if (QGpgme_VERSION VERSION_GREATER_EQUAL "1.18.0") set(QGPGME_SUPPORTS_KEY_REVOCATION 1) set(QGPGME_SUPPORTS_KEY_REFRESH 1) set(QGPGME_SUPPORTS_SET_FILENAME 1) set(QGPGME_SUPPORTS_SET_PRIMARY_UID 1) endif() if (Gpgmepp_VERSION VERSION_GREATER_EQUAL "1.18.1") set(GPGMEPP_SUPPORTS_SET_CURVE 1) set(QGPGME_SUPPORTS_DEFERRED_IMPORT_JOB 1) endif() # Kdepimlibs packages find_package(KF5Libkleo ${LIBKLEO_VERSION} CONFIG REQUIRED) find_package(KF5Mime ${KMIME_WANT_VERSION} CONFIG REQUIRED) find_package(KF5IdentityManagement ${KIDENTITYMANAGEMENT_VERSION} CONFIG) find_package(KF5MailTransport ${KMAILTRANSPORT_VERSION} CONFIG) find_package(KF5MailTransportAkonadi ${KMAILTRANSPORT_VERSION} CONFIG) find_package(Qt${QT_MAJOR_VERSION} ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets Test Network PrintSupport) find_package(LibAssuan ${LIBASSUAN_REQUIRED_VERSION} REQUIRED) set_package_properties(LibAssuan PROPERTIES TYPE REQUIRED PURPOSE "Needed for Kleopatra to act as the GnuPG UI Server" ) find_package(LibGpgError ${GPG_ERROR_REQUIRED_VERSION} REQUIRED) set_package_properties(LibGpgError PROPERTIES TYPE REQUIRED ) set(kleopatra_release FALSE) if(NOT kleopatra_release) find_package(Git) if(GIT_FOUND) execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} RESULT_VARIABLE rc ERROR_QUIET) if(rc EQUAL 0) execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%h ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE Kleopatra_WC_REVISION) string(REGEX REPLACE "\n" "" Kleopatra_WC_REVISION "${Kleopatra_WC_REVISION}") execute_process(COMMAND ${GIT_EXECUTABLE} log -1 --oneline --format=%cI ${CMAKE_CURRENT_SOURCE_DIR} WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE Kleopatra_WC_LAST_CHANGED_DATE) string(REGEX REPLACE "^([0-9]+)-([0-9]+)-([0-9]+)T([0-9]+):([0-9]+):([0-9]+).*$" "\\1\\2\\3T\\4\\5\\6" Kleopatra_WC_LAST_CHANGED_DATE "${Kleopatra_WC_LAST_CHANGED_DATE}") set(kleopatra_version "${kleopatra_version}+git${Kleopatra_WC_LAST_CHANGED_DATE}~${Kleopatra_WC_REVISION}") endif() endif() endif() configure_file(${CMAKE_CURRENT_SOURCE_DIR}/version-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/version-kleopatra.h) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/config-kleopatra.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-kleopatra.h) include_directories( ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR} ) if (WIN32) # On Windows, we need to use stuff deprecated since Qt 5.11, e.g. from QDesktopWidget ecm_set_disabled_deprecation_versions(QT 5.10.0 KF 5.102.0) else () -ecm_set_disabled_deprecation_versions(QT 5.15.2 KF 5.102.0) +ecm_set_disabled_deprecation_versions(QT 6.4 KF 5.102.0) endif () if(CMAKE_COMPILER_IS_GNUCXX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wno-missing-braces -Wno-parentheses -Wno-ignored-qualifiers") endif() if(MINGW) # we do not care about different signedness of passed pointer arguments add_compile_options($<$:-Wno-pointer-sign>) endif() add_definitions(-DQT_NO_EMIT) remove_definitions(-DQT_NO_FOREACH) # Disable the use of QStringBuilder for operator+ to prevent crashes when # returning the result of concatenating string temporaries in lambdas. We do # this for example in some std::transform expressions. # This is a known issue: https://bugreports.qt.io/browse/QTBUG-47066 # Alternatively, one would always have to remember to force the lambdas to # return a QString instead of QStringBuilder, but that's just too easy to # forget and, unfortunately, the compiler doesn't issue a warning if one forgets # this. So, it's just too dangerous. # One can still use QStringBuilder explicitly with the operator% if necessary. remove_definitions(-DQT_USE_FAST_OPERATOR_PLUS) remove_definitions(-DQT_USE_QSTRINGBUILDER) kde_enable_exceptions() 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) endif() add_subdirectory(pics) add_subdirectory(src) if(BUILD_TESTING) add_subdirectory(tests) add_subdirectory(autotests) endif() ecm_qt_install_logging_categories( EXPORT KLEOPATRA FILE kleopatra.categories DESTINATION ${KDE_INSTALL_LOGGINGCATEGORIESDIR} ) ki18n_install(po) if(KF5DocTools_FOUND) kdoctools_install(po) add_subdirectory(doc) endif() 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}) diff --git a/src/conf/appearanceconfigwidget.cpp b/src/conf/appearanceconfigwidget.cpp index f87858853..9bf2ff9f6 100644 --- a/src/conf/appearanceconfigwidget.cpp +++ b/src/conf/appearanceconfigwidget.cpp @@ -1,686 +1,706 @@ /* appearanceconfigwidget.cpp This file is part of kleopatra, the KDE key manager SPDX-FileCopyrightText: 2002, 2004, 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2002, 2003 Marc Mutz SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "appearanceconfigwidget.h" #include "ui_appearanceconfigwidget.h" #include "tagspreferences.h" #include "tooltippreferences.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Config; enum { HasNameRole = Qt::UserRole + 0x1234, /*!< Records that the user has assigned a name (to avoid comparing with i18n-strings) */ HasFontRole, /*!< Records that the user has chosen completely different font (as opposed to italic/bold/strikeout) */ IconNameRole, /*!< Records the name of the icon (since QIcon won't give it out again, once set) */ MayChangeNameRole, MayChangeForegroundRole, MayChangeBackgroundRole, MayChangeFontRole, MayChangeItalicRole, MayChangeBoldRole, MayChangeStrikeOutRole, MayChangeIconRole, StoredForegroundRole, /*!< Stores the actual configured foreground color */ StoredBackgroundRole, /*!< Stores the actual configured background color */ EndDummy, }; static QFont tryToFindFontFor(const QListWidgetItem *item) { if (item) if (const QListWidget *const lw = item->listWidget()) { return lw->font(); } return QApplication::font("QListWidget"); } static bool is(const QListWidgetItem *item, bool (QFont::*func)() const) { if (!item) { return false; } const QVariant v = item->data(Qt::FontRole); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (!v.isValid() || v.type() != QVariant::Font) { +#else + if (!v.isValid() || v.userType() != QMetaType::QFont) { +#endif return false; } return (v.value().*func)(); } static bool is_italic(const QListWidgetItem *item) { return is(item, &QFont::italic); } static bool is_bold(const QListWidgetItem *item) { return is(item, &QFont::bold); } static bool is_strikeout(const QListWidgetItem *item) { return is(item, &QFont::strikeOut); } static void set(QListWidgetItem *item, bool on, void (QFont::*func)(bool)) { if (!item) { return; } const QVariant v = item->data(Qt::FontRole); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QFont font = v.isValid() && v.type() == QVariant::Font ? v.value() : tryToFindFontFor(item); +#else + QFont font = v.isValid() && v.userType() == QMetaType::QFont ? v.value() : tryToFindFontFor(item); +#endif (font.*func)(on); item->setData(Qt::FontRole, font); } static void set_italic(QListWidgetItem *item, bool on) { set(item, on, &QFont::setItalic); } static void set_bold(QListWidgetItem *item, bool on) { set(item, on, &QFont::setBold); } static void set_strikeout(QListWidgetItem *item, bool on) { set(item, on, &QFont::setStrikeOut); } static void apply_config(const KConfigGroup &group, QListWidgetItem *item) { if (!item) { return; } const QString name = group.readEntry("Name"); item->setText(name.isEmpty() ? i18nc("Key filter without user-assigned name", "") : name); item->setData(HasNameRole, !name.isEmpty()); item->setData(MayChangeNameRole, !group.isEntryImmutable("Name")); const QColor fg = group.readEntry("foreground-color", QColor()); item->setData(StoredForegroundRole, fg.isValid() ? QBrush(fg) : QVariant()); if (!SystemInfo::isHighContrastModeActive()) { item->setData(Qt::ForegroundRole, fg.isValid() ? QBrush(fg) : QVariant()); } item->setData(MayChangeForegroundRole, !group.isEntryImmutable("foreground-color")); const QColor bg = group.readEntry("background-color", QColor()); item->setData(StoredBackgroundRole, bg.isValid() ? QBrush(bg) : QVariant()); if (!SystemInfo::isHighContrastModeActive()) { item->setData(Qt::BackgroundRole, bg.isValid() ? QBrush(bg) : QVariant()); } item->setData(MayChangeBackgroundRole, !group.isEntryImmutable("background-color")); const QFont defaultFont = tryToFindFontFor(item); if (group.hasKey("font")) { const QFont font = group.readEntry("font", defaultFont); item->setData(Qt::FontRole, font != defaultFont ? font : QVariant()); item->setData(HasFontRole, font != defaultFont); } else { QFont font = defaultFont; font.setStrikeOut(group.readEntry("font-strikeout", false)); font.setItalic(group.readEntry("font-italic", false)); font.setBold(group.readEntry("font-bold", false)); item->setData(Qt::FontRole, font); item->setData(HasFontRole, false); } item->setData(MayChangeFontRole, !group.isEntryImmutable("font")); item->setData(MayChangeItalicRole, !group.isEntryImmutable("font-italic")); item->setData(MayChangeBoldRole, !group.isEntryImmutable("font-bold")); item->setData(MayChangeStrikeOutRole, !group.isEntryImmutable("font-strikeout")); const QString iconName = group.readEntry("icon"); item->setData(Qt::DecorationRole, iconName.isEmpty() ? QVariant() : QIcon::fromTheme(iconName)); item->setData(IconNameRole, iconName.isEmpty() ? QVariant() : iconName); item->setData(MayChangeIconRole, !group.isEntryImmutable("icon")); } static void erase_if_allowed(QListWidgetItem *item, int role, int allowRole) { if (item && item->data(allowRole).toBool()) { item->setData(role, QVariant()); } } #if 0 static void erase_if_allowed(QListWidgetItem *item, const int role[], size_t numRoles, int allowRole) { if (item && item->data(allowRole).toBool()) for (unsigned int i = 0; i < numRoles; ++i) { item->setData(role[i], QVariant()); } } static void erase_if_allowed(QListWidgetItem *item, int role, const int allowRole[], size_t numAllowRoles) { if (!item) { return; } for (unsigned int i = 0; i < numAllowRoles; ++i) if (!item->data(allowRole[i]).toBool()) { return; } item->setData(role, QVariant()); } #endif static void erase_if_allowed(QListWidgetItem *item, const int role[], size_t numRoles, const int allowRole[], size_t numAllowRoles) { if (!item) { return; } for (unsigned int i = 0; i < numAllowRoles; ++i) if (!item->data(allowRole[i]).toBool()) { return; } for (unsigned int i = 0; i < numRoles; ++i) { item->setData(role[i], QVariant()); } } static void set_default_appearance(QListWidgetItem *item) { if (!item) { return; } erase_if_allowed(item, StoredForegroundRole, MayChangeForegroundRole); erase_if_allowed(item, Qt::ForegroundRole, MayChangeForegroundRole); erase_if_allowed(item, StoredBackgroundRole, MayChangeBackgroundRole); erase_if_allowed(item, Qt::BackgroundRole, MayChangeBackgroundRole); erase_if_allowed(item, Qt::DecorationRole, MayChangeIconRole); static const int fontRoles[] = { Qt::FontRole, HasFontRole }; static const int fontAllowRoles[] = { MayChangeFontRole, MayChangeItalicRole, MayChangeBoldRole, MayChangeStrikeOutRole, }; erase_if_allowed(item, fontRoles, sizeof(fontRoles) / sizeof(int), fontAllowRoles, sizeof(fontAllowRoles) / sizeof(int)); } static void writeOrDelete(KConfigGroup &group, const char *key, const QVariant &value) { if (value.isValid()) { group.writeEntry(key, value); } else { group.deleteEntry(key); } } static QVariant brush2color(const QVariant &v) { if (v.isValid()) { +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) if (v.type() == QVariant::Color) { +#else + if (v.userType() == QMetaType::QColor) { +#endif return v; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) } else if (v.type() == QVariant::Brush) { +#else + } else if (v.userType() == QMetaType::QBrush) { +#endif return v.value().color(); } } return QVariant(); } static void save_to_config(const QListWidgetItem *item, KConfigGroup &group) { if (!item) { return; } writeOrDelete(group, "Name", item->data(HasNameRole).toBool() ? item->text() : QVariant()); writeOrDelete(group, "foreground-color", brush2color(item->data(StoredForegroundRole))); writeOrDelete(group, "background-color", brush2color(item->data(StoredBackgroundRole))); writeOrDelete(group, "icon", item->data(IconNameRole)); group.deleteEntry("font"); group.deleteEntry("font-strikeout"); group.deleteEntry("font-italic"); group.deleteEntry("font-bold"); if (item->data(HasFontRole).toBool()) { writeOrDelete(group, "font", item->data(Qt::FontRole)); return; } if (is_strikeout(item)) { group.writeEntry("font-strikeout", true); } if (is_italic(item)) { group.writeEntry("font-italic", true); } if (is_bold(item)) { group.writeEntry("font-bold", true); } } static void kiosk_enable(QWidget *w, const QListWidgetItem *item, int allowRole) { if (!w) { return; } if (item && !item->data(allowRole).toBool()) { w->setEnabled(false); w->setToolTip(i18n("This parameter has been locked down by the system administrator.")); } else { w->setEnabled(item); w->setToolTip(QString()); } } class AppearanceConfigWidget::Private : public Ui_AppearanceConfigWidget { friend class ::Kleo::Config::AppearanceConfigWidget; AppearanceConfigWidget *const q; public: explicit Private(AppearanceConfigWidget *qq) : Ui_AppearanceConfigWidget() , q(qq) { setupUi(q); if (QLayout *const l = q->layout()) { l->setContentsMargins(0, 0, 0, 0); } highContrastMsg->setVisible(SystemInfo::isHighContrastModeActive()); highContrastMsg->setMessageType(KMessageWidget::Warning); highContrastMsg->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, q)); highContrastMsg->setText(i18n("The preview of colors is disabled because high-contrast mode is active.")); highContrastMsg->setCloseButtonVisible(false); if (Kleo::Settings{}.cmsEnabled()) { auto w = new QWidget; dnOrderWidget = new DNAttributeOrderConfigWidget{w}; dnOrderWidget->setObjectName(QStringLiteral("dnOrderWidget")); (new QVBoxLayout(w))->addWidget(dnOrderWidget); tabWidget->addTab(w, i18n("DN-Attribute Order")); connect(dnOrderWidget, &DNAttributeOrderConfigWidget::changed, q, &AppearanceConfigWidget::changed); } connect(iconButton, SIGNAL(clicked()), q, SLOT(slotIconClicked())); #ifndef QT_NO_COLORDIALOG connect(foregroundButton, SIGNAL(clicked()), q, SLOT(slotForegroundClicked())); connect(backgroundButton, SIGNAL(clicked()), q, SLOT(slotBackgroundClicked())); #else foregroundButton->hide(); backgroundButton->hide(); #endif #ifndef QT_NO_FONTDIALOG connect(fontButton, SIGNAL(clicked()), q, SLOT(slotFontClicked())); #else fontButton->hide(); #endif connect(categoriesLV, SIGNAL(itemSelectionChanged()), q, SLOT(slotSelectionChanged())); connect(defaultLookPB, SIGNAL(clicked()), q, SLOT(slotDefaultClicked())); connect(italicCB, SIGNAL(toggled(bool)), q, SLOT(slotItalicToggled(bool))); connect(boldCB, SIGNAL(toggled(bool)), q, SLOT(slotBoldToggled(bool))); connect(strikeoutCB, SIGNAL(toggled(bool)), q, SLOT(slotStrikeOutToggled(bool))); connect(tooltipValidityCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotTooltipValidityChanged(bool))); connect(tooltipOwnerCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotTooltipOwnerChanged(bool))); connect(tooltipDetailsCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotTooltipDetailsChanged(bool))); connect(useTagsCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotUseTagsChanged(bool))); } private: void enableDisableActions(QListWidgetItem *item); QListWidgetItem *selectedItem() const; private: void slotIconClicked(); #ifndef QT_NO_COLORDIALOG void slotForegroundClicked(); void slotBackgroundClicked(); #endif #ifndef QT_NO_FONTDIALOG void slotFontClicked(); #endif void slotSelectionChanged(); void slotDefaultClicked(); void slotItalicToggled(bool); void slotBoldToggled(bool); void slotStrikeOutToggled(bool); void slotTooltipValidityChanged(bool); void slotTooltipOwnerChanged(bool); void slotTooltipDetailsChanged(bool); void slotUseTagsChanged(bool); private: Kleo::DNAttributeOrderConfigWidget *dnOrderWidget = nullptr; }; AppearanceConfigWidget::AppearanceConfigWidget(QWidget *p, Qt::WindowFlags f) : QWidget(p, f), d(new Private(this)) { // load(); } AppearanceConfigWidget::~AppearanceConfigWidget() {} void AppearanceConfigWidget::Private::slotSelectionChanged() { enableDisableActions(selectedItem()); } QListWidgetItem *AppearanceConfigWidget::Private::selectedItem() const { const QList items = categoriesLV->selectedItems(); return items.empty() ? nullptr : items.front(); } void AppearanceConfigWidget::Private::enableDisableActions(QListWidgetItem *item) { kiosk_enable(iconButton, item, MayChangeIconRole); #ifndef QT_NO_COLORDIALOG kiosk_enable(foregroundButton, item, MayChangeForegroundRole); kiosk_enable(backgroundButton, item, MayChangeBackgroundRole); #endif #ifndef QT_NO_FONTDIALOG kiosk_enable(fontButton, item, MayChangeFontRole); #endif kiosk_enable(italicCB, item, MayChangeItalicRole); kiosk_enable(boldCB, item, MayChangeBoldRole); kiosk_enable(strikeoutCB, item, MayChangeStrikeOutRole); defaultLookPB->setEnabled(item); italicCB->setChecked(is_italic(item)); boldCB->setChecked(is_bold(item)); strikeoutCB->setChecked(is_strikeout(item)); } void AppearanceConfigWidget::Private::slotDefaultClicked() { QListWidgetItem *const item = selectedItem(); if (!item) { return; } set_default_appearance(item); enableDisableActions(item); Q_EMIT q->changed(); } void AppearanceConfigWidget::defaults() { // This simply means "default look for every category" for (int i = 0, end = d->categoriesLV->count(); i != end; ++i) { set_default_appearance(d->categoriesLV->item(i)); } // use a temporary TooltipPreferences instance for resetting the values to the defaults; // the setters respect the immutability of the individual settings, so that we don't have // to check this explicitly TooltipPreferences tooltipPrefs; tooltipPrefs.setShowValidity(tooltipPrefs.findItem(QStringLiteral("ShowValidity"))->getDefault().toBool()); d->tooltipValidityCheckBox->setChecked(tooltipPrefs.showValidity()); tooltipPrefs.setShowOwnerInformation(tooltipPrefs.findItem(QStringLiteral("ShowOwnerInformation"))->getDefault().toBool()); d->tooltipOwnerCheckBox->setChecked(tooltipPrefs.showOwnerInformation()); tooltipPrefs.setShowCertificateDetails(tooltipPrefs.findItem(QStringLiteral("ShowCertificateDetails"))->getDefault().toBool()); d->tooltipDetailsCheckBox->setChecked(tooltipPrefs.showCertificateDetails()); if (d->dnOrderWidget) { const Settings settings; if (!settings.isImmutable(QStringLiteral("AttributeOrder"))) { d->dnOrderWidget->setAttributeOrder(DN::defaultAttributeOrder()); } } Q_EMIT changed(); } void AppearanceConfigWidget::load() { if (d->dnOrderWidget) { const Settings settings; d->dnOrderWidget->setAttributeOrder(DN::attributeOrder()); d->dnOrderWidget->setEnabled(!settings.isImmutable(QStringLiteral("AttributeOrder"))); } d->categoriesLV->clear(); KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc")); if (!config) { return; } const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$"))); for (const QString &group : groups) { const KConfigGroup configGroup{config, group}; const bool isCmsSpecificKeyFilter = !configGroup.readEntry("is-openpgp-key", true); auto item = new QListWidgetItem{d->categoriesLV}; // hide CMS-specific filters if CMS is disabled; we hide those filters // instead of skipping them, so that they are not removed on save item->setHidden(isCmsSpecificKeyFilter && !Kleo::Settings{}.cmsEnabled()); apply_config(configGroup, item); } const TooltipPreferences prefs; d->tooltipValidityCheckBox->setChecked(prefs.showValidity()); d->tooltipValidityCheckBox->setEnabled(!prefs.isImmutable(QStringLiteral("ShowValidity"))); d->tooltipOwnerCheckBox->setChecked(prefs.showOwnerInformation()); d->tooltipOwnerCheckBox->setEnabled(!prefs.isImmutable(QStringLiteral("ShowOwnerInformation"))); d->tooltipDetailsCheckBox->setChecked(prefs.showCertificateDetails()); d->tooltipDetailsCheckBox->setEnabled(!prefs.isImmutable(QStringLiteral("ShowCertificateDetails"))); const TagsPreferences tagsPrefs; d->useTagsCheckBox->setChecked(tagsPrefs.useTags()); d->useTagsCheckBox->setEnabled(!tagsPrefs.isImmutable(QStringLiteral("UseTags"))); } void AppearanceConfigWidget::save() { if (d->dnOrderWidget) { Settings settings; settings.setAttributeOrder(d->dnOrderWidget->attributeOrder()); settings.save(); DN::setAttributeOrder(settings.attributeOrder()); } TooltipPreferences prefs; prefs.setShowValidity(d->tooltipValidityCheckBox->isChecked()); prefs.setShowOwnerInformation(d->tooltipOwnerCheckBox->isChecked()); prefs.setShowCertificateDetails(d->tooltipDetailsCheckBox->isChecked()); prefs.save(); KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc")); if (!config) { return; } // We know (assume) that the groups in the config object haven't changed, // so we just iterate over them and over the listviewitems, and map one-to-one. const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$"))); #if 0 if (groups.isEmpty()) { // If we created the default categories ourselves just now, then we need to make up their list Q3ListViewItemIterator lvit(categoriesLV); for (; lvit.current(); ++lvit) { groups << lvit.current()->text(0); } } #endif for (int i = 0, end = std::min(groups.size(), d->categoriesLV->count()); i != end; ++i) { const QListWidgetItem *const item = d->categoriesLV->item(i); Q_ASSERT(item); KConfigGroup group(config, groups[i]); save_to_config(item, group); } TagsPreferences tagsPrefs; tagsPrefs.setUseTags(d->useTagsCheckBox->isChecked()); tagsPrefs.save(); config->sync(); KeyFilterManager::instance()->reload(); } void AppearanceConfigWidget::Private::slotIconClicked() { QListWidgetItem *const item = selectedItem(); if (!item) { return; } const QString iconName = KIconDialog::getIcon( /* repeating default arguments begin */ KIconLoader::Desktop, KIconLoader::Application, false, 0, false, /* repeating default arguments end */ q); if (iconName.isEmpty()) { return; } item->setIcon(QIcon::fromTheme(iconName)); item->setData(IconNameRole, iconName); Q_EMIT q->changed(); } #ifndef QT_NO_COLORDIALOG void AppearanceConfigWidget::Private::slotForegroundClicked() { QListWidgetItem *const item = selectedItem(); if (!item) { return; } const QVariant v = brush2color(item->data(StoredForegroundRole)); const QColor initial = v.isValid() ? v.value() : categoriesLV->palette().color(QPalette::Normal, QPalette::Text); const QColor c = QColorDialog::getColor(initial, q); if (c.isValid()) { item->setData(StoredForegroundRole, QBrush(c)); if (!SystemInfo::isHighContrastModeActive()) { item->setData(Qt::ForegroundRole, QBrush(c)); } Q_EMIT q->changed(); } } void AppearanceConfigWidget::Private::slotBackgroundClicked() { QListWidgetItem *const item = selectedItem(); if (!item) { return; } const QVariant v = brush2color(item->data(StoredBackgroundRole)); const QColor initial = v.isValid() ? v.value() : categoriesLV->palette().color(QPalette::Normal, QPalette::Base); const QColor c = QColorDialog::getColor(initial, q); if (c.isValid()) { item->setData(StoredBackgroundRole, QBrush(c)); if (!SystemInfo::isHighContrastModeActive()) { item->setData(Qt::BackgroundRole, QBrush(c)); } Q_EMIT q->changed(); } } #endif // QT_NO_COLORDIALOG #ifndef QT_NO_FONTDIALOG void AppearanceConfigWidget::Private::slotFontClicked() { QListWidgetItem *const item = selectedItem(); if (!item) { return; } const QVariant v = item->data(Qt::FontRole); bool ok = false; const QFont defaultFont = tryToFindFontFor(item); +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) const QFont initial = v.isValid() && v.type() == QVariant::Font ? v.value() : defaultFont; +#else + const QFont initial = v.isValid() && v.userType() == QMetaType::QFont ? v.value() : defaultFont; +#endif QFont f = QFontDialog::getFont(&ok, initial, q); if (!ok) { return; } // disallow circumventing KIOSK: if (!item->data(MayChangeItalicRole).toBool()) { f.setItalic(initial.italic()); } if (!item->data(MayChangeBoldRole).toBool()) { f.setBold(initial.bold()); } if (!item->data(MayChangeStrikeOutRole).toBool()) { f.setStrikeOut(initial.strikeOut()); } item->setData(Qt::FontRole, f != defaultFont ? f : QVariant()); item->setData(HasFontRole, true); Q_EMIT q->changed(); } #endif // QT_NO_FONTDIALOG void AppearanceConfigWidget::Private::slotItalicToggled(bool on) { set_italic(selectedItem(), on); Q_EMIT q->changed(); } void AppearanceConfigWidget::Private::slotBoldToggled(bool on) { set_bold(selectedItem(), on); Q_EMIT q->changed(); } void AppearanceConfigWidget::Private::slotStrikeOutToggled(bool on) { set_strikeout(selectedItem(), on); Q_EMIT q->changed(); } void AppearanceConfigWidget::Private::slotTooltipValidityChanged(bool) { Q_EMIT q->changed(); } void AppearanceConfigWidget::Private::slotTooltipOwnerChanged(bool) { Q_EMIT q->changed(); } void AppearanceConfigWidget::Private::slotTooltipDetailsChanged(bool) { Q_EMIT q->changed(); } void AppearanceConfigWidget::Private::slotUseTagsChanged(bool) { Q_EMIT q->changed(); } #include "moc_appearanceconfigwidget.cpp" diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 3b14784b0..c86e33c0d 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,824 +1,827 @@ /* -*- mode: c++; c-basic-offset:4 -*- mainwindow.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "mainwindow.h" #include "aboutdata.h" #include "settings.h" #include #include "view/padwidget.h" #include "view/searchbar.h" #include "view/tabwidget.h" #include "view/keylistcontroller.h" #include "view/keycacheoverlay.h" #include "view/smartcardwidget.h" #include "view/welcomewidget.h" #include "commands/selftestcommand.h" #include "commands/importcrlcommand.h" #include "commands/importcertificatefromfilecommand.h" #include "commands/decryptverifyfilescommand.h" #include "commands/signencryptfilescommand.h" #include "conf/groupsconfigdialog.h" #include "utils/detail_p.h" #include #include "utils/action_data.h" #include "utils/filedialog.h" #include "utils/clipboardmenu.h" #include "utils/gui-helper.h" #include "utils/qt-cxx20-compat.h" #include "dialogs/updatenotification.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::chrono_literals; using namespace Kleo; using namespace Kleo::Commands; using namespace GpgME; static KGuiItem KStandardGuiItem_quit() { static const QString app = KAboutData::applicationData().displayName(); KGuiItem item = KStandardGuiItem::quit(); item.setText(xi18nc("@action:button", "&Quit %1", app)); return item; } static KGuiItem KStandardGuiItem_close() { KGuiItem item = KStandardGuiItem::close(); item.setText(i18nc("@action:button", "Only &Close Window")); return item; } static bool isQuitting = false; namespace { static const std::vector mainViewActionNames = { QStringLiteral("view_certificate_overview"), QStringLiteral("manage_smartcard"), QStringLiteral("pad_view") }; class CertificateView : public QWidget, public FocusFirstChild { Q_OBJECT public: CertificateView(QWidget* parent = nullptr) : QWidget{parent} , ui{this} { } SearchBar *searchBar() const { return ui.searchBar; } TabWidget *tabWidget() const { return ui.tabWidget; } void focusFirstChild(Qt::FocusReason reason) override { ui.searchBar->lineEdit()->setFocus(reason); } private: struct UI { TabWidget *tabWidget = nullptr; SearchBar *searchBar = nullptr; explicit UI(CertificateView *q) { auto vbox = new QVBoxLayout{q}; vbox->setSpacing(0); searchBar = new SearchBar{q}; vbox->addWidget(searchBar); tabWidget = new TabWidget{q}; vbox->addWidget(tabWidget); tabWidget->connectSearchBar(searchBar); } } ui; }; } class MainWindow::Private { friend class ::MainWindow; MainWindow *const q; public: explicit Private(MainWindow *qq); ~Private(); template void createAndStart() { (new T(this->currentView(), &this->controller))->start(); } template void createAndStart(QAbstractItemView *view) { (new T(view, &this->controller))->start(); } template void createAndStart(const QStringList &a) { (new T(a, this->currentView(), &this->controller))->start(); } template void createAndStart(const QStringList &a, QAbstractItemView *view) { (new T(a, view, &this->controller))->start(); } void closeAndQuit() { const QString app = KAboutData::applicationData().displayName(); const int rc = KMessageBox::questionTwoActionsCancel(q, xi18n("%1 may be used by other applications as a service." "You may instead want to close this window without exiting %1.", app), i18nc("@title:window", "Really Quit?"), KStandardGuiItem_close(), KStandardGuiItem_quit(), KStandardGuiItem::cancel(), QLatin1String("really-quit-") + app.toLower()); if (rc == KMessageBox::Cancel) { return; } isQuitting = true; if (!q->close()) { return; } // WARNING: 'this' might be deleted at this point! if (rc == KMessageBox::ButtonCode::SecondaryAction) { qApp->quit(); } } void configureToolbars() { KEditToolBar dlg(q->factory()); dlg.exec(); } void editKeybindings() { KShortcutsDialog::showDialog(q->actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, q); updateSearchBarClickMessage(); } void updateSearchBarClickMessage() { const QString shortcutStr = focusToClickSearchAction->shortcut().toString(); ui.searchTab->searchBar()->updateClickMessage(shortcutStr); } void updateStatusBar() { if (DeVSCompliance::isActive()) { auto statusBar = std::make_unique(); auto statusLbl = std::make_unique(DeVSCompliance::name()); if (!SystemInfo::isHighContrastModeActive()) { const auto color = KColorScheme(QPalette::Active, KColorScheme::View).foreground( DeVSCompliance::isCompliant() ? KColorScheme::NormalText: KColorScheme::NegativeText ).color(); const auto background = KColorScheme(QPalette::Active, KColorScheme::View).background( DeVSCompliance::isCompliant() ? KColorScheme::PositiveBackground : KColorScheme::NegativeBackground ).color(); statusLbl->setStyleSheet(QStringLiteral("QLabel { color: %1; background-color: %2; }"). arg(color.name()).arg(background.name())); } statusBar->insertPermanentWidget(0, statusLbl.release()); q->setStatusBar(statusBar.release()); // QMainWindow takes ownership } else { q->setStatusBar(nullptr); } } void selfTest() { createAndStart(); } void configureGroups() { if (KConfigDialog::showDialog(GroupsConfigDialog::dialogName())) { return; } KConfigDialog *dialog = new GroupsConfigDialog(q); dialog->show(); } void showHandbook(); void gnupgLogViewer() { // Warning: Don't assume that the program needs to be in PATH. On Windows, it will also be found next to the calling process. if (!QProcess::startDetached(QStringLiteral("kwatchgnupg"), QStringList())) KMessageBox::error(q, i18n("Could not start the GnuPG Log Viewer (kwatchgnupg). " "Please check your installation."), i18n("Error Starting KWatchGnuPG")); } void forceUpdateCheck() { UpdateNotification::forceUpdateCheck(q); } void slotConfigCommitted(); void slotContextMenuRequested(QAbstractItemView *, const QPoint &p) { if (auto const menu = qobject_cast(q->factory()->container(QStringLiteral("listview_popup"), q))) { menu->exec(p); } else { qCDebug(KLEOPATRA_LOG) << "no \"listview_popup\" in kleopatra's ui.rc file"; } } void slotFocusQuickSearch() { ui.searchTab->searchBar()->lineEdit()->setFocus(); } void showView(const QString &actionName, QWidget *widget) { const auto coll = q->actionCollection(); if (coll) { for ( const QString &name : mainViewActionNames ) { if (auto action = coll->action(name)) { action->setChecked(name == actionName); } } } ui.stackWidget->setCurrentWidget(widget); if (auto ffci = dynamic_cast(widget)) { ffci->focusFirstChild(Qt::TabFocusReason); } } void showCertificateView() { if (KeyCache::instance()->keys().empty()) { showView(QStringLiteral("view_certificate_overview"), ui.welcomeWidget); } else { showView(QStringLiteral("view_certificate_overview"), ui.searchTab); } } void showSmartcardView() { showView(QStringLiteral("manage_smartcard"), ui.scWidget); } void showPadView() { if (!ui.padWidget) { ui.padWidget = new PadWidget; ui.stackWidget->addWidget(ui.padWidget); } showView(QStringLiteral("pad_view"), ui.padWidget); ui.stackWidget->resize(ui.padWidget->sizeHint()); } void restartDaemons() { Kleo::killDaemons(); } private: void setupActions(); QAbstractItemView *currentView() const { return ui.searchTab->tabWidget()->currentView(); } void keyListingDone() { const auto curWidget = ui.stackWidget->currentWidget(); if (curWidget == ui.scWidget || curWidget == ui.padWidget) { return; } showCertificateView(); } private: Kleo::KeyListController controller; bool firstShow : 1; struct UI { CertificateView *searchTab = nullptr; PadWidget *padWidget = nullptr; SmartCardWidget *scWidget = nullptr; WelcomeWidget *welcomeWidget = nullptr; QStackedWidget *stackWidget = nullptr; explicit UI(MainWindow *q); } ui; QAction *focusToClickSearchAction = nullptr; ClipboardMenu *clipboadMenu = nullptr; }; MainWindow::Private::UI::UI(MainWindow *q) : padWidget(nullptr) { auto mainWidget = new QWidget{q}; auto mainLayout = new QVBoxLayout(mainWidget); stackWidget = new QStackedWidget{q}; searchTab = new CertificateView{q}; stackWidget->addWidget(searchTab); new KeyCacheOverlay(mainWidget, q); scWidget = new SmartCardWidget{q}; stackWidget->addWidget(scWidget); welcomeWidget = new WelcomeWidget{q}; stackWidget->addWidget(welcomeWidget); mainLayout->addWidget(stackWidget); q->setCentralWidget(mainWidget); } MainWindow::Private::Private(MainWindow *qq) : q(qq), controller(q), firstShow(true), ui(q) { KDAB_SET_OBJECT_NAME(controller); AbstractKeyListModel *flatModel = AbstractKeyListModel::createFlatKeyListModel(q); AbstractKeyListModel *hierarchicalModel = AbstractKeyListModel::createHierarchicalKeyListModel(q); KDAB_SET_OBJECT_NAME(flatModel); KDAB_SET_OBJECT_NAME(hierarchicalModel); controller.setFlatModel(flatModel); controller.setHierarchicalModel(hierarchicalModel); controller.setTabWidget(ui.searchTab->tabWidget()); ui.searchTab->tabWidget()->setFlatModel(flatModel); ui.searchTab->tabWidget()->setHierarchicalModel(hierarchicalModel); setupActions(); ui.stackWidget->setCurrentWidget(ui.searchTab); if (auto action = q->actionCollection()->action(QStringLiteral("view_certificate_overview"))) { action->setChecked(true); } connect(&controller, SIGNAL(contextMenuRequested(QAbstractItemView*,QPoint)), q, SLOT(slotContextMenuRequested(QAbstractItemView*,QPoint))); connect(KeyCache::instance().get(), &KeyCache::keyListingDone, q, [this] () {keyListingDone();}); q->createGUI(QStringLiteral("kleopatra.rc")); // make toolbar buttons accessible by keyboard auto toolbar = q->findChild(); if (toolbar) { auto toolbarButtons = toolbar->findChildren(); for (auto b : toolbarButtons) { b->setFocusPolicy(Qt::TabFocus); } // move toolbar and its child widgets before the central widget in the tab order; // this is necessary to make Shift+Tab work as expected forceSetTabOrder(q, toolbar); auto toolbarChildren = toolbar->findChildren(); std::for_each(std::rbegin(toolbarChildren), std::rend(toolbarChildren), [toolbar](auto w) { forceSetTabOrder(toolbar, w); }); } const auto title = Kleo::brandingWindowTitle(); if (!title.isEmpty()) { QApplication::setApplicationDisplayName(title); } const auto icon = Kleo::brandingIcon(); if (!icon.isEmpty()) { const auto dir = QDir(Kleo::gpg4winInstallPath() + QStringLiteral("/../share/kleopatra/pics")); qCDebug(KLEOPATRA_LOG) << "Loading branding icon:" << dir.absoluteFilePath(icon); QPixmap brandingIcon(dir.absoluteFilePath(icon)); if (!brandingIcon.isNull()) { auto w = new QWidget; auto hl = new QHBoxLayout; auto lbl = new QLabel; w->setLayout(hl); hl->addWidget(lbl); lbl->setPixmap(brandingIcon); toolbar->addSeparator(); toolbar->addWidget(w); } } // Disable whats this as the vast majority of things does not have whats this // set in Kleopatra. #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) // With Qt 6 this is default and the attribute is no longer documented. QApplication::setAttribute(Qt::AA_DisableWindowContextHelpButton); #endif if (auto action = q->actionCollection()->action(QStringLiteral("help_whats_this"))) { delete action; } q->setAcceptDrops(true); // set default window size q->resize(QSize(1024, 500)); q->setAutoSaveSettings(); updateSearchBarClickMessage(); updateStatusBar(); if (KeyCache::instance()->initialized()) { keyListingDone(); } } MainWindow::Private::~Private() {} MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : KXmlGuiWindow(parent, flags), d(new Private(this)) {} MainWindow::~MainWindow() {} void MainWindow::Private::setupActions() { KActionCollection *const coll = q->actionCollection(); const std::vector action_data = { // see keylistcontroller.cpp for more actions // Tools menu #ifndef Q_OS_WIN { "tools_start_kwatchgnupg", i18n("GnuPG Log Viewer"), QString(), "kwatchgnupg", q, [this](bool) { gnupgLogViewer(); }, QString() }, #endif { "tools_restart_backend", i18nc("@action:inmenu", "Restart Background Processes"), i18nc("@info:tooltip", "Restart the background processes, e.g. after making changes to the configuration."), "view-refresh", q, [this](bool) { restartDaemons(); }, {} }, // Help menu #ifdef Q_OS_WIN { "help_check_updates", i18n("Check for updates"), QString(), "gpg4win-compact", q, [this](bool) { forceUpdateCheck(); }, QString() }, #endif // View menu { "view_certificate_overview", i18nc("@action show certificate overview", "Certificates"), i18n("Show certificate overview"), "view-certificate", q, [this](bool) { showCertificateView(); }, QString() }, { "pad_view", i18nc("@action show input / output area for encrypting/signing resp. decrypting/verifying text", "Notepad"), i18n("Show pad for encrypting/decrypting and signing/verifying text"), "note", q, [this](bool) { showPadView(); }, QString() }, { "manage_smartcard", i18nc("@action show smartcard management view", "Smartcards"), i18n("Show smartcard management"), "auth-sim-locked", q, [this](bool) { showSmartcardView(); }, QString() }, // Settings menu { "settings_self_test", i18n("Perform Self-Test"), QString(), nullptr, q, [this](bool) { selfTest(); }, QString() }, { "configure_groups", i18n("Configure Groups..."), QString(), "group", q, [this](bool) { configureGroups(); }, QString() } }; make_actions_from_data(action_data, coll); if (!Settings().groupsEnabled()) { if (auto action = coll->action(QStringLiteral("configure_groups"))) { delete action; } } for ( const QString &name : mainViewActionNames ) { if (auto action = coll->action(name)) { action->setCheckable(true); } } KStandardAction::close(q, SLOT(close()), coll); KStandardAction::quit(q, SLOT(closeAndQuit()), coll); KStandardAction::configureToolbars(q, SLOT(configureToolbars()), coll); KStandardAction::keyBindings(q, SLOT(editKeybindings()), coll); KStandardAction::preferences(qApp, SLOT(openOrRaiseConfigDialog()), coll); focusToClickSearchAction = new QAction(i18n("Set Focus to Quick Search"), q); coll->addAction(QStringLiteral("focus_to_quickseach"), focusToClickSearchAction); coll->setDefaultShortcut(focusToClickSearchAction, QKeySequence(Qt::ALT | Qt::Key_Q)); connect(focusToClickSearchAction, SIGNAL(triggered(bool)), q, SLOT(slotFocusQuickSearch())); clipboadMenu = new ClipboardMenu(q); clipboadMenu->setMainWindow(q); clipboadMenu->clipboardMenu()->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste"))); clipboadMenu->clipboardMenu()->setPopupMode(QToolButton::InstantPopup); coll->addAction(QStringLiteral("clipboard_menu"), clipboadMenu->clipboardMenu()); /* Add additional help actions for documentation */ const auto compendium = new DocAction(QIcon::fromTheme(QStringLiteral("gpg4win-compact")), i18n("Gpg4win Compendium"), i18nc("The Gpg4win compendium is only available" "at this point (24.7.2017) in german and english." "Please check with Gpg4win before translating this filename.", "gpg4win-compendium-en.pdf"), QStringLiteral("../share/gpg4win")); coll->addAction(QStringLiteral("help_doc_compendium"), compendium); /* Documentation centered around the german approved VS-NfD mode for official * RESTRICTED communication. This is only available in some distributions with * the focus on official communications. */ const auto symguide = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("Password-based encryption"), i18nc("Only available in German and English. Leave to English for other languages.", "handout_symmetric_encryption_gnupg_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_symenc"), symguide); const auto quickguide = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("Quickguide"), i18nc("Only available in German and English. Leave to English for other languages.", "handout_sign_encrypt_gnupg_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_quickguide"), quickguide); const auto vsa10573 = new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")), i18n("SecOps VSA-10573"), i18nc("Only available in German and English. Leave to English for other languages.", "BSI-VSA-10573-ENG_secops-20220207.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_vsa10573"), vsa10573); const auto vsa10584 = new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")), i18n("SecOps VSA-10584"), i18nc("Only available in German and English. Leave to English for other languages.", "BSI-VSA-10584-ENG_secops-20220207.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_vsa10584"), vsa10584); const auto smartcard = new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")), i18n("Smartcard Management"), i18nc("Only available in German and English. Leave to English for other languages.", "smartcard_howto_de.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_smartcard"), smartcard); const auto groups = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("Group configuration"), i18nc("Only available in German and English. Leave to English for other languages.", "handout_group-feature_gnupg_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_groups"), groups); const auto man_gpg = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("GnuPG Manual"), QStringLiteral("gnupg.pdf"), QStringLiteral("../share/doc/gnupg")); coll->addAction(QStringLiteral("help_doc_gnupg"), man_gpg); q->setStandardToolBarMenuEnabled(true); controller.createActions(coll); ui.searchTab->tabWidget()->createActions(coll); } void MainWindow::Private::slotConfigCommitted() { controller.updateConfig(); updateStatusBar(); } void MainWindow::closeEvent(QCloseEvent *e) { // KMainWindow::closeEvent() insists on quitting the application, // so do not let it touch the event... qCDebug(KLEOPATRA_LOG); if (d->controller.hasRunningCommands()) { if (d->controller.shutdownWarningRequired()) { const int ret = KMessageBox::warningContinueCancel(this, i18n("There are still some background operations ongoing. " "These will be terminated when closing the window. " "Proceed?"), i18n("Ongoing Background Tasks")); if (ret != KMessageBox::Continue) { e->ignore(); return; } } d->controller.cancelCommands(); if (d->controller.hasRunningCommands()) { // wait for them to be finished: setEnabled(false); QEventLoop ev; QTimer::singleShot(100ms, &ev, &QEventLoop::quit); connect(&d->controller, &KeyListController::commandsExecuting, &ev, &QEventLoop::quit); ev.exec(); if (d->controller.hasRunningCommands()) qCWarning(KLEOPATRA_LOG) << "controller still has commands running, this may crash now..."; setEnabled(true); } } if (isQuitting || qApp->isSavingSession()) { d->ui.searchTab->tabWidget()->saveViews(KSharedConfig::openConfig().data()); KConfigGroup grp(KConfigGroup(KSharedConfig::openConfig(), autoSaveGroup())); saveMainWindowSettings(grp); e->accept(); } else { e->ignore(); hide(); } } void MainWindow::showEvent(QShowEvent *e) { KXmlGuiWindow::showEvent(e); if (d->firstShow) { d->ui.searchTab->tabWidget()->loadViews(KSharedConfig::openConfig().data()); d->firstShow = false; } if (!savedGeometry.isEmpty()) { restoreGeometry(savedGeometry); } } void MainWindow::hideEvent(QHideEvent *e) { savedGeometry = saveGeometry(); KXmlGuiWindow::hideEvent(e); } void MainWindow::importCertificatesFromFile(const QStringList &files) { if (!files.empty()) { d->createAndStart(files); } } static QStringList extract_local_files(const QMimeData *data) { const QList urls = data->urls(); // begin workaround KDE/Qt misinterpretation of text/uri-list QList::const_iterator end = urls.end(); if (urls.size() > 1 && !urls.back().isValid()) { --end; } // end workaround QStringList result; std::transform(urls.begin(), end, std::back_inserter(result), std::mem_fn(&QUrl::toLocalFile)); result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&QString::isEmpty)), result.end()); return result; } static bool can_decode_local_files(const QMimeData *data) { if (!data) { return false; } return !extract_local_files(data).empty(); } void MainWindow::dragEnterEvent(QDragEnterEvent *e) { qCDebug(KLEOPATRA_LOG); if (can_decode_local_files(e->mimeData())) { e->acceptProposedAction(); } } void MainWindow::dropEvent(QDropEvent *e) { qCDebug(KLEOPATRA_LOG); if (!can_decode_local_files(e->mimeData())) { return; } e->setDropAction(Qt::CopyAction); const QStringList files = extract_local_files(e->mimeData()); const unsigned int classification = classify(files); QMenu menu; QAction *const signEncrypt = menu.addAction(i18n("Sign/Encrypt...")); QAction *const decryptVerify = mayBeAnyMessageType(classification) ? menu.addAction(i18n("Decrypt/Verify...")) : nullptr; if (signEncrypt || decryptVerify) { menu.addSeparator(); } QAction *const importCerts = mayBeAnyCertStoreType(classification) ? menu.addAction(i18n("Import Certificates")) : nullptr; QAction *const importCRLs = mayBeCertificateRevocationList(classification) ? menu.addAction(i18n("Import CRLs")) : nullptr; if (importCerts || importCRLs) { menu.addSeparator(); } if (!signEncrypt && !decryptVerify && !importCerts && !importCRLs) { return; } menu.addAction(i18n("Cancel")); - +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) const QAction *const chosen = menu.exec(mapToGlobal(e->pos())); +#else + const QAction *const chosen = menu.exec(mapToGlobal(e->position().toPoint())); +#endif if (!chosen) { return; } if (chosen == signEncrypt) { d->createAndStart(files); } else if (chosen == decryptVerify) { d->createAndStart(files); } else if (chosen == importCerts) { d->createAndStart(files); } else if (chosen == importCRLs) { d->createAndStart(files); } e->accept(); } void MainWindow::readProperties(const KConfigGroup &cg) { qCDebug(KLEOPATRA_LOG); KXmlGuiWindow::readProperties(cg); setHidden(cg.readEntry("hidden", false)); } void MainWindow::saveProperties(KConfigGroup &cg) { qCDebug(KLEOPATRA_LOG); KXmlGuiWindow::saveProperties(cg); cg.writeEntry("hidden", isHidden()); } #include "mainwindow.moc" #include "moc_mainwindow.cpp" diff --git a/src/utils/dragqueen.cpp b/src/utils/dragqueen.cpp index defe388cf..386437441 100644 --- a/src/utils/dragqueen.cpp +++ b/src/utils/dragqueen.cpp @@ -1,210 +1,227 @@ /* -*- mode: c++; c-basic-offset:4 -*- utils/dragqueen.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "dragqueen.h" #include #include #include #include #include #include #include #include using namespace Kleo; namespace { class MimeDataProxy : public QMimeData { Q_OBJECT public: explicit MimeDataProxy(QMimeData *source) : QMimeData(), m_source(source) { } QStringList formats() const override { if (m_source) { return m_source->formats(); } else { return QStringList(); } } bool hasFormat(const QString &format) const override { return m_source && m_source->hasFormat(format); } protected: #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QVariant retrieveData(const QString &format, QVariant::Type type) const override #else QVariant retrieveData(const QString &format, QMetaType type) const override #endif { if (!m_source) { return QVariant(); } // Doesn't work, is protected: // return m_source->retrieveData( format, type ); #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) switch (type) { #else switch (type.id()) { #endif +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) case QVariant::String: +#else + case QMetaType::QString: +#endif if (format == QLatin1String("text/plain")) { return m_source->text(); } if (format == QLatin1String("text/html")) { return m_source->html(); } break; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) case QVariant::Color: +#else + case QMetaType::QColor: +#endif if (format == QLatin1String("application/x-color")) { return m_source->colorData(); } break; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) case QVariant::Image: +#else + case QMetaType::QImage: +#endif if (format == QLatin1String("application/x-qt-image")) { return m_source->imageData(); } break; +#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) case QVariant::List: case QVariant::Url: +#else + case QMetaType::QVariantList: + case QMetaType::QUrl: +#endif if (format == QLatin1String("text/uri-list")) { const QList urls = m_source->urls(); if (urls.size() == 1) { return urls.front(); } QList result; std::copy(urls.begin(), urls.end(), std::back_inserter(result)); return result; } break; default: break; } QVariant v = m_source->data(format); v.convert(type); return v; } private: QPointer m_source; }; } DragQueen::DragQueen(QWidget *p, Qt::WindowFlags f) : QLabel(p, f), m_data(), m_dragStartPosition() { } DragQueen::DragQueen(const QString &t, QWidget *p, Qt::WindowFlags f) : QLabel(t, p, f), m_data(), m_dragStartPosition() { } DragQueen::~DragQueen() { delete m_data; } void DragQueen::setUrl(const QString &url) { auto data = new QMimeData; QList urls; urls.push_back(QUrl(url)); data->setUrls(urls); setMimeData(data); } QString DragQueen::url() const { if (!m_data || !m_data->hasUrls()) { return QString(); } const QList urls = m_data->urls(); if (urls.empty()) { return QString(); } return urls.front().toString(); } void DragQueen::setMimeData(QMimeData *data) { if (data == m_data) { return; } delete m_data; m_data = data; } QMimeData *DragQueen::mimeData() const { return m_data; } void DragQueen::mousePressEvent(QMouseEvent *e) { #ifndef QT_NO_DRAGANDDROP if (m_data && e->button() == Qt::LeftButton) { m_dragStartPosition = e->pos(); } #endif QLabel::mousePressEvent(e); } static QPoint calculate_hot_spot(const QPoint &mouse, const QSize &pix, const QLabel *label) { const Qt::Alignment align = label->alignment(); const int margin = label->margin(); const QRect cr = label->contentsRect().adjusted(margin, margin, -margin, -margin); const QRect rect = QStyle::alignedRect(QApplication::layoutDirection(), align, pix, cr); return mouse - rect.topLeft(); } void DragQueen::mouseMoveEvent(QMouseEvent *e) { #ifndef QT_NO_DRAGANDDROP if (m_data && (e->buttons() & Qt::LeftButton) && (m_dragStartPosition - e->pos()).manhattanLength() > QApplication::startDragDistance()) { auto drag = new QDrag(this); const QPixmap pix = pixmap(Qt::ReturnByValue); if (!pix.isNull()) { drag->setPixmap(pix); drag->setHotSpot(calculate_hot_spot(e->pos(), pix.size(), this)); } drag->setMimeData(new MimeDataProxy(m_data)); drag->exec(); } else { #endif QLabel::mouseMoveEvent(e); #ifndef QT_NO_DRAGANDDROP } #endif } #include "dragqueen.moc"