diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index f11662766..e041896e5 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,646 +1,646 @@
 # SPDX-FileCopyrightText: none
 # SPDX-License-Identifier: BSD-3-Clause
 add_subdirectory(icons)
 add_subdirectory(mimetypes)
 
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
 include_directories(${CMAKE_CURRENT_SOURCE_DIR})
 
 if (NOT DISABLE_KWATCHGNUPG)
     add_subdirectory(kwatchgnupg)
 endif()
 add_subdirectory(libkleopatraclient)
 add_subdirectory(conf)
 
 if(WIN32)
   set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp)
   set(_kleopatra_extra_SRCS
       pics/gpg4win.qrc
       selftest/registrycheck.cpp selftest/registrycheck.h
       utils/gnupg-registry.c
       utils/userinfo_win.cpp
       utils/winapi-helpers.cpp utils/winapi-helpers.h
       utils/windowsprocessdevice.cpp utils/windowsprocessdevice.h
       versioninfo.rc kleopatra.w32-manifest
   )
 else()
   set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp)
   set(_kleopatra_extra_SRCS)
 endif()
 
 set(_kleopatra_uiserver_SRCS
     ${_kleopatra_extra_uiserver_SRCS}
 
     selftest/uiservercheck.cpp selftest/uiservercheck.h
     uiserver/assuanserverconnection.cpp uiserver/assuanserverconnection.h
     uiserver/createchecksumscommand.cpp uiserver/createchecksumscommand.h
     uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandemailbase.h
     uiserver/decryptverifycommandfilesbase.cpp uiserver/decryptverifycommandfilesbase.h
     uiserver/echocommand.cpp uiserver/echocommand.h
     uiserver/encryptcommand.cpp uiserver/encryptcommand.h
     uiserver/importfilescommand.cpp uiserver/importfilescommand.h
     uiserver/prepencryptcommand.cpp uiserver/prepencryptcommand.h
     uiserver/prepsigncommand.cpp uiserver/prepsigncommand.h
     uiserver/selectcertificatecommand.cpp
     uiserver/sessiondata.cpp uiserver/sessiondata.h
     uiserver/signcommand.cpp uiserver/signcommand.h
     uiserver/signencryptfilescommand.cpp
     uiserver/uiserver.cpp
     uiserver/verifychecksumscommand.cpp uiserver/verifychecksumscommand.h
   )
 
 set(_kleopatra_uiserver_extra_libs LibAssuan::LibAssuan LibGpgError::LibGpgError)
 
 if(HAVE_GPG_ERR_SOURCE_KLEO)
   add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO)
   add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_KLEO)
 else()
   add_definitions(-DGPG_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1)
   add_definitions(-DGPGMEPP_ERR_SOURCE_DEFAULT=GPG_ERR_SOURCE_USER_1)
 endif()
 
 if(KPim6IdentityManagementCore_FOUND AND KPim6MailTransport_FOUND AND KPim6AkonadiMime_FOUND)
   set(_kleopatra_mail_libs
       KPim6::IdentityManagementCore # Export OpenPGP keys using WKS
       KPim6::MailTransport
       KPim6::AkonadiMime
   )
   add_definitions(-DMAILAKONADI_ENABLED)
 endif()
 
 ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui)
 
 set(_kleopatra_SRCS
   ${_kleopatra_extra_SRCS}
 
   accessibility/accessiblelink.cpp
   accessibility/accessiblelink_p.h
   accessibility/accessiblerichtextlabel.cpp
   accessibility/accessiblerichtextlabel_p.h
   accessibility/accessiblevaluelabel.cpp
   accessibility/accessiblevaluelabel_p.h
   accessibility/accessiblewidgetfactory.cpp
   accessibility/accessiblewidgetfactory.h
   commands/addadskcommand.cpp
   commands/addadskcommand.h
   commands/addsubkeycommand.cpp
   commands/addsubkeycommand.h
   commands/adduseridcommand.cpp
   commands/adduseridcommand.h
   commands/authenticatepivcardapplicationcommand.cpp
   commands/authenticatepivcardapplicationcommand.h
   commands/cardcommand.cpp
   commands/cardcommand.h
   commands/certificatetocardcommand.cpp
   commands/certificatetocardcommand.h
   commands/certificatetopivcardcommand.cpp
   commands/certificatetopivcardcommand.h
   commands/certifycertificatecommand.cpp
   commands/certifycertificatecommand.h
   commands/certifygroupcommand.cpp
   commands/certifygroupcommand.h
   commands/changeexpirycommand.cpp
   commands/changeexpirycommand.h
   commands/changeownertrustcommand.cpp
   commands/changeownertrustcommand.h
   commands/changepassphrasecommand.cpp
   commands/changepassphrasecommand.h
   commands/changepincommand.cpp
   commands/changepincommand.h
   commands/changeroottrustcommand.cpp
   commands/changeroottrustcommand.h
   commands/checksumcreatefilescommand.cpp
   commands/checksumcreatefilescommand.h
   commands/checksumverifyfilescommand.cpp
   commands/checksumverifyfilescommand.h
   commands/clearcrlcachecommand.cpp
   commands/clearcrlcachecommand.h
   commands/command.cpp
   commands/command.h
   commands/createcsrforcardkeycommand.cpp
   commands/createcsrforcardkeycommand.h
   commands/creategroupcommand.cpp
   commands/creategroupcommand.h
   commands/createopenpgpkeyfromcardkeyscommand.cpp
   commands/createopenpgpkeyfromcardkeyscommand.h
   commands/decryptverifyclipboardcommand.cpp
   commands/decryptverifyclipboardcommand.h
   commands/decryptverifyfilescommand.cpp
   commands/decryptverifyfilescommand.h
   commands/deletecertificatescommand.cpp
   commands/deletecertificatescommand.h
   commands/detailscommand.cpp
   commands/detailscommand.h
   commands/dumpcertificatecommand.cpp
   commands/dumpcertificatecommand.h
   commands/dumpcrlcachecommand.cpp
   commands/dumpcrlcachecommand.h
-  commands/encryptclipboardcommand.cpp
-  commands/encryptclipboardcommand.h
   commands/exportcertificatecommand.cpp
   commands/exportcertificatecommand.h
   commands/exportgroupscommand.cpp
   commands/exportgroupscommand.h
   commands/exportopenpgpcertstoservercommand.cpp
   commands/exportopenpgpcertstoservercommand.h
   commands/exportopenpgpcerttoprovidercommand.cpp
   commands/exportopenpgpcerttoprovidercommand.h
   commands/exportpaperkeycommand.cpp
   commands/exportpaperkeycommand.h
   commands/exportsecretkeycommand.cpp
   commands/exportsecretkeycommand.h
   commands/exportsecretsubkeycommand.cpp
   commands/exportsecretsubkeycommand.h
   commands/generateopenpgpcardkeysandcertificatecommand.cpp
   commands/generateopenpgpcardkeysandcertificatecommand.h
   commands/genrevokecommand.cpp
   commands/genrevokecommand.h
   commands/gnupgprocesscommand.cpp
   commands/gnupgprocesscommand.h
   commands/importcertificatefromclipboardcommand.cpp
   commands/importcertificatefromclipboardcommand.h
   commands/importcertificatefromdatacommand.cpp
   commands/importcertificatefromdatacommand.h
   commands/importcertificatefromfilecommand.cpp
   commands/importcertificatefromfilecommand.h
   commands/importcertificatefromkeyservercommand.cpp
   commands/importcertificatefromkeyservercommand.h
   commands/importcertificatefrompivcardcommand.cpp
   commands/importcertificatefrompivcardcommand.h
   commands/importcertificatescommand.cpp
   commands/importcertificatescommand.h
   commands/importcrlcommand.cpp
   commands/importcrlcommand.h
   commands/importpaperkeycommand.cpp
   commands/importpaperkeycommand.h
   commands/keytocardcommand.cpp
   commands/keytocardcommand.h
   commands/lookupcertificatescommand.cpp
   commands/lookupcertificatescommand.h
   commands/newcertificatesigningrequestcommand.cpp
   commands/newcertificatesigningrequestcommand.h
   commands/newopenpgpcertificatecommand.cpp
   commands/newopenpgpcertificatecommand.h
   commands/openpgpgeneratecardkeycommand.cpp
   commands/openpgpgeneratecardkeycommand.h
   commands/pivgeneratecardkeycommand.cpp
   commands/pivgeneratecardkeycommand.h
   commands/refreshcertificatescommand.cpp
   commands/refreshcertificatescommand.h
   commands/refreshopenpgpcertscommand.cpp
   commands/refreshopenpgpcertscommand.h
   commands/refreshx509certscommand.cpp
   commands/refreshx509certscommand.h
   commands/reloadkeyscommand.cpp
   commands/reloadkeyscommand.h
   commands/revokecertificationcommand.cpp
   commands/revokecertificationcommand.h
   commands/revokekeycommand.cpp
   commands/revokekeycommand.h
   commands/revokeuseridcommand.cpp
   commands/revokeuseridcommand.h
   commands/selftestcommand.cpp
   commands/selftestcommand.h
   commands/setinitialpincommand.cpp
   commands/setinitialpincommand.h
   commands/setpivcardapplicationadministrationkeycommand.cpp
   commands/setpivcardapplicationadministrationkeycommand.h
   commands/setprimaryuseridcommand.cpp
   commands/setprimaryuseridcommand.h
-  commands/signclipboardcommand.cpp
-  commands/signclipboardcommand.h
+  commands/signencryptclipboardcommand.cpp
+  commands/signencryptclipboardcommand.h
   commands/signencryptfilescommand.cpp
   commands/signencryptfilescommand.h
   commands/signencryptfoldercommand.cpp
   commands/signencryptfoldercommand.h
   commands/togglecertificateenabledcommand.cpp
   commands/togglecertificateenabledcommand.h
   commands/viewemailfilescommand.cpp
   commands/viewemailfilescommand.h
   conf/configuredialog.cpp
   conf/configuredialog.h
   conf/groupsconfigdialog.cpp
   conf/groupsconfigdialog.h
   conf/groupsconfigwidget.cpp
   conf/groupsconfigwidget.h
   crypto/autodecryptverifyfilescontroller.cpp
   crypto/autodecryptverifyfilescontroller.h
   crypto/certificateresolver.cpp
   crypto/certificateresolver.h
   crypto/checksumsutils_p.cpp
   crypto/checksumsutils_p.h
   crypto/controller.cpp
   crypto/controller.h
   crypto/createchecksumscontroller.cpp
   crypto/createchecksumscontroller.h
   crypto/decryptverifyemailcontroller.cpp
   crypto/decryptverifyemailcontroller.h
   crypto/decryptverifyfilescontroller.cpp
   crypto/decryptverifyfilescontroller.h
   crypto/decryptverifytask.cpp
   crypto/decryptverifytask.h
   crypto/encryptemailcontroller.cpp
   crypto/encryptemailcontroller.h
   crypto/encryptemailtask.cpp
   crypto/encryptemailtask.h
   crypto/gui/certificatelineedit.cpp
   crypto/gui/certificatelineedit.h
   crypto/gui/certificateselectionline.cpp
   crypto/gui/certificateselectionline.h
   crypto/gui/decryptverifyfilesdialog.cpp
   crypto/gui/decryptverifyfilesdialog.h
   crypto/gui/decryptverifyfileswizard.cpp
   crypto/gui/decryptverifyfileswizard.h
   crypto/gui/decryptverifyoperationwidget.cpp
   crypto/gui/decryptverifyoperationwidget.h
   crypto/gui/encryptemailwizard.cpp
   crypto/gui/encryptemailwizard.h
   crypto/gui/newresultpage.cpp
   crypto/gui/newresultpage.h
   crypto/gui/objectspage.cpp
   crypto/gui/objectspage.h
   crypto/gui/resolverecipientspage.cpp
   crypto/gui/resolverecipientspage.h
   crypto/gui/resultitemwidget.cpp
   crypto/gui/resultitemwidget.h
   crypto/gui/resultlistwidget.cpp
   crypto/gui/resultlistwidget.h
   crypto/gui/resultpage.cpp
   crypto/gui/resultpage.h
   crypto/gui/signemailwizard.cpp
   crypto/gui/signemailwizard.h
   crypto/gui/signencryptemailconflictdialog.cpp
   crypto/gui/signencryptemailconflictdialog.h
   crypto/gui/signencryptfilesdialog.cpp
   crypto/gui/signencryptfilesdialog.h
   crypto/gui/signencryptwidget.cpp
   crypto/gui/signencryptwidget.h
   crypto/gui/signencryptwizard.cpp
   crypto/gui/signencryptwizard.h
   crypto/gui/signerresolvepage.cpp
   crypto/gui/signerresolvepage.h
   crypto/gui/signingcertificateselectiondialog.cpp
   crypto/gui/signingcertificateselectiondialog.h
   crypto/gui/signingcertificateselectionwidget.cpp
   crypto/gui/signingcertificateselectionwidget.h
   crypto/gui/unknownrecipientwidget.cpp
   crypto/gui/unknownrecipientwidget.h
   crypto/gui/verifychecksumsdialog.cpp
   crypto/gui/verifychecksumsdialog.h
   crypto/gui/wizard.cpp
   crypto/gui/wizard.h
   crypto/gui/wizardpage.cpp
   crypto/gui/wizardpage.h
   crypto/newsignencryptemailcontroller.cpp
   crypto/newsignencryptemailcontroller.h
   crypto/recipient.cpp
   crypto/recipient.h
   crypto/sender.cpp
   crypto/sender.h
   crypto/signemailcontroller.cpp
   crypto/signemailcontroller.h
   crypto/signemailtask.cpp
   crypto/signemailtask.h
   crypto/signencryptfilescontroller.cpp
   crypto/signencryptfilescontroller.h
   crypto/signencrypttask.cpp
   crypto/signencrypttask.h
   crypto/task.cpp
   crypto/task.h
   crypto/taskcollection.cpp
   crypto/taskcollection.h
   crypto/verifychecksumscontroller.cpp
   crypto/verifychecksumscontroller.h
   dialogs/addsubkeydialog.cpp
   dialogs/addsubkeydialog.h
   dialogs/adduseriddialog.cpp
   dialogs/adduseriddialog.h
   dialogs/animatedexpander.cpp
   dialogs/animatedexpander.h
   dialogs/certificatedetailsdialog.cpp
   dialogs/certificatedetailsdialog.h
   dialogs/certificatedetailsinputwidget.cpp
   dialogs/certificatedetailsinputwidget.h
   dialogs/certificatedetailswidget.cpp
   dialogs/certificatedetailswidget.h
   dialogs/certificatedumpwidget.cpp
   dialogs/certificatedumpwidget.h
   dialogs/certificateselectiondialog.cpp
   dialogs/certificateselectiondialog.h
   dialogs/certifycertificatedialog.cpp
   dialogs/certifycertificatedialog.h
   dialogs/certifywidget.cpp
   dialogs/certifywidget.h
   dialogs/createcsrforcardkeydialog.cpp
   dialogs/createcsrforcardkeydialog.h
   dialogs/copytosmartcarddialog.cpp
   dialogs/copytosmartcarddialog.h
   dialogs/debugdialog.cpp
   dialogs/debugdialog.h
   dialogs/deletecertificatesdialog.cpp
   dialogs/deletecertificatesdialog.h
   dialogs/editgroupdialog.cpp
   dialogs/editgroupdialog.h
   dialogs/expirydialog.cpp
   dialogs/expirydialog.h
   dialogs/exportdialog.cpp
   dialogs/exportdialog.h
   dialogs/gencardkeydialog.cpp
   dialogs/gencardkeydialog.h
   dialogs/groupdetailsdialog.cpp
   dialogs/groupdetailsdialog.h
   dialogs/lookupcertificatesdialog.cpp
   dialogs/lookupcertificatesdialog.h
   dialogs/pivcardapplicationadministrationkeyinputdialog.cpp
   dialogs/pivcardapplicationadministrationkeyinputdialog.h
   dialogs/revokekeydialog.cpp
   dialogs/revokekeydialog.h
   dialogs/revokerswidget.cpp
   dialogs/revokerswidget.h
   dialogs/selftestdialog.cpp
   dialogs/selftestdialog.h
   dialogs/setinitialpindialog.cpp
   dialogs/setinitialpindialog.h
+  dialogs/signencryptclipboarddialog.cpp
+  dialogs/signencryptclipboarddialog.h
   dialogs/smartcardwindow.cpp
   dialogs/smartcardwindow.h
   dialogs/subkeyswidget.cpp
   dialogs/subkeyswidget.h
   dialogs/trustchainwidget.cpp
   dialogs/trustchainwidget.h
   dialogs/updatenotification.cpp
   dialogs/updatenotification.h
   dialogs/useridswidget.cpp
   dialogs/useridswidget.h
   dialogs/weboftrustwidget.cpp
   dialogs/weboftrustwidget.h
   interfaces/anchorprovider.h
   interfaces/focusfirstchild.h
   newcertificatewizard/advancedsettingsdialog.cpp
   newcertificatewizard/advancedsettingsdialog_p.h
   newcertificatewizard/enterdetailspage.cpp
   newcertificatewizard/enterdetailspage_p.h
   newcertificatewizard/keycreationpage.cpp
   newcertificatewizard/keycreationpage_p.h
   newcertificatewizard/listwidget.cpp
   newcertificatewizard/listwidget.h
   newcertificatewizard/newcertificatewizard.cpp
   newcertificatewizard/newcertificatewizard.h
   newcertificatewizard/resultpage.cpp
   newcertificatewizard/resultpage_p.h
   newcertificatewizard/wizardpage.cpp
   newcertificatewizard/wizardpage_p.h
   selftest/compliancecheck.cpp
   selftest/compliancecheck.h
   selftest/enginecheck.cpp
   selftest/enginecheck.h
   selftest/gpgagentcheck.cpp
   selftest/gpgagentcheck.h
   selftest/gpgconfcheck.cpp
   selftest/gpgconfcheck.h
   selftest/libkleopatrarccheck.cpp
   selftest/libkleopatrarccheck.h
   selftest/selftest.cpp
   selftest/selftest.h
   smartcard/algorithminfo.h
   smartcard/card.cpp
   smartcard/card.h
   smartcard/deviceinfowatcher.cpp
   smartcard/deviceinfowatcher.h
   smartcard/keypairinfo.cpp
   smartcard/keypairinfo.h
   smartcard/netkeycard.cpp
   smartcard/netkeycard.h
   smartcard/openpgpcard.cpp
   smartcard/openpgpcard.h
   smartcard/p15card.cpp
   smartcard/p15card.h
   smartcard/pivcard.cpp
   smartcard/pivcard.h
   smartcard/readerstatus.cpp
   smartcard/readerstatus.h
   smartcard/utils.cpp
   smartcard/utils.h
   utils/accessibility.cpp
   utils/accessibility.h
   utils/action_data.cpp
   utils/action_data.h
   utils/applicationstate.cpp
   utils/applicationstate.h
   utils/archivedefinition.cpp
   utils/archivedefinition.h
   utils/certificatepair.h
   utils/clipboardmenu.cpp
   utils/clipboardmenu.h
   utils/debug-helpers.cpp
   utils/debug-helpers.h
   utils/dragqueen.cpp
   utils/dragqueen.h
   utils/email.cpp
   utils/email.h
   utils/emptypassphraseprovider.cpp
   utils/emptypassphraseprovider.h
   utils/filedialog.cpp
   utils/filedialog.h
   utils/fileutils.cpp
   utils/fileutils.h
   utils/gui-helper.cpp
   utils/gui-helper.h
   utils/headerview.cpp
   utils/headerview.h
   utils/input.cpp
   utils/input.h
   utils/iodevicelogger.cpp
   utils/iodevicelogger.h
   utils/kdpipeiodevice.cpp
   utils/kdpipeiodevice.h
   utils/keyexportdraghandler.cpp
   utils/keyexportdraghandler.h
   utils/kuniqueservice.cpp
   utils/kuniqueservice.h
   utils/log.cpp
   utils/log.h
   utils/memory-helpers.h
   utils/migration.cpp
   utils/migration.h
   utils/output.cpp
   utils/output.h
   utils/overwritedialog.cpp
   utils/overwritedialog.h
   utils/path-helper.cpp
   utils/path-helper.h
   utils/scrollarea.cpp
   utils/scrollarea.h
   utils/statusmessage.cpp
   utils/statusmessage.h
   utils/systemtrayicon.cpp
   utils/systemtrayicon.h
   utils/tags.cpp
   utils/tags.h
   utils/types.cpp
   utils/types.h
   utils/userinfo.cpp
   utils/userinfo.h
   utils/writecertassuantransaction.cpp
   utils/writecertassuantransaction.h
   utils/wsastarter.cpp
   utils/wsastarter.h
   view/anchorcache.cpp
   view/anchorcache_p.h
   view/cardkeysview.cpp
   view/cardkeysview.h
   view/htmllabel.cpp
   view/htmllabel.h
   view/infofield.cpp
   view/infofield.h
   view/keycacheoverlay.cpp
   view/keycacheoverlay.h
   view/keylistcontroller.cpp
   view/keylistcontroller.h
   view/keytreeview.cpp
   view/keytreeview.h
   view/netkeywidget.cpp
   view/netkeywidget.h
   view/overlaywidget.cpp
   view/overlaywidget.h
   view/p15cardwidget.cpp
   view/p15cardwidget.h
   view/padwidget.cpp
   view/padwidget.h
   view/pgpcardwidget.cpp
   view/pgpcardwidget.h
   view/pivcardwidget.cpp
   view/pivcardwidget.h
   view/progressoverlay.cpp
   view/progressoverlay.h
   view/searchbar.cpp
   view/searchbar.h
   view/smartcardactions.cpp
   view/smartcardactions.h
   view/smartcardswidget.cpp
   view/smartcardswidget.h
   view/smartcardwidget.cpp
   view/smartcardwidget.h
   view/tabwidget.cpp
   view/tabwidget.h
   view/textoverlay.cpp
   view/textoverlay.h
   view/urllabel.cpp
   view/urllabel.h
   view/waitwidget.cpp
   view/waitwidget.h
   view/welcomewidget.cpp
   view/welcomewidget.h
 
   aboutdata.cpp
   aboutdata.h
   kleopatra.qrc
   kleopatraapplication.cpp
   kleopatraapplication.h
   main.cpp
   mainwindow.cpp
   mainwindow.h
   systrayicon.cpp
   systrayicon.h
   kleopatra_options.h
 )
 
 if(WIN32)
   configure_file (versioninfo.rc.in versioninfo.rc)
   configure_file (kleopatra.w32-manifest.in kleopatra.w32-manifest)
   set(_kleopatra_SRCS
     ${CMAKE_CURRENT_BINARY_DIR}/kleopatra.w32-manifest
     ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc
     conf/kmessageboxdontaskagainstorage.cpp
     conf/kmessageboxdontaskagainstorage.h
     ${_kleopatra_SRCS}
   )
 endif()
 
 set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp conf/kleopageconfigdialog.h ${_kleopatra_SRCS})
 
 ecm_qt_declare_logging_category(_kleopatra_SRCS HEADER kleopatra_debug.h IDENTIFIER KLEOPATRA_LOG CATEGORY_NAME org.kde.pim.kleopatra
         DESCRIPTION "kleopatra (kleopatra)"
         OLD_CATEGORY_NAMES log_kleopatra
         EXPORT KLEOPATRA
     )
 
 
 if(KLEO_MODEL_TEST)
   add_definitions(-DKLEO_MODEL_TEST)
   set(_kleopatra_SRCS ${_kleopatra_SRCS} models/modeltest.cpp)
 endif()
 
 ki18n_wrap_ui(_kleopatra_SRCS
   dialogs/setinitialpindialog.ui
   newcertificatewizard/listwidget.ui
 )
 
 kconfig_add_kcfg_files(_kleopatra_SRCS
   kcfg/emailoperationspreferences.kcfgc
   kcfg/fileoperationspreferences.kcfgc
   kcfg/settings.kcfgc
   kcfg/smimevalidationpreferences.kcfgc
   kcfg/tooltippreferences.kcfgc
 )
 
 file(GLOB ICONS_SRCS "${CMAKE_CURRENT_SOURCE_DIR}/icons/*-apps-kleopatra.png")
 ecm_add_app_icon(_kleopatra_SRCS ICONS ${ICONS_SRCS})
 
 add_executable(kleopatra_bin ${_kleopatra_SRCS} ${_kleopatra_uiserver_SRCS})
 
 # For the ConfigureDialog & KCMs
 target_link_libraries(kleopatra_bin kcm_kleopatra_static)
 
 #if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
 #    set_target_properties(kleopatra_bin PROPERTIES UNITY_BUILD ON)
 #endif()
 set_target_properties(kleopatra_bin PROPERTIES OUTPUT_NAME kleopatra)
 
 if (WIN32)
   set(_kleopatra_platform_libs "secur32")
 endif ()
 
 target_link_libraries(kleopatra_bin
   Gpgmepp
   KPim6::Libkleo
   KPim6::Mime
   KPim6::MimeTreeParserWidgets
   KF6::Codecs
   KF6::CoreAddons
   KF6::Crash
   KF6::I18n
   KF6::IconThemes
   KF6::ItemModels
   KF6::KIOCore
   KF6::KIOWidgets
   KF6::WindowSystem
   KF6::XmlGui
   Qt::Network
   Qt::PrintSupport # Printing secret keys
   kleopatraclientcore
   ${_kleopatra_extra_libs}
   ${_kleopatra_mail_libs}
   ${_kleopatra_uiserver_extra_libs}
   ${_kleopatra_dbusaddons_libs}
   ${_kleopatra_platform_libs}
 )
 
 target_link_libraries(kleopatra_bin QGpgmeQt6)
 
 install(TARGETS kleopatra_bin ${KDE_INSTALL_TARGETS_DEFAULT_ARGS})
 
 install(
   PROGRAMS data/org.kde.kleopatra.desktop data/kleopatra_import.desktop
   DESTINATION ${KDE_INSTALL_APPDIR}
 )
 install(FILES data/org.kde.kleopatra.appdata.xml DESTINATION ${KDE_INSTALL_METAINFODIR})
 install(FILES data/kleopatra-mime.xml DESTINATION ${KDE_INSTALL_MIMEDIR})
 install(
   PROGRAMS data/kleopatra_signencryptfiles.desktop
         data/kleopatra_signencryptfolders.desktop
         data/kleopatra_decryptverifyfiles.desktop
         data/kleopatra_decryptverifyfolders.desktop
   DESTINATION ${KDE_INSTALL_DATADIR}/kio/servicemenus
 )
 
 install(FILES kleopatradebugcommandsrc DESTINATION ${KDE_INSTALL_CONFDIR})
diff --git a/src/commands/signclipboardcommand.cpp b/src/commands/signclipboardcommand.cpp
deleted file mode 100644
index b94e2ca11..000000000
--- a/src/commands/signclipboardcommand.cpp
+++ /dev/null
@@ -1,178 +0,0 @@
-/* -*- mode: c++; c-basic-offset:4 -*-
-    commands/signclipboardcommand.cpp
-
-    This file is part of Kleopatra, the KDE keymanager
-    SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
-
-    SPDX-License-Identifier: GPL-2.0-or-later
-*/
-
-#include <config-kleopatra.h>
-
-#include "signclipboardcommand.h"
-
-#ifndef QT_NO_CLIPBOARD
-
-#include "command_p.h"
-
-#include <crypto/signemailcontroller.h>
-
-#include <utils/input.h>
-#include <utils/output.h>
-
-#include <Libkleo/Stl_Util>
-
-#include "kleopatra_debug.h"
-#include <KLocalizedString>
-
-#include <QApplication>
-#include <QClipboard>
-#include <QMimeData>
-
-#include <exception>
-
-using namespace Kleo;
-using namespace Kleo::Commands;
-using namespace Kleo::Crypto;
-
-class SignClipboardCommand::Private : public Command::Private
-{
-    friend class ::Kleo::Commands::SignClipboardCommand;
-    SignClipboardCommand *q_func() const
-    {
-        return static_cast<SignClipboardCommand *>(q);
-    }
-
-public:
-    explicit Private(SignClipboardCommand *qq, KeyListController *c);
-    ~Private() override;
-
-    void init();
-
-private:
-    void slotSignersResolved();
-    void slotControllerDone()
-    {
-        finished();
-    }
-    void slotControllerError(int, const QString &)
-    {
-        finished();
-    }
-
-private:
-    std::shared_ptr<const ExecutionContext> shared_qq;
-    std::shared_ptr<Input> input;
-    SignEMailController controller;
-};
-
-SignClipboardCommand::Private *SignClipboardCommand::d_func()
-{
-    return static_cast<Private *>(d.get());
-}
-const SignClipboardCommand::Private *SignClipboardCommand::d_func() const
-{
-    return static_cast<const Private *>(d.get());
-}
-
-#define d d_func()
-#define q q_func()
-
-SignClipboardCommand::Private::Private(SignClipboardCommand *qq, KeyListController *c)
-    : Command::Private(qq, c)
-    , shared_qq(qq, [](SignClipboardCommand *) {})
-    , input()
-    , controller(SignEMailController::ClipboardMode)
-{
-}
-
-SignClipboardCommand::Private::~Private()
-{
-    qCDebug(KLEOPATRA_LOG);
-}
-
-SignClipboardCommand::SignClipboardCommand(GpgME::Protocol protocol, KeyListController *c)
-    : Command(new Private(this, c))
-{
-    d->init();
-    d->controller.setProtocol(protocol);
-}
-
-SignClipboardCommand::SignClipboardCommand(GpgME::Protocol protocol, QAbstractItemView *v, KeyListController *c)
-    : Command(v, new Private(this, c))
-{
-    d->init();
-    d->controller.setProtocol(protocol);
-}
-
-void SignClipboardCommand::Private::init()
-{
-    controller.setExecutionContext(shared_qq);
-    controller.setDetachedSignature(false);
-    connect(&controller, &Controller::done, q, [this]() {
-        slotControllerDone();
-    });
-    connect(&controller, &Controller::error, q, [this](int err, const QString &details) {
-        slotControllerError(err, details);
-    });
-}
-
-SignClipboardCommand::~SignClipboardCommand()
-{
-    qCDebug(KLEOPATRA_LOG);
-}
-
-// static
-bool SignClipboardCommand::canSignCurrentClipboard()
-{
-    bool canSign = false;
-    if (const QClipboard *const clip = QApplication::clipboard()) {
-        if (const QMimeData *const mime = clip->mimeData()) {
-            canSign = mime->hasText();
-        }
-    }
-    return canSign;
-}
-
-void SignClipboardCommand::doStart()
-{
-    try {
-        // snapshot clipboard content here, in case it's being changed...
-        d->input = Input::createFromClipboard();
-
-        connect(&d->controller, &SignEMailController::signersResolved, this, [this]() {
-            d->slotSignersResolved();
-        });
-
-        d->controller.startResolveSigners();
-
-    } catch (const std::exception &e) {
-        d->information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Sign Clipboard Error"));
-        d->finished();
-    }
-}
-
-void SignClipboardCommand::Private::slotSignersResolved()
-{
-    try {
-        controller.setInputAndOutput(input, Output::createFromClipboard());
-        input.reset(); // no longer needed, so don't keep a reference
-        controller.start();
-    } catch (const std::exception &e) {
-        information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Sign Clipboard Error"));
-        finished();
-    }
-}
-
-void SignClipboardCommand::doCancel()
-{
-    qCDebug(KLEOPATRA_LOG);
-    d->controller.cancel();
-}
-
-#undef d
-#undef q
-
-#include "moc_signclipboardcommand.cpp"
-
-#endif // QT_NO_CLIPBOARD
diff --git a/src/commands/signencryptclipboardcommand.cpp b/src/commands/signencryptclipboardcommand.cpp
new file mode 100644
index 000000000..4ea1bcd31
--- /dev/null
+++ b/src/commands/signencryptclipboardcommand.cpp
@@ -0,0 +1,123 @@
+/* -*- mode: c++; c-basic-offset:4 -*-
+    commands/signclipboardcommand.cpp
+
+    This file is part of Kleopatra, the KDE keymanager
+    SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
+
+    SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include <config-kleopatra.h>
+
+#include "signencryptclipboardcommand.h"
+
+#ifndef QT_NO_CLIPBOARD
+
+#include "dialogs/signencryptclipboarddialog.h"
+
+#include "command_p.h"
+
+#include "kleopatra_debug.h"
+#include <KLocalizedString>
+
+#include <exception>
+
+#include <QApplication>
+#include <QClipboard>
+#include <QMimeData>
+
+using namespace Kleo;
+using namespace Kleo::Commands;
+
+class SignEncryptClipboardCommand::Private : public Command::Private
+{
+    friend class ::Kleo::Commands::SignEncryptClipboardCommand;
+    SignEncryptClipboardCommand *q_func() const
+    {
+        return static_cast<SignEncryptClipboardCommand *>(q);
+    }
+
+public:
+    explicit Private(SignEncryptClipboardCommand *qq, KeyListController *c);
+    ~Private() override;
+
+    void init();
+    void slotDialogRejected();
+
+private:
+    std::unique_ptr<SignEncryptClipboardDialog> dialog;
+};
+
+SignEncryptClipboardCommand::Private *SignEncryptClipboardCommand::d_func()
+{
+    return static_cast<Private *>(d.get());
+}
+const SignEncryptClipboardCommand::Private *SignEncryptClipboardCommand::d_func() const
+{
+    return static_cast<const Private *>(d.get());
+}
+
+#define d d_func()
+#define q q_func()
+
+SignEncryptClipboardCommand::Private::Private(SignEncryptClipboardCommand *qq, KeyListController *c)
+    : Command::Private(qq, c)
+{
+}
+
+SignEncryptClipboardCommand::Private::~Private()
+{
+    qCDebug(KLEOPATRA_LOG);
+}
+
+void SignEncryptClipboardCommand::Private::slotDialogRejected()
+{
+    canceled();
+}
+
+SignEncryptClipboardCommand::SignEncryptClipboardCommand(KeyListController *c)
+    : Command(new Private(this, c))
+{
+}
+
+SignEncryptClipboardCommand::~SignEncryptClipboardCommand()
+{
+    qCDebug(KLEOPATRA_LOG);
+}
+
+void SignEncryptClipboardCommand::doStart()
+{
+    try {
+        d->dialog = std::make_unique<SignEncryptClipboardDialog>();
+    } catch (const std::exception &e) {
+        d->information(i18n("An error occurred: %1", QString::fromLocal8Bit(e.what())), i18n("Sign Clipboard Error"));
+        d->finished();
+    }
+    d->dialog->show();
+    connect(d->dialog.get(), &QDialog::rejected, this, [this]() {
+        d->slotDialogRejected();
+    });
+}
+
+void SignEncryptClipboardCommand::doCancel()
+{
+    qCDebug(KLEOPATRA_LOG);
+    d->dialog->deleteLater();
+}
+
+bool SignEncryptClipboardCommand::canSignEncryptCurrentClipboard()
+{
+    if (const auto &clip = QApplication::clipboard()) {
+        if (const auto &mime = clip->mimeData()) {
+            return mime->hasText();
+        }
+    }
+    return false;
+}
+
+#undef d
+#undef q
+
+#include "moc_signencryptclipboardcommand.cpp"
+
+#endif // QT_NO_CLIPBOARD
diff --git a/src/commands/signclipboardcommand.h b/src/commands/signencryptclipboardcommand.h
similarity index 60%
rename from src/commands/signclipboardcommand.h
rename to src/commands/signencryptclipboardcommand.h
index d33035176..02837c4c1 100644
--- a/src/commands/signclipboardcommand.h
+++ b/src/commands/signencryptclipboardcommand.h
@@ -1,48 +1,43 @@
 /* -*- mode: c++; c-basic-offset:4 -*-
     commands/signclipboardcommand.h
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #pragma once
 
 #include <commands/command.h>
 
 #ifndef QT_NO_CLIPBOARD
 
-#include <utils/types.h>
-
-#include <gpgme++/global.h>
-
 namespace Kleo
 {
 namespace Commands
 {
 
-class SignClipboardCommand : public Command
+class SignEncryptClipboardCommand : public Command
 {
     Q_OBJECT
 public:
-    explicit SignClipboardCommand(GpgME::Protocol protocol, QAbstractItemView *view, KeyListController *parent);
-    explicit SignClipboardCommand(GpgME::Protocol protocol, KeyListController *parent);
-    ~SignClipboardCommand() override;
+    explicit SignEncryptClipboardCommand(KeyListController *parent);
+    ~SignEncryptClipboardCommand() override;
 
-    static bool canSignCurrentClipboard();
+    static bool canSignEncryptCurrentClipboard();
 
 private:
     void doStart() override;
     void doCancel() override;
 
 private:
     class Private;
     inline Private *d_func();
     inline const Private *d_func() const;
 };
 
 }
 }
 
 #endif // QT_NO_CLIPBOARD
diff --git a/src/crypto/gui/resultpage.cpp b/src/crypto/gui/resultpage.cpp
index 9025916a8..6c8a5698e 100644
--- a/src/crypto/gui/resultpage.cpp
+++ b/src/crypto/gui/resultpage.cpp
@@ -1,179 +1,178 @@
 /* -*- mode: c++; c-basic-offset:4 -*-
     crypto/gui/resultpage.cpp
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include <config-kleopatra.h>
 
 #include "resultitemwidget.h"
 #include "resultlistwidget.h"
 #include "resultpage.h"
 
 #include <crypto/taskcollection.h>
 
 #include <utils/scrollarea.h>
 
 #include <KLocalizedString>
 
 #include <QCheckBox>
 #include <QHash>
 #include <QLabel>
 #include <QProgressBar>
 #include <QVBoxLayout>
 
 using namespace Kleo;
 using namespace Kleo::Crypto;
 using namespace Kleo::Crypto::Gui;
 
 class ResultPage::Private
 {
     ResultPage *const q;
 
 public:
     explicit Private(ResultPage *qq);
 
     void progress(int progress, int total);
     void result(const std::shared_ptr<const Task::Result> &result);
     void started(const std::shared_ptr<Task> &result);
     void allDone();
     QLabel *labelForTag(const QString &tag);
 
     std::shared_ptr<TaskCollection> m_tasks;
     QProgressBar *m_progressBar;
     QHash<QString, QLabel *> m_progressLabelByTag;
     QVBoxLayout *m_progressLabelLayout;
     int m_lastErrorItemIndex = 0;
     ResultListWidget *m_resultList;
     QCheckBox *m_keepOpenCB;
 };
 
 ResultPage::Private::Private(ResultPage *qq)
     : q(qq)
 {
     QBoxLayout *const layout = new QVBoxLayout(q);
     auto const labels = new QWidget;
     m_progressLabelLayout = new QVBoxLayout(labels);
     layout->addWidget(labels);
     m_progressBar = new QProgressBar;
     layout->addWidget(m_progressBar);
     m_resultList = new ResultListWidget;
     layout->addWidget(m_resultList);
     m_keepOpenCB = new QCheckBox;
     m_keepOpenCB->setText(i18n("Keep open after operation completed"));
     m_keepOpenCB->setChecked(true);
     layout->addWidget(m_keepOpenCB);
 }
 
 void ResultPage::Private::progress(int progress, int total)
 {
     Q_ASSERT(progress >= 0);
     Q_ASSERT(total >= 0);
     m_progressBar->setRange(0, total);
     m_progressBar->setValue(progress);
 }
 
 void ResultPage::Private::allDone()
 {
     Q_ASSERT(m_tasks);
     q->setAutoAdvance(!m_keepOpenCB->isChecked() && !m_tasks->errorOccurred());
-    m_progressBar->setRange(0, 100);
-    m_progressBar->setValue(100);
+    m_progressBar->setVisible(false);
     m_tasks.reset();
     const auto progressLabelByTagKeys{m_progressLabelByTag.keys()};
     for (const QString &i : progressLabelByTagKeys) {
         if (!i.isEmpty()) {
-            m_progressLabelByTag.value(i)->setText(i18n("%1: All operations completed.", i));
+            m_progressLabelByTag.value(i)->setText({});
         } else {
-            m_progressLabelByTag.value(i)->setText(i18n("All operations completed."));
+            m_progressLabelByTag.value(i)->setText({});
         }
     }
     Q_EMIT q->completeChanged();
 }
 
 void ResultPage::Private::result(const std::shared_ptr<const Task::Result> &)
 {
 }
 
 void ResultPage::Private::started(const std::shared_ptr<Task> &task)
 {
     Q_ASSERT(task);
     const QString tag = task->tag();
     QLabel *const label = labelForTag(tag);
     Q_ASSERT(label);
     if (tag.isEmpty()) {
         label->setText(i18nc("number, operation description", "Operation %1: %2", m_tasks->numberOfCompletedTasks() + 1, task->label()));
     } else {
         label->setText(i18nc(R"(tag( "OpenPGP" or "CMS"),  operation description)", "%1: %2", tag, task->label()));
     }
 }
 
 ResultPage::ResultPage(QWidget *parent, Qt::WindowFlags flags)
     : WizardPage(parent, flags)
     , d(new Private(this))
 {
     setTitle(i18n("<b>Results</b>"));
 }
 
 ResultPage::~ResultPage()
 {
 }
 
 bool ResultPage::keepOpenWhenDone() const
 {
     return d->m_keepOpenCB->isChecked();
 }
 
 void ResultPage::setKeepOpenWhenDone(bool keep)
 {
     d->m_keepOpenCB->setChecked(keep);
 }
 
 void ResultPage::setTaskCollection(const std::shared_ptr<TaskCollection> &coll)
 {
     Q_ASSERT(!d->m_tasks);
     if (d->m_tasks == coll) {
         return;
     }
     d->m_tasks = coll;
     Q_ASSERT(d->m_tasks);
     d->m_resultList->setTaskCollection(coll);
     connect(d->m_tasks.get(), &TaskCollection::progress, this, [this](int current, int total) {
         d->progress(current, total);
     });
     connect(d->m_tasks.get(), SIGNAL(done()), this, SLOT(allDone()));
     connect(d->m_tasks.get(),
             SIGNAL(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)),
             this,
             SLOT(result(std::shared_ptr<const Kleo::Crypto::Task::Result>)));
     connect(d->m_tasks.get(), SIGNAL(started(std::shared_ptr<Kleo::Crypto::Task>)), this, SLOT(started(std::shared_ptr<Kleo::Crypto::Task>)));
 
     for (const std::shared_ptr<Task> &i : d->m_tasks->tasks()) { // create labels for all tags in collection
         Q_ASSERT(i && d->labelForTag(i->tag()));
         Q_UNUSED(i)
     }
     Q_EMIT completeChanged();
 }
 
 QLabel *ResultPage::Private::labelForTag(const QString &tag)
 {
     if (QLabel *const label = m_progressLabelByTag.value(tag)) {
         return label;
     }
     auto label = new QLabel;
     label->setTextFormat(Qt::RichText);
     label->setWordWrap(true);
     m_progressLabelLayout->addWidget(label);
     m_progressLabelByTag.insert(tag, label);
     return label;
 }
 
 bool ResultPage::isComplete() const
 {
     return d->m_tasks ? d->m_tasks->allTasksCompleted() : true;
 }
 
 #include "moc_resultpage.cpp"
diff --git a/src/crypto/gui/signencryptfilesdialog.cpp b/src/crypto/gui/signencryptfilesdialog.cpp
index b976e06ab..1126b15ef 100644
--- a/src/crypto/gui/signencryptfilesdialog.cpp
+++ b/src/crypto/gui/signencryptfilesdialog.cpp
@@ -1,688 +1,676 @@
 /*  crypto/gui/signencryptfileswizard.cpp
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
 
     SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
     SPDX-FileContributor: Intevation GmbH
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include <config-kleopatra.h>
 
 #include "kleopatra_debug.h"
 
 #include "signencryptfilesdialog.h"
 #include "signencryptwidget.h"
 
 #include "resultpage.h"
 #include "utils/scrollarea.h"
 
 #include <fileoperationspreferences.h>
 #include <settings.h>
 
 #include <KColorScheme>
 #include <KConfigGroup>
 #include <KLocalizedString>
 #include <KMessageBox>
 #include <KMessageWidget>
 #include <KSeparator>
 #include <KSharedConfig>
 #include <KTitleWidget>
 
 #include <Libkleo/Compliance>
 #include <Libkleo/FileNameRequester>
 #include <Libkleo/Formatting>
 #include <Libkleo/GnuPG>
 #include <Libkleo/SystemInfo>
 
 #include <QCheckBox>
 #include <QGroupBox>
 #include <QIcon>
 #include <QLabel>
 #include <QPushButton>
 #include <QStackedLayout>
 #include <QStyle>
 #include <QVBoxLayout>
 #include <QWindow>
 
 #include <gpgme++/key.h>
 
 #include <array>
 
 using namespace GpgME;
 using namespace Kleo;
 using namespace Kleo::Crypto::Gui;
 
 class FileNameRequesterWithIcon : public QWidget
 {
     Q_OBJECT
 
 public:
     explicit FileNameRequesterWithIcon(QDir::Filters filter, QWidget *parent = nullptr)
         : QWidget(parent)
     {
         auto layout = new QHBoxLayout{this};
         layout->setContentsMargins(0, 0, 0, 0);
         mIconLabel = new QLabel{this};
         mRequester = new FileNameRequester{filter, this};
         mRequester->setExistingOnly(false);
         layout->addWidget(mIconLabel);
         layout->addWidget(mRequester);
 
         setFocusPolicy(mRequester->focusPolicy());
         setFocusProxy(mRequester);
 
         connect(mRequester, &FileNameRequester::fileNameChanged, this, &FileNameRequesterWithIcon::fileNameChanged);
     }
 
     void setIcon(const QIcon &icon)
     {
         mIconLabel->setPixmap(icon.pixmap(32, 32));
     }
 
     void setFileName(const QString &name)
     {
         mRequester->setFileName(name);
     }
 
     QString fileName() const
     {
         return mRequester->fileName();
     }
 
     void setNameFilter(const QString &nameFilter)
     {
         mRequester->setNameFilter(nameFilter);
     }
 
     QString nameFilter() const
     {
         return mRequester->nameFilter();
     }
 
     FileNameRequester *requester()
     {
         return mRequester;
     }
 
 Q_SIGNALS:
     void fileNameChanged(const QString &filename);
 
 protected:
     bool event(QEvent *e) override
     {
         if (e->type() == QEvent::ToolTipChange) {
             mRequester->setToolTip(toolTip());
         }
         return QWidget::event(e);
     }
 
 private:
     QLabel *mIconLabel;
     FileNameRequester *mRequester;
 };
 
 class SigEncPage : public QWidget
 {
     Q_OBJECT
 
 public:
     explicit SigEncPage(QWidget *parent = nullptr)
         : QWidget(parent)
         , mWidget(new SignEncryptWidget)
         , mOutLayout(new QVBoxLayout)
         , mOutputLabel{nullptr}
         , mArchive(false)
         , mUseOutputDir(false)
         , mSingleFile{true}
     {
         auto mainLayout = new QVBoxLayout(this);
         mainLayout->setContentsMargins({});
         auto scrollArea = new Kleo::ScrollArea;
         mainLayout->addWidget(scrollArea);
 
         auto wrapper = new QWidget;
         scrollArea->setWidget(wrapper);
 
         scrollArea->setFrameStyle(0);
         auto vLay = new QVBoxLayout(wrapper);
         vLay->setContentsMargins({});
 
         if (!Settings{}.cmsEnabled()) {
             mWidget->setProtocol(GpgME::OpenPGP);
         }
         mWidget->setSignAsText(i18nc("@option:check on SignEncryptPage", "&Sign as:"));
         mWidget->setEncryptForMeText(i18nc("@option:check on SignEncryptPage", "Encrypt for &me:"));
         mWidget->setEncryptForOthersText(i18nc("@label on SignEncryptPage", "Encrypt for &others:"));
         mWidget->setEncryptWithPasswordText(i18nc("@option:check on SignEncryptPage", "Encrypt with &password:"));
         vLay->addWidget(mWidget);
         connect(mWidget, &SignEncryptWidget::operationChanged, this, &SigEncPage::checkReady);
         connect(mWidget, &SignEncryptWidget::keysChanged, this, &SigEncPage::updateFileWidgets);
 
         vLay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3);
 
         vLay->addLayout(mOutLayout);
 
         mPlaceholderWidget = new QLabel(i18nc("@label:textbox", "Please select an action."));
         mOutLayout->addWidget(mPlaceholderWidget);
 
         mOutputLabel = new QLabel(i18nc("@label on SignEncryptPage", "Output &files/folder:"));
         auto font = mOutputLabel->font();
         font.setWeight(QFont::DemiBold);
         mOutputLabel->setFont(font);
         mOutLayout->addWidget(mOutputLabel);
 
         createRequesters(mOutLayout);
 
         mUseOutputDirChk = new QCheckBox(i18nc("@option:check on SignEncryptPage", "Encrypt / Sign &each file separately."));
         mUseOutputDirChk->setToolTip(i18nc("@info:tooltip", "Keep each file separate instead of creating an archive for all."));
         mOutLayout->addWidget(mUseOutputDirChk);
         connect(mUseOutputDirChk, &QCheckBox::toggled, this, [this](bool state) {
             mUseOutputDir = state;
             mArchive = !mUseOutputDir && !mSingleFile;
             updateFileWidgets();
         });
 
         auto messageWidget = new KMessageWidget;
         messageWidget->setMessageType(KMessageWidget::Error);
         messageWidget->setIcon(style()->standardIcon(QStyle::SP_MessageBoxCritical, nullptr, this));
         messageWidget->setText(i18n("Invalid compliance settings for signing and encrypting files."));
         messageWidget->setToolTip(xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
                                          "<para>You cannot use <application>Kleopatra</application> for signing or encrypting files "
                                          "because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>",
                                          DeVSCompliance::name(true)));
         messageWidget->setCloseButtonVisible(false);
         messageWidget->setVisible(DeVSCompliance::isActive() && !DeVSCompliance::isCompliant());
         vLay->addWidget(messageWidget);
 
         setMinimumHeight(300);
         vLay->addStretch();
     }
 
     void setEncryptionPreset(bool value)
     {
         mWidget->setEncryptionChecked(value);
     }
 
     void setSigningPreset(bool value)
     {
         mWidget->setSigningChecked(value);
     }
 
     void setArchiveForced(bool archive)
     {
         mArchive = archive;
         setArchiveMutable(!archive);
     }
 
     void setArchiveMutable(bool archive)
     {
         mUseOutputDirChk->setVisible(archive);
         if (archive) {
             const KConfigGroup archCfg(KSharedConfig::openConfig(), QStringLiteral("SignEncryptFilesWizard"));
             mUseOutputDirChk->setChecked(archCfg.readEntry("LastUseOutputDir", false));
         } else {
             mUseOutputDirChk->setChecked(false);
         }
     }
 
     void setSingleFile(bool singleFile)
     {
         mSingleFile = singleFile;
         mArchive = !mUseOutputDir && !mSingleFile;
     }
 
     bool validatePage()
     {
-        if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) {
-            return false;
-        }
-
         return mWidget->isComplete();
     }
 
     std::vector<Key> recipients() const
     {
         return mWidget->recipients();
     }
 
     /* In the future we might find a usecase for multiple
      * signers */
     std::vector<Key> signers() const
     {
         const Key k = mWidget->signUserId().parent();
         if (!k.isNull()) {
             return {k};
         }
         return {};
     }
 
     void done()
     {
         mWidget->saveOwnKeys();
         if (mUseOutputDirChk->isVisible()) {
             KConfigGroup archCfg(KSharedConfig::openConfig(), QStringLiteral("SignEncryptFilesDialog"));
             archCfg.writeEntry("LastUseOutputDir", mUseOutputDir);
         }
 
         auto sign = !mWidget->signUserId().isNull();
         auto encrypt = !mWidget->selfUserId().isNull() || !mWidget->recipients().empty();
         if (!mWidget->validate()) {
             return;
         }
         if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) {
             KMessageBox::error(topLevelWidget(),
                                xi18nc("@info %1 is a placeholder for the name of a compliance mode. E.g. NATO RESTRICTED compliant or VS-NfD compliant",
                                       "<para>Sorry! You cannot use <application>Kleopatra</application> for signing or encrypting files "
                                       "because the <application>GnuPG</application> system used by <application>Kleopatra</application> is not %1.</para>",
                                       DeVSCompliance::name(true)));
             return;
         }
 
         if (sign && !encrypt && mArchive) {
             auto status = KMessageBox::warningContinueCancel(
                 this,
                 xi18nc("@info",
                        "<para>Archiving in combination with sign-only currently requires what are known as opaque signatures - "
                        "unlike detached ones, these embed the content in the signature.</para>"
                        "<para>This format is rather unusual. You might want to archive the files separately, "
                        "and then sign the archive as one file with Kleopatra.</para>"
                        "<para>Future versions of Kleopatra are expected to also support detached signatures in this case.</para>"),
                 i18nc("@title:window", "Unusual Signature Warning"),
                 KStandardGuiItem::cont(),
                 KStandardGuiItem::cancel(),
                 QStringLiteral("signencryptfileswizard-archive+sign-only-warning"));
             if (status != KMessageBox::Continue) {
                 return;
             }
         }
 
         if (encrypt && !mWidget->encryptSymmetric() && std::ranges::none_of(recipients(), [](const auto &k) {
                 return k.hasSecret();
             })) {
             if (KMessageBox::warningContinueCancel(this,
                                                    xi18nc("@info",
                                                           "<para>None of the recipients you are encrypting to seems to be your own.</para>"
                                                           "<para>This means that you will not be able to decrypt the data anymore, once encrypted.</para>"
                                                           "<para>Do you want to continue, or cancel to change the recipient selection?</para>"),
                                                    i18nc("@title:window", "Encrypt-To-Self Warning"),
                                                    KStandardGuiItem::cont(),
                                                    KStandardGuiItem::cancel(),
                                                    QStringLiteral("warn-encrypt-to-non-self"),
                                                    KMessageBox::Notify | KMessageBox::Dangerous)
                 == KMessageBox::Cancel) {
                 return;
             }
         }
         Q_EMIT finished();
     }
 
     bool isDeVsAndValid() const
     {
         return mWidget->isDeVsAndValid();
     }
 
+    QString continueButtonText() const
+    {
+        return mWidget->continueButtonText();
+    }
+
 private:
     struct RequesterInfo {
         SignEncryptFilesDialog::KindNames id;
         QString icon;
         QString toolTip;
         QString accessibleName;
         QString nameFilterBinary;
         QString nameFilterAscii;
     };
     void createRequesters(QBoxLayout *lay)
     {
         static const std::array<RequesterInfo, 6> requestersInfo = {{
             {
                 SignEncryptFilesDialog::SignatureCMS,
                 QStringLiteral("document-sign"),
                 i18nc("@info:tooltip", "This is the filename of the S/MIME signature."),
                 i18nc("Lineedit accessible name", "S/MIME signature file"),
                 i18nc("Name filter binary", "S/MIME Signatures (*.p7s)"),
                 i18nc("Name filter ASCII", "S/MIME Signatures (*.p7s *.pem)"),
             },
             {
                 SignEncryptFilesDialog::SignaturePGP,
                 QStringLiteral("document-sign"),
                 i18nc("@info:tooltip", "This is the filename of the detached OpenPGP signature."),
                 i18nc("Lineedit accessible name", "OpenPGP signature file"),
                 i18nc("Name filter binary", "OpenPGP Signatures (*.sig *.pgp)"),
                 i18nc("Name filter ASCII", "OpenPGP Signatures (*.asc *.sig)"),
             },
             {
                 SignEncryptFilesDialog::CombinedPGP,
                 QStringLiteral("document-edit-sign-encrypt"),
                 i18nc("@info:tooltip", "This is the filename of the OpenPGP-signed and encrypted file."),
                 i18nc("Lineedit accessible name", "OpenPGP signed and encrypted file"),
                 i18nc("Name filter binary", "OpenPGP Files (*.gpg *.pgp)"),
                 i18nc("Name filter ASCII", "OpenPGP Files (*.asc)"),
             },
             {
                 SignEncryptFilesDialog::EncryptedPGP,
                 QStringLiteral("document-encrypt"),
                 i18nc("@info:tooltip", "This is the filename of the OpenPGP encrypted file."),
                 i18nc("Lineedit accessible name", "OpenPGP encrypted file"),
                 i18nc("Name filter binary", "OpenPGP Files (*.gpg *.pgp)"),
                 i18nc("Name filter ASCII", "OpenPGP Files (*.asc)"),
             },
             {
                 SignEncryptFilesDialog::EncryptedCMS,
                 QStringLiteral("document-encrypt"),
                 i18nc("@info:tooltip", "This is the filename of the S/MIME encrypted file."),
                 i18nc("Lineedit accessible name", "S/MIME encrypted file"),
                 i18nc("Name filter binary", "S/MIME Files (*.p7m)"),
                 i18nc("Name filter ASCII", "S/MIME Files (*.p7m *.pem)"),
             },
             {
                 SignEncryptFilesDialog::Directory,
                 QStringLiteral("folder"),
                 i18nc("@info:tooltip", "The resulting files are written to this directory."),
                 i18nc("Lineedit accessible name", "Output directory"),
                 {},
                 {},
             },
         }};
 
         if (!mRequesters.empty()) {
             return;
         }
         const bool isAscii = FileOperationsPreferences().addASCIIArmor();
         for (const auto &requester : requestersInfo) {
             const auto id = requester.id;
             auto requesterWithIcon = new FileNameRequesterWithIcon{id == SignEncryptFilesDialog::Directory ? QDir::Dirs : QDir::Files, this};
             requesterWithIcon->setIcon(QIcon::fromTheme(requester.icon));
             requesterWithIcon->setToolTip(requester.toolTip);
             requesterWithIcon->requester()->setAccessibleNameOfLineEdit(requester.accessibleName);
             requesterWithIcon->setNameFilter(isAscii ? requester.nameFilterAscii : requester.nameFilterBinary);
             lay->addWidget(requesterWithIcon);
 
             connect(requesterWithIcon, &FileNameRequesterWithIcon::fileNameChanged, this, [this, id](const QString &newName) {
                 mOutNames[id] = newName;
             });
 
             mRequesters.insert(id, requesterWithIcon);
         }
     }
 
 public:
     void setOutputNames(const QMap<int, QString> &names)
     {
         Q_ASSERT(mOutNames.isEmpty());
         for (auto it = std::begin(names); it != std::end(names); ++it) {
             mRequesters.value(it.key())->setFileName(it.value());
         }
         mOutNames = names;
         updateFileWidgets();
     }
 
     QMap<int, QString> outputNames() const
     {
         if (!mUseOutputDir) {
             auto ret = mOutNames;
             ret.remove(SignEncryptFilesDialog::Directory);
             return ret;
         }
         return mOutNames;
     }
 
     bool encryptSymmetric() const
     {
         return mWidget->encryptSymmetric();
     }
 
 private Q_SLOTS:
     void updateFileWidgets()
     {
         if (mRequesters.isEmpty()) {
             return;
         }
         const std::vector<Key> recipients = mWidget->recipients();
         const Key sigKey = mWidget->signUserId().parent();
         const bool pgp = mWidget->encryptSymmetric() || std::any_of(std::cbegin(recipients), std::cend(recipients), [](const auto &k) {
                              return k.protocol() == Protocol::OpenPGP;
                          });
         const bool cms = std::any_of(std::cbegin(recipients), std::cend(recipients), [](const auto &k) {
             return k.protocol() == Protocol::CMS;
         });
 
         mOutLayout->setEnabled(false);
         if (cms || pgp || !sigKey.isNull()) {
             mPlaceholderWidget->setVisible(false);
             mOutputLabel->setVisible(true);
             mRequesters[SignEncryptFilesDialog::SignatureCMS]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::CMS);
             mRequesters[SignEncryptFilesDialog::EncryptedCMS]->setVisible(!mUseOutputDir && cms);
             mRequesters[SignEncryptFilesDialog::CombinedPGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && pgp);
             mRequesters[SignEncryptFilesDialog::EncryptedPGP]->setVisible(!mUseOutputDir && sigKey.protocol() != Protocol::OpenPGP && pgp);
             mRequesters[SignEncryptFilesDialog::SignaturePGP]->setVisible(!mUseOutputDir && sigKey.protocol() == Protocol::OpenPGP && !pgp);
             mRequesters[SignEncryptFilesDialog::Directory]->setVisible(mUseOutputDir);
             auto firstNotHidden = std::find_if(std::cbegin(mRequesters), std::cend(mRequesters), [](auto w) {
                 return !w->isHidden();
             });
             mOutputLabel->setBuddy(*firstNotHidden);
         } else {
             mPlaceholderWidget->setVisible(true);
             mOutputLabel->setVisible(false);
             std::for_each(std::cbegin(mRequesters), std::cend(mRequesters), [](auto w) {
                 w->setVisible(false);
             });
             mOutputLabel->setBuddy(nullptr);
         }
         mOutLayout->setEnabled(true);
         Q_EMIT checkReady(mWidget->currentOp());
     }
 
 Q_SIGNALS:
     void finished();
     void checkReady(SignEncryptWidget::Operations op);
 
 private:
     SignEncryptWidget *mWidget;
     QMap<int, QString> mOutNames;
     QMap<int, FileNameRequesterWithIcon *> mRequesters;
     QVBoxLayout *mOutLayout;
     QWidget *mPlaceholderWidget;
     QCheckBox *mUseOutputDirChk;
     QLabel *mOutputLabel;
     bool mArchive;
     bool mUseOutputDir;
     bool mSingleFile;
 };
 
 class SignEncryptResultPage : public Kleo::Crypto::Gui::ResultPage
 {
     Q_OBJECT
 
 public:
     explicit SignEncryptResultPage(QWidget *parent = nullptr)
         : ResultPage(parent)
     {
         setTitle(i18nc("@title", "Results"));
         setSubTitle(i18nc("@title", "Status and progress of the crypto operations is shown here."));
     }
 };
 
 SignEncryptFilesDialog::SignEncryptFilesDialog(QWidget *parent, Qt::WindowFlags f)
     : QDialog(parent, f)
 {
     readConfig();
 
     setWindowTitle(i18nc("@title", "Sign / Encrypt Files"));
 
     mSigEncPage = new SigEncPage;
     mResultPage = new SignEncryptResultPage(this);
     mResultPage->setVisible(false);
     auto layout = new QVBoxLayout(this);
 
     auto title = new KTitleWidget;
     title->setText(i18nc("@title:dialog", "Sign / Encrypt Files"));
     layout->addWidget(title);
 
     auto stackedLayout = new QStackedLayout;
     stackedLayout->addWidget(mSigEncPage);
     stackedLayout->addWidget(mResultPage);
     layout->addLayout(stackedLayout);
 
     auto buttons = new QDialogButtonBox;
 
     QPushButton *labelButton = nullptr;
 
     if (DeVSCompliance::isActive()) {
         /* We use a custom button to display a label next to the
            buttons. */
         labelButton = buttons->addButton(QString(), QDialogButtonBox::ActionRole);
         /* We style the button so that it looks and acts like a
            label.  */
         labelButton->setStyleSheet(QStringLiteral("border: none"));
         labelButton->setFocusPolicy(Qt::NoFocus);
     }
 
     auto okButton = buttons->addButton(i18nc("@action:button", "Continue"), QDialogButtonBox::ActionRole);
     auto cancelButton = buttons->addButton(QDialogButtonBox::Cancel);
     connect(cancelButton, &QPushButton::clicked, this, &QDialog::reject);
     connect(okButton, &QPushButton::clicked, this, [this]() {
         mSigEncPage->done();
     });
     connect(mSigEncPage, &SigEncPage::finished, this, [this, title, okButton, stackedLayout]() {
         if (stackedLayout->currentIndex() == 0) {
             stackedLayout->setCurrentIndex(1);
             Q_EMIT operationPrepared();
             title->setText(i18nc("@title:dialog", "Results"));
             okButton->setText(i18nc("@action:button", "Finish"));
         } else {
             accept();
         }
     });
 
     connect(mSigEncPage, &SigEncPage::checkReady, this, [this, okButton, labelButton](const auto op) {
-        QString label;
-        switch (op) {
-        case SignEncryptWidget::Sign:
-            label = i18nc("@action:button", "Sign");
-            break;
-        case SignEncryptWidget::Encrypt:
-            label = i18nc("@action:button", "Encrypt");
-            break;
-        case SignEncryptWidget::SignAndEncrypt:
-            label = i18nc("@action:button", "Sign / Encrypt");
-            break;
-        default:;
-        };
-        if (!label.isEmpty()) {
-            okButton->setText(label);
-            if (DeVSCompliance::isActive()) {
-                const bool de_vs = DeVSCompliance::isCompliant() && mSigEncPage->isDeVsAndValid();
-                DeVSCompliance::decorate(okButton, de_vs);
-
-                okButton->setToolTip(DeVSCompliance::name(de_vs));
-                labelButton->setText(DeVSCompliance::name(de_vs));
-            }
+        okButton->setText(mSigEncPage->continueButtonText());
+        if (mSigEncPage->validatePage() && DeVSCompliance::isActive()) {
+            const bool de_vs = DeVSCompliance::isCompliant() && mSigEncPage->isDeVsAndValid();
+            DeVSCompliance::decorate(okButton, de_vs);
+
+            okButton->setToolTip(DeVSCompliance::name(de_vs));
+            labelButton->setText(DeVSCompliance::name(de_vs));
         } else {
-            okButton->setText(i18nc("@action:button", "Next"));
+            if (labelButton) {
+                labelButton->setText({});
+            }
             okButton->setIcon(QIcon());
             okButton->setStyleSheet(QString());
         }
         okButton->setEnabled(mSigEncPage->validatePage());
     });
 
     layout->addWidget(buttons);
 }
 
 SignEncryptFilesDialog::~SignEncryptFilesDialog()
 {
     qCDebug(KLEOPATRA_LOG) << this << __func__;
     writeConfig();
 }
 
 void SignEncryptFilesDialog::setSigningPreset(bool preset)
 {
     mSigEncPage->setSigningPreset(preset);
 }
 
 void SignEncryptFilesDialog::setSigningUserMutable(bool mut)
 {
     if (mut == mSigningUserMutable) {
         return;
     }
     mSigningUserMutable = mut;
 }
 
 void SignEncryptFilesDialog::setEncryptionPreset(bool preset)
 {
     mSigEncPage->setEncryptionPreset(preset);
 }
 
 void SignEncryptFilesDialog::setEncryptionUserMutable(bool mut)
 {
     if (mut == mEncryptionUserMutable) {
         return;
     }
     mEncryptionUserMutable = mut;
 }
 
 void SignEncryptFilesDialog::setArchiveForced(bool archive)
 {
     mSigEncPage->setArchiveForced(archive);
 }
 
 void SignEncryptFilesDialog::setArchiveMutable(bool archive)
 {
     mSigEncPage->setArchiveMutable(archive);
 }
 
 void SignEncryptFilesDialog::setSingleFile(bool singleFile)
 {
     mSigEncPage->setSingleFile(singleFile);
 }
 
 std::vector<Key> SignEncryptFilesDialog::resolvedRecipients() const
 {
     return mSigEncPage->recipients();
 }
 
 std::vector<Key> SignEncryptFilesDialog::resolvedSigners() const
 {
     return mSigEncPage->signers();
 }
 
 void SignEncryptFilesDialog::setTaskCollection(const std::shared_ptr<Kleo::Crypto::TaskCollection> &coll)
 {
     mResultPage->setTaskCollection(coll);
 }
 
 void SignEncryptFilesDialog::setOutputNames(const QMap<int, QString> &map) const
 {
     mSigEncPage->setOutputNames(map);
 }
 
 QMap<int, QString> SignEncryptFilesDialog::outputNames() const
 {
     return mSigEncPage->outputNames();
 }
 
 bool SignEncryptFilesDialog::encryptSymmetric() const
 {
     return mSigEncPage->encryptSymmetric();
 }
 
 void SignEncryptFilesDialog::readConfig()
 {
     KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptFilesWizard"));
     const QSize size = dialog.readEntry("Size", QSize(640, 480));
     if (size.isValid()) {
         resize(size);
     }
 }
 
 void SignEncryptFilesDialog::writeConfig()
 {
     KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptFilesWizard"));
     dialog.writeEntry("Size", size());
     dialog.sync();
 }
 
 #include "signencryptfilesdialog.moc"
 
 #include "moc_signencryptfilesdialog.cpp"
diff --git a/src/crypto/gui/signencryptwidget.cpp b/src/crypto/gui/signencryptwidget.cpp
index 80779d618..97500bb3e 100644
--- a/src/crypto/gui/signencryptwidget.cpp
+++ b/src/crypto/gui/signencryptwidget.cpp
@@ -1,1022 +1,1040 @@
 /*  crypto/gui/signencryptwidget.cpp
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
     SPDX-FileContributor: Intevation GmbH
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 
 #include "signencryptwidget.h"
 
 #include "kleopatra_debug.h"
 
 #include "certificatelineedit.h"
 #include "fileoperationspreferences.h"
 #include "kleopatraapplication.h"
 #include "settings.h"
 #include "unknownrecipientwidget.h"
 
 #include "utils/gui-helper.h"
 
 #include <QButtonGroup>
 #include <QCheckBox>
 #include <QGroupBox>
 #include <QHBoxLayout>
 #include <QLabel>
 #include <QRadioButton>
 #include <QScrollArea>
 #include <QScrollBar>
 #include <QVBoxLayout>
 
 #include <Libkleo/Algorithm>
 #include <Libkleo/Compliance>
 #include <Libkleo/DefaultKeyFilter>
 #include <Libkleo/ExpiryChecker>
 #include <Libkleo/ExpiryCheckerConfig>
 #include <Libkleo/ExpiryCheckerSettings>
 #include <Libkleo/Formatting>
 #include <Libkleo/KeyCache>
 #include <Libkleo/KeyHelpers>
 #include <Libkleo/KeyListModel>
 #include <Libkleo/KeyListSortFilterProxyModel>
 
 #include <Libkleo/GnuPG>
 
 #include <KConfigGroup>
 #include <KLocalizedString>
 #include <KMessageBox>
 #include <KMessageWidget>
 #include <KSeparator>
 #include <KSharedConfig>
 
 using namespace Kleo;
 using namespace Kleo::Dialogs;
 using namespace GpgME;
 
 namespace
 {
 class SignCertificateFilter : public DefaultKeyFilter
 {
 public:
     SignCertificateFilter(GpgME::Protocol proto)
         : DefaultKeyFilter()
     {
         setIsBad(DefaultKeyFilter::NotSet);
         setHasSecret(DefaultKeyFilter::Set);
         setCanSign(DefaultKeyFilter::Set);
         setValidIfSMIME(DefaultKeyFilter::Set);
 
         if (proto == GpgME::OpenPGP) {
             setIsOpenPGP(DefaultKeyFilter::Set);
         } else if (proto == GpgME::CMS) {
             setIsOpenPGP(DefaultKeyFilter::NotSet);
         }
     }
 };
 class EncryptCertificateFilter : public DefaultKeyFilter
 {
 public:
     EncryptCertificateFilter(GpgME::Protocol proto)
         : DefaultKeyFilter()
     {
         setIsBad(DefaultKeyFilter::NotSet);
         setCanEncrypt(DefaultKeyFilter::Set);
         setValidIfSMIME(DefaultKeyFilter::Set);
 
         if (proto == GpgME::OpenPGP) {
             setIsOpenPGP(DefaultKeyFilter::Set);
         } else if (proto == GpgME::CMS) {
             setIsOpenPGP(DefaultKeyFilter::NotSet);
         }
     }
 };
 class EncryptSelfCertificateFilter : public EncryptCertificateFilter
 {
 public:
     EncryptSelfCertificateFilter(GpgME::Protocol proto)
         : EncryptCertificateFilter(proto)
     {
         setRevoked(DefaultKeyFilter::NotSet);
         setExpired(DefaultKeyFilter::NotSet);
         setCanEncrypt(DefaultKeyFilter::Set);
         setHasSecret(DefaultKeyFilter::Set);
         setValidIfSMIME(DefaultKeyFilter::Set);
     }
 };
 }
 
 class SignEncryptWidget::Private
 {
     SignEncryptWidget *const q;
 
 public:
     struct RecipientWidgets {
         CertificateLineEdit *edit;
         KMessageWidget *expiryMessage;
     };
 
     explicit Private(SignEncryptWidget *qq, bool sigEncExclusive)
         : q{qq}
         , mModel{AbstractKeyListModel::createFlatKeyListModel(qq)}
         , mIsExclusive{sigEncExclusive}
     {
     }
 
     CertificateLineEdit *addRecipientWidget();
     /* Inserts a new recipient widget after widget @p after or at the end
      * if @p after is null. */
     CertificateLineEdit *insertRecipientWidget(CertificateLineEdit *after);
     void recpRemovalRequested(const RecipientWidgets &recipient);
     void onProtocolChanged();
     void updateCheckBoxes();
     ExpiryChecker *expiryChecker();
     void updateExpiryMessages(KMessageWidget *w, const GpgME::UserID &userID, ExpiryChecker::CheckFlags flags);
     void updateAllExpiryMessages();
 
 public:
     UserIDSelectionCombo *mSigSelect = nullptr;
     KMessageWidget *mSignKeyExpiryMessage = nullptr;
     UserIDSelectionCombo *mSelfSelect = nullptr;
     KMessageWidget *mEncryptToSelfKeyExpiryMessage = nullptr;
     std::vector<RecipientWidgets> mRecpWidgets;
     QList<UnknownRecipientWidget *> mUnknownWidgets;
     QList<GpgME::Key> mAddedKeys;
     QList<KeyGroup> mAddedGroups;
     QVBoxLayout *mRecpLayout = nullptr;
     Operations mOp;
     AbstractKeyListModel *mModel = nullptr;
     QCheckBox *mSymmetric = nullptr;
     QCheckBox *mSigChk = nullptr;
     QLabel *mEncOtherLabel = nullptr;
     QCheckBox *mEncSelfChk = nullptr;
     GpgME::Protocol mCurrentProto = GpgME::UnknownProtocol;
     const bool mIsExclusive;
     std::unique_ptr<ExpiryChecker> mExpiryChecker;
     QRadioButton *mPGPRB;
     QRadioButton *mCMSRB;
 };
 
 SignEncryptWidget::SignEncryptWidget(QWidget *parent, bool sigEncExclusive)
     : QWidget{parent}
     , d{new Private{this, sigEncExclusive}}
 {
     auto lay = new QVBoxLayout(this);
     lay->setContentsMargins(0, 0, 0, 0);
 
     d->mModel->useKeyCache(true, KeyList::IncludeGroups);
 
     const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
     const bool havePublicKeys = !KeyCache::instance()->keys().empty();
     const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly();
     const bool publicKeyOnly = FileOperationsPreferences().publicKeyEncryptionOnly();
 
     bool pgpOnly = KeyCache::instance()->pgpOnly();
     if (!pgpOnly && Settings{}.cmsEnabled() && sigEncExclusive) {
         auto protocolSelectionLay = new QHBoxLayout;
         auto protocolLabel = new QLabel(i18nc("@label", "Protocol:"));
         auto font = protocolLabel->font();
         font.setWeight(QFont::DemiBold);
         protocolLabel->setFont(font);
         lay->addWidget(protocolLabel);
 
         lay->addLayout(protocolSelectionLay);
         lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3);
 
         auto grp = new QButtonGroup;
         d->mPGPRB = new QRadioButton(i18nc("@option:radio", "OpenPGP"));
         d->mCMSRB = new QRadioButton(i18nc("@option:radio", "S/MIME"));
         grp->addButton(d->mPGPRB);
         grp->addButton(d->mCMSRB);
 
         protocolSelectionLay->addWidget(d->mPGPRB);
         protocolSelectionLay->addWidget(d->mCMSRB);
 
         connect(d->mPGPRB, &QRadioButton::toggled, this, [this](bool value) {
             if (value) {
                 setProtocol(GpgME::OpenPGP);
                 KConfigGroup(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptWidget")).writeEntry("wasCMS", false);
             }
         });
         connect(d->mCMSRB, &QRadioButton::toggled, this, [this](bool value) {
             if (value) {
                 setProtocol(GpgME::CMS);
                 KConfigGroup(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptWidget")).writeEntry("wasCMS", true);
             }
         });
     }
 
     /* The signature selection */
     {
         d->mSigChk = new QCheckBox{i18n("&Sign as:"), this};
         d->mSigChk->setEnabled(haveSecretKeys);
         d->mSigChk->setChecked(haveSecretKeys);
         auto checkFont = d->mSigChk->font();
         checkFont.setWeight(QFont::DemiBold);
         d->mSigChk->setFont(checkFont);
         lay->addWidget(d->mSigChk);
 
         d->mSigSelect = new UserIDSelectionCombo{KeyUsage::Sign, this};
         d->mSigSelect->setEnabled(d->mSigChk->isChecked());
         lay->addWidget(d->mSigSelect);
 
         d->mSignKeyExpiryMessage = new KMessageWidget{this};
         d->mSignKeyExpiryMessage->setVisible(false);
         lay->addWidget(d->mSignKeyExpiryMessage);
 
         connect(d->mSigSelect, &UserIDSelectionCombo::certificateSelectionRequested, this, [this]() {
             ownCertificateSelectionRequested(CertificateSelectionDialog::SignOnly, d->mSigSelect);
         });
         connect(d->mSigSelect, &UserIDSelectionCombo::customItemSelected, this, [this]() {
             updateOp();
             d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey);
         });
 
         connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool checked) {
             d->mSigSelect->setEnabled(checked);
             updateOp();
             d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey);
         });
         connect(d->mSigSelect, &UserIDSelectionCombo::currentKeyChanged, this, [this]() {
             updateOp();
             d->updateExpiryMessages(d->mSignKeyExpiryMessage, signUserId(), ExpiryChecker::OwnSigningKey);
         });
 
         lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3);
     }
 
     // Recipient selection
     {
         // Own key
         d->mEncSelfChk = new QCheckBox{i18n("Encrypt for me:"), this};
         d->mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly);
         d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly);
         auto checkFont = d->mEncSelfChk->font();
         checkFont.setWeight(QFont::DemiBold);
         d->mEncSelfChk->setFont(checkFont);
         lay->addWidget(d->mEncSelfChk);
 
         d->mSelfSelect = new UserIDSelectionCombo{KeyUsage::Encrypt, this};
         d->mSelfSelect->setEnabled(d->mEncSelfChk->isChecked());
         lay->addWidget(d->mSelfSelect);
 
         d->mEncryptToSelfKeyExpiryMessage = new KMessageWidget{this};
         d->mEncryptToSelfKeyExpiryMessage->setVisible(false);
         lay->addWidget(d->mEncryptToSelfKeyExpiryMessage);
 
         connect(d->mSelfSelect, &UserIDSelectionCombo::certificateSelectionRequested, this, [this]() {
             ownCertificateSelectionRequested(CertificateSelectionDialog::EncryptOnly, d->mSelfSelect);
         });
         connect(d->mSelfSelect, &UserIDSelectionCombo::customItemSelected, this, [this]() {
             updateOp();
             d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey);
         });
         connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool checked) {
             d->mSelfSelect->setEnabled(checked);
         });
 
         lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3);
 
         // Checkbox for other keys
         d->mEncOtherLabel = new QLabel(i18nc("@label", "Encrypt for others:"), this);
         d->mEncOtherLabel->setEnabled(havePublicKeys && !symmetricOnly);
         d->mEncOtherLabel->setFont(checkFont);
         lay->addWidget(d->mEncOtherLabel);
 
         d->mRecpLayout = new QVBoxLayout;
         lay->addLayout(d->mRecpLayout);
 
         d->addRecipientWidget();
 
         lay->addSpacing(style()->pixelMetric(QStyle::PM_LayoutVerticalSpacing) * 3);
 
         // Checkbox for password
         d->mSymmetric = new QCheckBox(i18nc("@option:check", "Encrypt with password:"));
         d->mSymmetric->setFont(checkFont);
         d->mSymmetric->setToolTip(i18nc("Tooltip information for symmetric encryption",
                                         "Additionally to the keys of the recipients you can encrypt your data with a password. "
                                         "Anyone who has the password can read the data without any secret key. "
                                         "Using a password is <b>less secure</b> then public key cryptography. Even if you pick a very strong password."));
         d->mSymmetric->setChecked((symmetricOnly || !havePublicKeys) && !publicKeyOnly);
         d->mSymmetric->setEnabled(!publicKeyOnly);
         lay->addWidget(d->mSymmetric);
 
         lay->addWidget(new QLabel(i18nc("@info", "Anyone you share the password with can read the data.")));
 
         lay->addStretch();
 
         // Connect it
         connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool checked) {
             d->mSelfSelect->setEnabled(checked);
             updateOp();
             d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey);
         });
         connect(d->mSelfSelect, &UserIDSelectionCombo::currentKeyChanged, this, [this]() {
             updateOp();
             d->updateExpiryMessages(d->mEncryptToSelfKeyExpiryMessage, selfUserId(), ExpiryChecker::OwnEncryptionKey);
         });
         connect(d->mSymmetric, &QCheckBox::toggled, this, &SignEncryptWidget::updateOp);
 
         if (d->mIsExclusive) {
             connect(d->mEncSelfChk, &QCheckBox::toggled, this, [this](bool value) {
                 if (d->mCurrentProto != GpgME::CMS) {
                     return;
                 }
                 if (value) {
                     d->mSigChk->setChecked(false);
                 }
             });
             connect(d->mSigChk, &QCheckBox::toggled, this, [this](bool value) {
                 if (d->mCurrentProto != GpgME::CMS) {
                     return;
                 }
                 if (value) {
                     d->mEncSelfChk->setChecked(false);
                     // Copying the vector makes sure that all items are actually deleted.
                     for (const auto &widget : std::vector(d->mRecpWidgets)) {
                         d->recpRemovalRequested(widget);
                     }
                     d->addRecipientWidget();
                 }
             });
         }
 
         // Ensure that the d->mSigChk is aligned together with the encryption check boxes.
         d->mSigChk->setMinimumWidth(qMax(d->mEncOtherLabel->width(), d->mEncSelfChk->width()));
     }
 
     connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() {
         d->updateCheckBoxes();
         d->updateAllExpiryMessages();
     });
     connect(KleopatraApplication::instance(), &KleopatraApplication::configurationChanged, this, [this]() {
         d->updateCheckBoxes();
         d->mExpiryChecker.reset();
         d->updateAllExpiryMessages();
     });
 
     if (!pgpOnly && Settings{}.cmsEnabled() && sigEncExclusive) {
         KConfigGroup config(KSharedConfig::openStateConfig(), QStringLiteral("SignEncryptWidget"));
         if (config.readEntry("wasCMS", false)) {
             d->mCMSRB->setChecked(true);
             setProtocol(GpgME::CMS);
         } else {
             d->mPGPRB->setChecked(true);
             setProtocol(GpgME::OpenPGP);
         }
     }
 
     if (pgpOnly || !Settings{}.cmsEnabled()) {
         setProtocol(GpgME::OpenPGP);
     }
 
     loadKeys();
     d->onProtocolChanged();
     updateOp();
 }
 
 SignEncryptWidget::~SignEncryptWidget() = default;
 
 void SignEncryptWidget::setSignAsText(const QString &text)
 {
     d->mSigChk->setText(text);
 }
 
 void SignEncryptWidget::setEncryptForMeText(const QString &text)
 {
     d->mEncSelfChk->setText(text);
 }
 
 void SignEncryptWidget::setEncryptForOthersText(const QString &text)
 {
     d->mEncOtherLabel->setText(text);
 }
 
 void SignEncryptWidget::setEncryptWithPasswordText(const QString &text)
 {
     d->mSymmetric->setText(text);
 }
 
 CertificateLineEdit *SignEncryptWidget::Private::addRecipientWidget()
 {
     return insertRecipientWidget(nullptr);
 }
 
 CertificateLineEdit *SignEncryptWidget::Private::insertRecipientWidget(CertificateLineEdit *after)
 {
     Q_ASSERT(!after || mRecpLayout->indexOf(after) != -1);
 
     const auto index = after ? mRecpLayout->indexOf(after) + 2 : mRecpLayout->count();
 
     const RecipientWidgets recipient{new CertificateLineEdit{mModel, KeyUsage::Encrypt, new EncryptCertificateFilter{mCurrentProto}, q}, new KMessageWidget{q}};
     recipient.edit->setAccessibleNameOfLineEdit(i18nc("text for screen readers", "recipient key"));
     recipient.edit->setEnabled(!KeyCache::instance()->keys().empty() && !FileOperationsPreferences().symmetricEncryptionOnly());
     recipient.expiryMessage->setVisible(false);
     if (!after) {
         mEncOtherLabel->setBuddy(recipient.edit);
     }
     if (static_cast<unsigned>(index / 2) < mRecpWidgets.size()) {
         mRecpWidgets.insert(mRecpWidgets.begin() + index / 2, recipient);
     } else {
         mRecpWidgets.push_back(recipient);
     }
 
     if (mRecpLayout->count() > 0) {
         auto prevWidget = after ? after : mRecpLayout->itemAt(mRecpLayout->count() - 1)->widget();
         Kleo::forceSetTabOrder(prevWidget, recipient.edit);
         Kleo::forceSetTabOrder(recipient.edit, recipient.expiryMessage);
     } else {
         Kleo::forceSetTabOrder(mEncryptToSelfKeyExpiryMessage, recipient.edit);
         Kleo::forceSetTabOrder(recipient.edit, recipient.expiryMessage);
     }
     Kleo::forceSetTabOrder(recipient.expiryMessage, mSymmetric);
     mRecpLayout->insertWidget(index, recipient.edit);
     mRecpLayout->insertWidget(index + 1, recipient.expiryMessage);
 
     connect(recipient.edit, &CertificateLineEdit::keyChanged, q, &SignEncryptWidget::recipientsChanged);
     connect(recipient.edit, &CertificateLineEdit::editingStarted, q, &SignEncryptWidget::recipientsChanged);
     connect(recipient.edit, &CertificateLineEdit::cleared, q, &SignEncryptWidget::recipientsChanged);
     connect(recipient.edit, &CertificateLineEdit::certificateSelectionRequested, q, [this, recipient]() {
         q->certificateSelectionRequested(recipient.edit);
     });
 
     if (mIsExclusive) {
         connect(recipient.edit, &CertificateLineEdit::keyChanged, q, [this]() {
             if (mCurrentProto != GpgME::CMS) {
                 return;
             }
             mSigChk->setChecked(false);
         });
     }
 
     return recipient.edit;
 }
 
 void SignEncryptWidget::addRecipient(const Key &key)
 {
     CertificateLineEdit *certSel = d->addRecipientWidget();
     if (!key.isNull()) {
         certSel->setKey(key);
         d->mAddedKeys << key;
     }
 }
 
 void SignEncryptWidget::addRecipient(const KeyGroup &group)
 {
     CertificateLineEdit *certSel = d->addRecipientWidget();
     if (!group.isNull()) {
         certSel->setGroup(group);
         d->mAddedGroups << group;
     }
 }
 
 void SignEncryptWidget::ownCertificateSelectionRequested(CertificateSelectionDialog::Options options, UserIDSelectionCombo *combo)
 {
     CertificateSelectionDialog dialog{this};
     dialog.setOptions(CertificateSelectionDialog::Options( //
                           CertificateSelectionDialog::SingleSelection | //
                           CertificateSelectionDialog::SecretKeys | //
                           CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto))
                       | //
                       options);
 
     auto keyFilter = std::make_shared<DefaultKeyFilter>();
     keyFilter->setMatchContexts(KeyFilter::Filtering);
     keyFilter->setIsBad(DefaultKeyFilter::NotSet);
     if (options & CertificateSelectionDialog::SignOnly) {
         keyFilter->setName(i18n("Usable for Signing"));
         keyFilter->setDescription(i18n("Certificates that can be used for signing"));
         keyFilter->setId(QStringLiteral("CanSignFilter"));
     } else {
         keyFilter->setName(i18n("Usable for Encryption"));
         keyFilter->setDescription(i18n("Certificates that can be used for encryption"));
         keyFilter->setId(QStringLiteral("CanEncryptFilter"));
     }
 
     dialog.addCustomKeyFilter(keyFilter);
     dialog.setKeyFilter(keyFilter);
 
     if (dialog.exec()) {
         auto userId = dialog.selectedUserIDs()[0];
         auto index = combo->findUserId(userId);
         if (index == -1) {
             combo->appendCustomItem(QIcon::fromTheme(QStringLiteral("emblem-error")), Formatting::summaryLine(userId), QVariant::fromValue(userId));
             index = combo->combo()->count() - 1;
         }
         combo->combo()->setCurrentIndex(index);
     }
 
     recipientsChanged();
 }
 
 void SignEncryptWidget::certificateSelectionRequested(CertificateLineEdit *certificateLineEdit)
 {
     CertificateSelectionDialog dlg{this};
 
     dlg.setOptions(CertificateSelectionDialog::Options( //
         CertificateSelectionDialog::MultiSelection | //
         CertificateSelectionDialog::EncryptOnly | //
         CertificateSelectionDialog::optionsFromProtocol(d->mCurrentProto) | //
         CertificateSelectionDialog::IncludeGroups));
 
     auto keyFilter = std::make_shared<DefaultKeyFilter>();
     keyFilter->setMatchContexts(KeyFilter::Filtering);
     keyFilter->setIsBad(DefaultKeyFilter::NotSet);
     keyFilter->setName(i18n("Usable for Encryption"));
     keyFilter->setDescription(i18n("Certificates that can be used for encryption"));
     keyFilter->setId(QStringLiteral("CanEncryptFilter"));
 
     dlg.addCustomKeyFilter(keyFilter);
     dlg.setKeyFilter(keyFilter);
 
     if (!certificateLineEdit->key().isNull()) {
         const auto key = certificateLineEdit->key();
         const auto name = QString::fromUtf8(key.userID(0).name());
         const auto email = QString::fromUtf8(key.userID(0).email());
         dlg.setStringFilter(!name.isEmpty() ? name : email);
     } else if (!certificateLineEdit->group().isNull()) {
         dlg.setStringFilter(certificateLineEdit->group().name());
     } else if (!certificateLineEdit->userID().isNull()) {
         const auto userID = certificateLineEdit->userID();
         const auto name = QString::fromUtf8(userID.name());
         const auto email = QString::fromUtf8(userID.email());
         dlg.setStringFilter(!name.isEmpty() ? name : email);
     } else {
         dlg.setStringFilter(certificateLineEdit->text());
     }
 
     if (dlg.exec()) {
         const std::vector<UserID> userIds = dlg.selectedUserIDs();
         const std::vector<KeyGroup> groups = dlg.selectedGroups();
         if (userIds.size() == 0 && groups.size() == 0) {
             return;
         }
         CertificateLineEdit *certWidget = nullptr;
         for (const auto &userId : userIds) {
             if (!certWidget) {
                 certWidget = certificateLineEdit;
             } else {
                 certWidget = d->insertRecipientWidget(certWidget);
             }
             certWidget->setUserID(userId);
         }
         for (const KeyGroup &group : groups) {
             if (!certWidget) {
                 certWidget = certificateLineEdit;
             } else {
                 certWidget = d->insertRecipientWidget(certWidget);
             }
             certWidget->setGroup(group);
         }
     }
 
     recipientsChanged();
 }
 
 void SignEncryptWidget::clearAddedRecipients()
 {
     for (auto w : std::as_const(d->mUnknownWidgets)) {
         d->mRecpLayout->removeWidget(w);
         delete w;
     }
 
     for (auto &key : std::as_const(d->mAddedKeys)) {
         removeRecipient(key);
     }
 
     for (auto &group : std::as_const(d->mAddedGroups)) {
         removeRecipient(group);
     }
 }
 
 void SignEncryptWidget::addUnknownRecipient(const char *keyID)
 {
     auto unknownWidget = new UnknownRecipientWidget(keyID);
     d->mUnknownWidgets << unknownWidget;
 
     if (d->mRecpLayout->count() > 0) {
         auto lastWidget = d->mRecpLayout->itemAt(d->mRecpLayout->count() - 1)->widget();
         setTabOrder(lastWidget, unknownWidget);
     }
     d->mRecpLayout->addWidget(unknownWidget);
 
     connect(KeyCache::instance().get(), &Kleo::KeyCache::keysMayHaveChanged, this, [this]() {
         // Check if any unknown recipient can now be found.
         for (auto w : std::as_const(d->mUnknownWidgets)) {
             auto key = KeyCache::instance()->findByKeyIDOrFingerprint(w->keyID().toLatin1().constData());
             if (key.isNull()) {
                 std::vector<std::string> subids;
                 subids.push_back(std::string(w->keyID().toLatin1().constData()));
                 for (const auto &subkey : KeyCache::instance()->findSubkeysByKeyID(subids)) {
                     key = subkey.parent();
                 }
             }
             if (key.isNull()) {
                 continue;
             }
             // Key is now available replace by line edit.
             qCDebug(KLEOPATRA_LOG) << "Removing widget for keyid: " << w->keyID();
             d->mRecpLayout->removeWidget(w);
             d->mUnknownWidgets.removeAll(w);
             delete w;
             addRecipient(key);
         }
     });
 }
 
 void SignEncryptWidget::recipientsChanged()
 {
     const bool hasEmptyRecpWidget = std::any_of(std::cbegin(d->mRecpWidgets), std::cend(d->mRecpWidgets), [](auto w) {
         return w.edit->isEmpty();
     });
     if (!hasEmptyRecpWidget) {
         d->addRecipientWidget();
     }
     updateOp();
     for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
         if (!recipient.edit->isEditingInProgress() || recipient.edit->isEmpty()) {
             d->updateExpiryMessages(recipient.expiryMessage, recipient.edit->userID(), ExpiryChecker::EncryptionKey);
         }
     }
 }
 
 UserID SignEncryptWidget::signUserId() const
 {
     if (d->mSigSelect->isEnabled()) {
         return d->mSigSelect->currentUserID();
     }
     return UserID();
 }
 
 UserID SignEncryptWidget::selfUserId() const
 {
     if (d->mSelfSelect->isEnabled()) {
         return d->mSelfSelect->currentUserID();
     }
     return UserID();
 }
 
 std::vector<Key> SignEncryptWidget::recipients() const
 {
     std::vector<Key> ret;
     for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
         const auto *const w = recipient.edit;
         if (!w->isEnabled()) {
             // If one is disabled, all are disabled.
             break;
         }
         const Key k = w->key();
         const KeyGroup g = w->group();
         const UserID u = w->userID();
         if (!k.isNull()) {
             ret.push_back(k);
         } else if (!g.isNull()) {
             const auto keys = g.keys();
             std::copy(keys.begin(), keys.end(), std::back_inserter(ret));
         } else if (!u.isNull()) {
             ret.push_back(u.parent());
         }
     }
     const Key k = selfUserId().parent();
     if (!k.isNull()) {
         ret.push_back(k);
     }
     return ret;
 }
 
 bool SignEncryptWidget::isDeVsAndValid() const
 {
     if (!signUserId().isNull() && !DeVSCompliance::userIDIsCompliant(signUserId())) {
         return false;
     }
 
     if (!selfUserId().isNull() && !DeVSCompliance::userIDIsCompliant(selfUserId())) {
         return false;
     }
 
     for (const auto &key : recipients()) {
         if (!DeVSCompliance::keyIsCompliant(key)) {
             return false;
         }
     }
 
     return true;
 }
 
 static QString expiryMessage(const ExpiryChecker::Result &result)
 {
     if (result.expiration.certificate.isNull()) {
         return {};
     }
     switch (result.expiration.status) {
     case ExpiryChecker::Expired:
         return i18nc("@info", "This certificate is expired.");
     case ExpiryChecker::ExpiresSoon: {
         if (result.expiration.duration.count() == 0) {
             return i18nc("@info", "This certificate expires today.");
         } else {
             return i18ncp("@info", "This certificate expires tomorrow.", "This certificate expires in %1 days.", result.expiration.duration.count());
         }
     }
     case ExpiryChecker::NoSuitableSubkey:
         if (result.checkFlags & ExpiryChecker::EncryptionKey) {
             return i18nc("@info", "This certificate cannot be used for encryption.");
         } else {
             return i18nc("@info", "This certificate cannot be used for signing.");
         }
     case ExpiryChecker::InvalidKey:
     case ExpiryChecker::InvalidCheckFlags:
         break; // wrong usage of ExpiryChecker; can be ignored
     case ExpiryChecker::NotNearExpiry:;
     }
     return {};
 }
 
 void SignEncryptWidget::updateOp()
 {
     const std::vector<Key> recp = recipients();
 
     Operations op = NoOperation;
     if (!signUserId().isNull()) {
         op |= Sign;
     }
     if (!recp.empty() || encryptSymmetric()) {
         op |= Encrypt;
     }
     d->mOp = op;
     Q_EMIT operationChanged(d->mOp);
     Q_EMIT keysChanged();
 }
 
 SignEncryptWidget::Operations SignEncryptWidget::currentOp() const
 {
     return d->mOp;
 }
 
 namespace
 {
 bool recipientWidgetHasFocus(QWidget *w)
 {
     // check if w (or its focus proxy) or a child widget of w has focus
     return w->hasFocus() || w->isAncestorOf(qApp->focusWidget());
 }
 }
 
 void SignEncryptWidget::Private::recpRemovalRequested(const RecipientWidgets &recipient)
 {
     if (!recipient.edit) {
         return;
     }
     if (recipientWidgetHasFocus(recipient.edit) || recipientWidgetHasFocus(recipient.expiryMessage)) {
         const int index = mRecpLayout->indexOf(recipient.edit);
         const auto focusWidget = (index < mRecpLayout->count() - 2) ? //
             mRecpLayout->itemAt(index + 2)->widget()
                                                                     : mRecpLayout->itemAt(mRecpLayout->count() - 3)->widget();
         focusWidget->setFocus();
     }
     mRecpLayout->removeWidget(recipient.expiryMessage);
     mRecpLayout->removeWidget(recipient.edit);
     const auto it = std::find_if(std::begin(mRecpWidgets), std::end(mRecpWidgets), [recipient](const auto &r) {
         return r.edit == recipient.edit;
     });
     mRecpWidgets.erase(it);
     recipient.expiryMessage->deleteLater();
     recipient.edit->deleteLater();
 }
 
 void SignEncryptWidget::removeRecipient(const GpgME::Key &key)
 {
     for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
         const auto editKey = recipient.edit->key();
         if (key.isNull() && editKey.isNull()) {
             d->recpRemovalRequested(recipient);
             return;
         }
         if (editKey.primaryFingerprint() && key.primaryFingerprint() && !strcmp(editKey.primaryFingerprint(), key.primaryFingerprint())) {
             d->recpRemovalRequested(recipient);
             return;
         }
     }
 }
 
 void SignEncryptWidget::removeRecipient(const KeyGroup &group)
 {
     for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
         const auto editGroup = recipient.edit->group();
         if (group.isNull() && editGroup.isNull()) {
             d->recpRemovalRequested(recipient);
             return;
         }
         if (editGroup.name() == group.name()) {
             d->recpRemovalRequested(recipient);
             return;
         }
     }
 }
 
 bool SignEncryptWidget::encryptSymmetric() const
 {
     return d->mSymmetric->isChecked();
 }
 
 void SignEncryptWidget::loadKeys()
 {
     KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys"));
     auto cache = KeyCache::instance();
     d->mSigSelect->setDefaultKey(keys.readEntry("SigningKey", QString()));
     d->mSelfSelect->setDefaultKey(keys.readEntry("EncryptKey", QString()));
 }
 
 void SignEncryptWidget::saveOwnKeys() const
 {
     KConfigGroup keys(KSharedConfig::openConfig(), QStringLiteral("SignEncryptKeys"));
     auto sigKey = d->mSigSelect->currentKey();
     auto encKey = d->mSelfSelect->currentKey();
     if (!sigKey.isNull()) {
         keys.writeEntry("SigningKey", sigKey.primaryFingerprint());
     }
     if (!encKey.isNull()) {
         keys.writeEntry("EncryptKey", encKey.primaryFingerprint());
     }
 }
 
 void SignEncryptWidget::setSigningChecked(bool value)
 {
     d->mSigChk->setChecked(value && !KeyCache::instance()->secretKeys().empty());
 }
 
 void SignEncryptWidget::setEncryptionChecked(bool checked)
 {
     if (checked) {
         const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
         const bool havePublicKeys = !KeyCache::instance()->keys().empty();
         const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly();
         const bool publicKeyOnly = FileOperationsPreferences().publicKeyEncryptionOnly();
         d->mEncSelfChk->setChecked(haveSecretKeys && !symmetricOnly);
         d->mSymmetric->setChecked((symmetricOnly || !havePublicKeys) && !publicKeyOnly);
     } else {
         d->mEncSelfChk->setChecked(false);
         d->mSymmetric->setChecked(false);
     }
 }
 
 void SignEncryptWidget::setProtocol(GpgME::Protocol proto)
 {
     if (d->mCurrentProto == proto) {
         return;
     }
     d->mCurrentProto = proto;
 
     if (d->mPGPRB) {
         d->mPGPRB->setChecked(proto == GpgME::OpenPGP);
         d->mCMSRB->setChecked(proto == GpgME::CMS);
     }
     d->onProtocolChanged();
 }
 
 void Kleo::SignEncryptWidget::Private::onProtocolChanged()
 {
     auto encryptForOthers = q->recipients().size() > 0;
     mSigSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new SignCertificateFilter(mCurrentProto)));
     mSelfSelect->setKeyFilter(std::shared_ptr<KeyFilter>(new EncryptSelfCertificateFilter(mCurrentProto)));
     const auto encFilter = std::shared_ptr<KeyFilter>(new EncryptCertificateFilter(mCurrentProto));
     for (const auto &widget : std::vector(mRecpWidgets)) {
         recpRemovalRequested(widget);
     }
     addRecipientWidget();
     for (const auto &recipient : std::as_const(mRecpWidgets)) {
         recipient.edit->setKeyFilter(encFilter);
     }
 
     if (mIsExclusive) {
         mSymmetric->setDisabled(mCurrentProto == GpgME::CMS);
         if (mSymmetric->isChecked() && mCurrentProto == GpgME::CMS) {
             mSymmetric->setChecked(false);
         }
         if (mSigChk->isChecked() && mCurrentProto == GpgME::CMS && (mEncSelfChk->isChecked() || encryptForOthers)) {
             mSigChk->setChecked(false);
         }
     }
 }
 
 static bool recipientIsOkay(const CertificateLineEdit *edit)
 {
     if (!edit->isEnabled() || edit->isEmpty()) {
         return true;
     }
     if (!edit->hasAcceptableInput()) {
         return false;
     }
     if (const auto userID = edit->userID(); !userID.isNull()) {
         return Kleo::canBeUsedForEncryption(userID.parent()) && !userID.isBad();
     }
     if (const auto key = edit->key(); !key.isNull()) {
         return Kleo::canBeUsedForEncryption(key);
     }
     if (const auto group = edit->group(); !group.isNull()) {
         return std::ranges::all_of(group.keys(), Kleo::canBeUsedForEncryption);
     }
     // we should never reach this point
     return false;
 }
 
 bool SignEncryptWidget::isComplete() const
 {
+    if (DeVSCompliance::isActive() && !DeVSCompliance::isCompliant()) {
+        return false;
+    }
+
     if (currentOp() == NoOperation) {
         return false;
     }
     if ((currentOp() & SignEncryptWidget::Sign) && !Kleo::canBeUsedForSigning(signUserId().parent())) {
         return false;
     }
     if (currentOp() & SignEncryptWidget::Encrypt) {
         if (!selfUserId().isNull() && !Kleo::canBeUsedForEncryption(selfUserId().parent())) {
             return false;
         }
         const bool allOtherRecipientsAreOkay = std::ranges::all_of(d->mRecpWidgets, [](const auto &r) {
             return recipientIsOkay(r.edit);
         });
         if (!allOtherRecipientsAreOkay) {
             return false;
         }
     }
     return true;
 }
 
 bool SignEncryptWidget::validate()
 {
     CertificateLineEdit *firstUnresolvedRecipient = nullptr;
     QStringList unresolvedRecipients;
     for (const auto &recipient : std::as_const(d->mRecpWidgets)) {
         if (recipient.edit->isEnabled() && !recipient.edit->hasAcceptableInput()) {
             if (!firstUnresolvedRecipient) {
                 firstUnresolvedRecipient = recipient.edit;
             }
             unresolvedRecipients.push_back(recipient.edit->text().toHtmlEscaped());
         }
     }
     if (!unresolvedRecipients.isEmpty()) {
         KMessageBox::errorList(this,
                                i18n("Could not find a key for the following recipients:"),
                                unresolvedRecipients,
                                i18nc("@title:window", "Failed to find some keys"));
     }
     if (firstUnresolvedRecipient) {
         firstUnresolvedRecipient->setFocus();
     }
     return unresolvedRecipients.isEmpty();
 }
 
 void SignEncryptWidget::Private::updateCheckBoxes()
 {
     const bool haveSecretKeys = !KeyCache::instance()->secretKeys().empty();
     const bool symmetricOnly = FileOperationsPreferences().symmetricEncryptionOnly();
     mSigChk->setEnabled(haveSecretKeys);
     mEncSelfChk->setEnabled(haveSecretKeys && !symmetricOnly);
     if (symmetricOnly) {
         mEncSelfChk->setChecked(false);
         mSymmetric->setChecked(true);
     }
 }
 
 ExpiryChecker *Kleo::SignEncryptWidget::Private::expiryChecker()
 {
     if (!mExpiryChecker) {
         mExpiryChecker.reset(new ExpiryChecker{ExpiryCheckerConfig{}.settings()});
     }
     return mExpiryChecker.get();
 }
 
 void SignEncryptWidget::Private::updateExpiryMessages(KMessageWidget *messageWidget, const GpgME::UserID &userID, ExpiryChecker::CheckFlags flags)
 {
     messageWidget->setCloseButtonVisible(false);
 
     if (userID.isNull()) {
         messageWidget->setVisible(false);
     } else if (userID.parent().isExpired()) {
         messageWidget->setText(i18nc("@info", "This certificate is expired."));
         messageWidget->setVisible(true);
     } else if (userID.isRevoked() || userID.parent().isRevoked()) {
         messageWidget->setText(i18nc("@info", "This certificate is revoked."));
         messageWidget->setVisible(true);
     } else if (Settings{}.showExpiryNotifications()) {
         const auto result = expiryChecker()->checkKey(userID.parent(), flags);
         const auto message = expiryMessage(result);
         messageWidget->setText(message);
         messageWidget->setVisible(!message.isEmpty());
     } else {
         messageWidget->setVisible(false);
     }
 }
 
 void SignEncryptWidget::Private::updateAllExpiryMessages()
 {
     updateExpiryMessages(mSignKeyExpiryMessage, q->signUserId(), ExpiryChecker::OwnSigningKey);
     updateExpiryMessages(mEncryptToSelfKeyExpiryMessage, q->selfUserId(), ExpiryChecker::OwnEncryptionKey);
     for (const auto &recipient : std::as_const(mRecpWidgets)) {
         if (recipient.edit->isEnabled()) {
             updateExpiryMessages(recipient.expiryMessage, recipient.edit->userID(), ExpiryChecker::EncryptionKey);
         }
     }
 }
 
+QString SignEncryptWidget::continueButtonText() const
+{
+    switch (d->mOp) {
+    case SignEncryptWidget::Sign:
+        return i18nc("@action:button", "Sign");
+    case SignEncryptWidget::Encrypt:
+        return i18nc("@action:button", "Encrypt");
+    case SignEncryptWidget::SignAndEncrypt:
+        return i18nc("@action:button", "Sign / Encrypt");
+    default:
+        return i18nc("@action:button", "Next");
+    };
+}
+
 #include "moc_signencryptwidget.cpp"
diff --git a/src/crypto/gui/signencryptwidget.h b/src/crypto/gui/signencryptwidget.h
index e8ee5eb69..6ba84efd0 100644
--- a/src/crypto/gui/signencryptwidget.h
+++ b/src/crypto/gui/signencryptwidget.h
@@ -1,143 +1,147 @@
 /*  crypto/gui/signencryptwidget.h
 
     This file is part of Kleopatra, the KDE keymanager
     SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
     SPDX-FileContributor: Intevation GmbH
 
     SPDX-License-Identifier: GPL-2.0-or-later
 */
 #pragma once
 
 #include "dialogs/certificateselectiondialog.h"
 
 #include <QList>
 #include <QWidget>
 
 #include <gpgme++/global.h>
 
 #include <Libkleo/UserIDSelectionCombo>
 
 #include <memory>
 
 namespace GpgME
 {
 class Key;
 }
 
 namespace Kleo
 {
 class CertificateLineEdit;
 class KeyGroup;
 
 class SignEncryptWidget : public QWidget
 {
     Q_OBJECT
 public:
     enum Operation {
         NoOperation = 0x00,
         Sign = 0x01,
         Encrypt = 0x02,
         SignAndEncrypt = Sign | Encrypt
     };
     Q_DECLARE_FLAGS(Operations, Operation)
 
     /** If exclusive is true, only one protocol can be used, and cms operations can be
      * done only either as sign or as encrypt */
     explicit SignEncryptWidget(QWidget *parent = nullptr, bool exclusive = false);
 
     ~SignEncryptWidget() override;
 
     /** Overwrite default text with custom text, e.g. with a character marked
      *  as shortcut key. */
     void setSignAsText(const QString &text);
     void setEncryptForMeText(const QString &text);
     void setEncryptForOthersText(const QString &text);
     void setEncryptWithPasswordText(const QString &text);
 
     /** Returns the list of recipients selected in the dialog
      * or an empty list if encryption is disabled */
     std::vector<GpgME::Key> recipients() const;
 
     /** Returns the selected signing key or a null key if signing
      * is disabled. */
     GpgME::UserID signUserId() const;
 
     /** Returns the selected encrypt to self key or a null key if
      * encrypt to self is disabled. */
     GpgME::UserID selfUserId() const;
 
     /** Returns the operation based on the current selection. */
     Operations currentOp() const;
 
     /** Whether or not symmetric encryption should also be used. */
     bool encryptSymmetric() const;
 
     /** Save the currently selected signing and encrypt to self keys. */
     void saveOwnKeys() const;
 
     /** Return whether or not all keys involved in the operation are
         compliant with CO_DE_VS, and all keys are valid (i.e. all
         userIDs have Validity >= Full).  */
     bool isDeVsAndValid() const;
 
     /** Set whether or not signing group should be checked */
     void setSigningChecked(bool value);
 
     /** Set whether or not encryption group should be checked */
     void setEncryptionChecked(bool value);
 
     /** Filter for a specific protocol. Use UnknownProtocol for both
      * S/MIME and OpenPGP */
     void setProtocol(GpgME::Protocol protocol);
 
     /** Add a recipient with the key key */
     void addRecipient(const GpgME::Key &key);
 
     /** Add a group of recipients */
     void addRecipient(const Kleo::KeyGroup &group);
 
     /** Add a placehoder for an unknown key */
     void addUnknownRecipient(const char *keyId);
 
     /** Remove all Recipients added by keyId or by key. */
     void clearAddedRecipients();
 
     /** Remove a Recipient key */
     void removeRecipient(const GpgME::Key &key);
 
     /** Remove a recipient group */
     void removeRecipient(const Kleo::KeyGroup &group);
 
     /** Returns true if all required information has been entered. */
     bool isComplete() const;
 
     /** Returns true if all recipients have been resolved. Otherwise, shows
         an error message and returns false. */
     bool validate();
 
+    /** Text for the "continue" button, depending on the configured operation;
+     * e.g. "Encrypt", "Sign", "Sign / Encrypt". */
+    QString continueButtonText() const;
+
 protected Q_SLOTS:
     void updateOp();
     void recipientsChanged();
     void certificateSelectionRequested(CertificateLineEdit *w);
     void ownCertificateSelectionRequested(Kleo::Dialogs::CertificateSelectionDialog::Options options, UserIDSelectionCombo *combo);
 
 protected:
     void loadKeys();
 
 Q_SIGNALS:
     /* Emitted when the certificate selection changed the operation
      * with that selection. e.g. "Sign" or "Sign/Encrypt".
      * If no crypto operation is selected this returns a null string. */
     void operationChanged(Operations op);
 
     /* Emitted when the certificate selection might be changed. */
     void keysChanged();
 
 private:
     class Private;
     const std::unique_ptr<Private> d;
 };
 
 Q_DECLARE_OPERATORS_FOR_FLAGS(SignEncryptWidget::Operations)
 
 } // namespace Kleo
diff --git a/src/crypto/signencrypttask.cpp b/src/crypto/signencrypttask.cpp
index 1ff1e2413..dc3fd786a 100644
--- a/src/crypto/signencrypttask.cpp
+++ b/src/crypto/signencrypttask.cpp
@@ -1,1040 +1,1058 @@
 /* -*- mode: c++; c-basic-offset:4 -*-
     crypto/signencrypttask.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 <config-kleopatra.h>
 
 #include "signencrypttask.h"
 
 #include <utils/input.h>
 #include <utils/kleo_assert.h>
 #include <utils/output.h>
 #include <utils/path-helper.h>
 
 #include <Libkleo/AuditLogEntry>
 #include <Libkleo/Formatting>
 #include <Libkleo/KleoException>
 #include <Libkleo/Stl_Util>
 
 #include <QGpgME/Debug>
 #include <QGpgME/EncryptArchiveJob>
 #include <QGpgME/EncryptJob>
 #include <QGpgME/Protocol>
 #include <QGpgME/SignArchiveJob>
 #include <QGpgME/SignEncryptArchiveJob>
 #include <QGpgME/SignEncryptJob>
 #include <QGpgME/SignJob>
 
 #include <gpgme++/encryptionresult.h>
 #include <gpgme++/key.h>
 #include <gpgme++/signingresult.h>
 
 #include <KLocalizedString>
 
 #include "kleopatra_debug.h"
 #include <QFileInfo>
 #include <QPointer>
 
 using namespace Kleo;
 using namespace Kleo::Crypto;
 using namespace GpgME;
 
 namespace
 {
 
 class ErrorResult : public Task::Result
 {
 public:
     ErrorResult(bool sign, bool encrypt, const Error &err, const QString &errStr, const QString &input, const QString &output, const AuditLogEntry &auditLog)
         : Task::Result()
         , m_sign(sign)
         , m_encrypt(encrypt)
         , m_error(err)
         , m_errString(errStr)
         , m_inputLabel(input)
         , m_outputLabel(output)
         , m_auditLog(auditLog)
     {
     }
 
     QString overview() const override;
     QString details() const override;
     GpgME::Error error() const override
     {
         return m_error;
     }
     QString errorString() const override
     {
         return m_errString;
     }
     AuditLogEntry auditLog() const override
     {
         return m_auditLog;
     }
 
 private:
     const bool m_sign;
     const bool m_encrypt;
     const Error m_error;
     const QString m_errString;
     const QString m_inputLabel;
     const QString m_outputLabel;
     const AuditLogEntry m_auditLog;
 };
 
 namespace
 {
 struct LabelAndError {
     QString label;
     QString errorString;
     QStringList fileNames;
 };
 }
 
 class SignEncryptFilesResult : public Task::Result
 {
 public:
     SignEncryptFilesResult(const SigningResult &sr,
                            const EncryptionResult &er,
                            const LabelAndError &input,
                            const LabelAndError &output,
                            const AuditLogEntry &auditLog)
         : Task::Result()
         , m_sresult(sr)
         , m_eresult(er)
         , m_input{input}
         , m_output{output}
         , m_auditLog(auditLog)
     {
         qCDebug(KLEOPATRA_LOG) << "\ninputError :" << m_input.errorString << "\noutputError:" << m_output.errorString;
         Q_ASSERT(!m_sresult.isNull() || !m_eresult.isNull());
     }
 
     QString overview() const override;
     QString details() const override;
     GpgME::Error error() const override;
     QString errorString() const override;
     AuditLogEntry auditLog() const override;
 
 private:
     const SigningResult m_sresult;
     const EncryptionResult m_eresult;
     const LabelAndError m_input;
     const LabelAndError m_output;
     const AuditLogEntry m_auditLog;
 };
 
 QString formatResultLine(const QStringList &inputs,
                          const QString &output,
                          bool sign,
                          bool encrypt,
                          bool signingFailed,
                          bool encryptionFailed,
                          const GpgME::Error &error)
 {
     Q_ASSERT(inputs.size() > 0);
     Q_ASSERT(sign || encrypt);
 
     if (error.isCanceled()) {
         if (sign && encrypt) {
             return i18nc("@info", "Signing and encryption canceled.");
         }
         if (sign) {
             return i18nc("@info", "Signing canceled.");
         }
 
         return i18nc("@info", "Encryption canceled.");
     }
 
     if (signingFailed && encryptionFailed) {
         if (inputs.size() == 1) {
             return xi18nc("@info Failed to sign and encrypt <file>: <reason>",
                           "Failed to sign and encrypt <filename>%1</filename>: <emphasis strong='true'>%2</emphasis>",
                           inputs[0],
                           Formatting::errorAsString(error));
         }
         if (inputs.size() == 2) {
             return xi18nc("@info Failed to sign and encrypt <file> and <file>: <reason>",
                           "Failed to sign and encrypt <filename>%1</filename> and <filename>%2</filename>: <emphasis strong='true'>%3</emphasis>",
                           inputs[0],
                           inputs[1],
                           Formatting::errorAsString(error));
         }
         return xi18ncp("@info Failed to sign and encrypt <file> and <n> other(s): <reason>",
                        "Failed to sign and encrypt <filename>%2</filename> and %1 other: <emphasis strong='true'>%3</emphasis>",
                        "Failed to sign and encrypt <filename>%2</filename> and %1 others: <emphasis strong='true'>%3</emphasis>",
                        inputs.size() - 1,
                        inputs[0],
                        Formatting::errorAsString(error));
     }
 
     if (signingFailed) {
         if (inputs.size() == 1) {
             return xi18nc("@info Failed to sign <file>: <reason>",
                           "Failed to sign <filename>%1</filename>: <emphasis strong='true'>%2</emphasis>",
                           inputs[0],
                           Formatting::errorAsString(error));
         }
         if (inputs.size() == 2) {
             return xi18nc("@info Failed to sign <file> and <file>: <reason>",
                           "Failed to sign <filename>%1</filename> and <filename>%2</filename>: <emphasis strong='true'>%3</emphasis>",
                           inputs[0],
                           inputs[1],
                           Formatting::errorAsString(error));
         }
         return xi18ncp("@info Failed to sign <file> and <n> other(s): <reason>",
                        "Failed to sign <filename>%2</filename> and %1 other: <emphasis strong='true'>%3</emphasis>",
                        "Failed to sign <filename>%2</filename> and %1 others: <emphasis strong='true'>%3</emphasis>",
                        inputs.size() - 1,
                        inputs[0],
                        Formatting::errorAsString(error));
     }
 
     if (encryptionFailed) {
         if (inputs.size() == 1) {
             return xi18nc("@info Failed to encrypt <file>: <reason>",
                           "Failed to encrypt <filename>%1</filename>: <emphasis strong='true'>%2</emphasis>",
                           inputs[0],
                           Formatting::errorAsString(error));
         }
         if (inputs.size() == 2) {
             return xi18nc("@info Failed to encrypt <file> and <file>: <reason>",
                           "Failed to encrypt <filename>%1</filename> and <filename>%2</filename>: <emphasis strong='true'>%3</emphasis>",
                           inputs[0],
                           inputs[1],
                           Formatting::errorAsString(error));
         }
         return xi18ncp("@info Failed to encrypt <file> and <n> other(s): <reason>",
                        "Failed to encrypt <filename>%2</filename> and %1 other: <emphasis strong='true'>%3</emphasis>",
                        "Failed to encrypt <filename>%2</filename> and %1 others: <emphasis strong='true'>%3</emphasis>",
                        inputs.size() - 1,
                        inputs[0],
                        Formatting::errorAsString(error));
     }
 
     if (sign && encrypt) {
         if (inputs.size() == 1) {
             return xi18nc("@info Successfully signed and encrypted <file> and saved it as <file>.",
                           "Successfully signed and encrypted <filename>%1</filename> and saved it as <filename>%2</filename>.",
                           inputs[0],
                           output);
         }
         if (inputs.size() == 2) {
             return xi18nc("@info Successfully signed and encrypted <file> and <file> and saved it as <file>.",
                           "Successfully signed and encrypted <filename>%1</filename> and <filename>%2</filename> and saved it as <filename>%3</filename>.",
                           inputs[0],
                           inputs[1],
                           output);
         }
         return xi18ncp("@info Successfully signed and encrypted <file> and <n> other(s) as <file>.",
                        "Successfully signed and encrypted <filename>%2</filename> and %1 other and saved it as <filename>%3</filename>.",
                        "Successfully signed and encrypted <filename>%2</filename> and %1 others and saved it as <filename>%3</filename>.",
                        inputs.size() - 1,
                        inputs[0],
                        output);
     }
 
     if (sign) {
         if (inputs.size() == 1) {
             return xi18nc("@info Successfully signed <file> and saved the signature in <file>.",
                           "Successfully signed <filename>%1</filename> and saved the signature in <filename>%2</filename>.",
                           inputs[0],
                           output);
         }
         if (inputs.size() == 2) {
             return xi18nc("@info Successfully signed <file> and <file> and saved the signature in <file>.",
                           "Successfully signed <filename>%1</filename> and <filename>%2</filename> and saved the signature in <filename>%3</filename>.",
                           inputs[0],
                           inputs[1],
                           output);
         }
         return xi18ncp("@info Successfully signed <file> and <n> other(s) and saved the signature in <file>.",
                        "Successfully signed <filename>%2</filename> and %1 other and saved the signature in <filename>%3</filename>.",
                        "Successfully signed <filename>%2</filename> and %1 others and saved the signature in <filename>%3</filename>.",
                        inputs.size() - 1,
                        inputs[0],
                        output);
     }
 
     if (inputs.size() == 1) {
         return xi18nc("@info Successfully encrypted <file> and saved it as <file>.",
                       "Successfully encrypted <filename>%1</filename> and saved it as <filename>%2</filename>.",
                       inputs[0],
                       output);
     }
     if (inputs.size() == 2) {
         return xi18nc("@info Successfully encrypted <file> and <file> and saved it as <file>.",
                       "Successfully encrypted <filename>%1</filename> and <filename>%2</filename> and saved it as <filename>%3</filename>.",
                       inputs[0],
                       inputs[1],
                       output);
     }
     return xi18ncp("@info Successfully encrypted <file> and <n> other(s) and saved it as <file>.",
                    "Successfully encrypted <filename>%2</filename> and %1 other and saved it as <filename>%3</filename>.",
                    "Successfully encrypted <filename>%2</filename> and %1 others and saved it as <filename>%3</filename>.",
                    inputs.size() - 1,
                    inputs[0],
                    output);
 }
 
 static QString escape(QString s)
 {
     s = s.toHtmlEscaped();
     s.replace(QLatin1Char('\n'), QStringLiteral("<br>"));
     return s;
 }
 
 static QString makeResultDetails(const SigningResult &result, const QString &inputError, const QString &outputError)
 {
     const Error err = result.error();
     if (err.code() == GPG_ERR_EIO) {
         if (!inputError.isEmpty()) {
             return i18n("Input error: %1", escape(inputError));
         } else if (!outputError.isEmpty()) {
             return i18n("Output error: %1", escape(outputError));
         }
     }
 
     if (err || err.isCanceled()) {
         return Formatting::errorAsString(err).toHtmlEscaped();
     }
     return QString();
 }
 
 static QString makeResultDetails(const EncryptionResult &result, const QString &inputError, const QString &outputError)
 {
     const Error err = result.error();
     if (err.code() == GPG_ERR_EIO) {
         if (!inputError.isEmpty()) {
             return i18n("Input error: %1", escape(inputError));
         } else if (!outputError.isEmpty()) {
             return i18n("Output error: %1", escape(outputError));
         }
     }
 
     if (err || err.isCanceled()) {
         return Formatting::errorAsString(err).toHtmlEscaped();
     }
     return {};
 }
 
 }
 
 QString ErrorResult::overview() const
 {
     Q_ASSERT(m_error || m_error.isCanceled());
     Q_ASSERT(m_sign || m_encrypt);
 
     return formatResultLine({m_inputLabel}, m_outputLabel, m_sign, m_encrypt, true, true, m_error);
 }
 
 QString ErrorResult::details() const
 {
     return m_errString;
 }
 
 class SignEncryptTask::Private
 {
     friend class ::Kleo::Crypto::SignEncryptTask;
     SignEncryptTask *const q;
 
 public:
     explicit Private(SignEncryptTask *qq);
 
 private:
     QString inputLabel() const;
     QString outputLabel() const;
 
     bool removeExistingOutputFile();
 
     void startSignEncryptJob(GpgME::Protocol proto);
     std::unique_ptr<QGpgME::SignJob> createSignJob(GpgME::Protocol proto);
     std::unique_ptr<QGpgME::SignEncryptJob> createSignEncryptJob(GpgME::Protocol proto);
     std::unique_ptr<QGpgME::EncryptJob> createEncryptJob(GpgME::Protocol proto);
 
     void startSignEncryptArchiveJob(GpgME::Protocol proto);
     std::unique_ptr<QGpgME::SignArchiveJob> createSignArchiveJob(GpgME::Protocol proto);
     std::unique_ptr<QGpgME::SignEncryptArchiveJob> createSignEncryptArchiveJob(GpgME::Protocol proto);
     std::unique_ptr<QGpgME::EncryptArchiveJob> createEncryptArchiveJob(GpgME::Protocol proto);
 
     std::shared_ptr<const Task::Result> makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog);
 
 private:
     void slotResult(const SigningResult &);
     void slotResult(const SigningResult &, const EncryptionResult &);
     void slotResult(const EncryptionResult &);
 
     void slotResult(const QGpgME::Job *, const SigningResult &, const EncryptionResult &);
 
 private:
     std::shared_ptr<Input> input;
     std::shared_ptr<Output> output;
     QStringList inputFileNames;
     QString outputFileName;
     std::vector<Key> signers;
     std::vector<Key> recipients;
     SignEncryptTask::DataSource dataSource = SignEncryptTask::DataSource::Files;
 
     bool sign : 1;
     bool encrypt : 1;
     bool detached : 1;
     bool symmetric : 1;
     bool clearsign : 1;
     bool archive : 1;
 
     QPointer<QGpgME::Job> job;
     QString labelText;
     std::shared_ptr<OverwritePolicy> m_overwritePolicy;
 };
 
 SignEncryptTask::Private::Private(SignEncryptTask *qq)
     : q{qq}
     , sign{true}
     , encrypt{true}
     , detached{false}
     , clearsign{false}
     , archive{false}
     , m_overwritePolicy{new OverwritePolicy{OverwritePolicy::Ask}}
 {
     q->setAsciiArmor(true);
 }
 
 std::shared_ptr<const Task::Result> SignEncryptTask::Private::makeErrorResult(const Error &err, const QString &errStr, const AuditLogEntry &auditLog)
 {
     return std::shared_ptr<const ErrorResult>(new ErrorResult(sign, encrypt, err, errStr, inputLabel(), outputLabel(), auditLog));
 }
 
 SignEncryptTask::SignEncryptTask(QObject *p)
     : Task(p)
     , d(new Private(this))
 {
 }
 
 SignEncryptTask::~SignEncryptTask()
 {
 }
 
 void SignEncryptTask::setInputFileName(const QString &fileName)
 {
     kleo_assert(!d->job);
     kleo_assert(!fileName.isEmpty());
     d->inputFileNames = QStringList(fileName);
 }
 
 void SignEncryptTask::setInputFileNames(const QStringList &fileNames)
 {
     kleo_assert(!d->job);
     kleo_assert(!fileNames.empty());
     d->inputFileNames = fileNames;
 }
 
 void SignEncryptTask::setInput(const std::shared_ptr<Input> &input)
 {
     kleo_assert(!d->job);
     kleo_assert(input);
     d->input = input;
 }
 
 void SignEncryptTask::setOutput(const std::shared_ptr<Output> &output)
 {
     kleo_assert(!d->job);
     kleo_assert(output);
     d->output = output;
 }
 
 void SignEncryptTask::setOutputFileName(const QString &fileName)
 {
     kleo_assert(!d->job);
     kleo_assert(!fileName.isEmpty());
     d->outputFileName = fileName;
 }
 
 QString SignEncryptTask::outputFileName() const
 {
     return d->outputFileName;
 }
 
 void SignEncryptTask::setSigners(const std::vector<Key> &signers)
 {
     kleo_assert(!d->job);
     d->signers = signers;
 }
 
 void SignEncryptTask::setRecipients(const std::vector<Key> &recipients)
 {
     kleo_assert(!d->job);
     d->recipients = recipients;
 }
 
 void SignEncryptTask::setOverwritePolicy(const std::shared_ptr<OverwritePolicy> &policy)
 {
     kleo_assert(!d->job);
     d->m_overwritePolicy = policy;
 }
 
 void SignEncryptTask::setSign(bool sign)
 {
     kleo_assert(!d->job);
     d->sign = sign;
 }
 
 void SignEncryptTask::setEncrypt(bool encrypt)
 {
     kleo_assert(!d->job);
     d->encrypt = encrypt;
 }
 
 void SignEncryptTask::setDetachedSignature(bool detached)
 {
     kleo_assert(!d->job);
     d->detached = detached;
 }
 
 bool SignEncryptTask::detachedSignatureEnabled() const
 {
     return d->detached;
 }
 
 void SignEncryptTask::setEncryptSymmetric(bool symmetric)
 {
     kleo_assert(!d->job);
     d->symmetric = symmetric;
 }
 
 void SignEncryptTask::setClearsign(bool clearsign)
 {
     kleo_assert(!d->job);
     d->clearsign = clearsign;
 }
 
 void SignEncryptTask::setCreateArchive(bool archive)
 {
     kleo_assert(!d->job);
     d->archive = archive;
 }
 
 Protocol SignEncryptTask::protocol() const
 {
     if (d->sign && !d->signers.empty()) {
         return d->signers.front().protocol();
     }
     if (d->encrypt || d->symmetric) {
         if (!d->recipients.empty()) {
             return d->recipients.front().protocol();
         } else {
             return GpgME::OpenPGP; // symmetric OpenPGP encryption
         }
     }
     throw Kleo::Exception(gpg_error(GPG_ERR_INTERNAL), i18n("Cannot determine protocol for task"));
 }
 
 QString SignEncryptTask::label() const
 {
     if (!d->labelText.isEmpty()) {
         return d->labelText;
     }
     return d->inputLabel();
 }
 
 QString SignEncryptTask::tag() const
 {
     return Formatting::displayName(protocol());
 }
 
 unsigned long long SignEncryptTask::inputSize() const
 {
     return d->input ? d->input->size() : 0U;
 }
 
 static bool archiveJobsCanBeUsed(GpgME::Protocol protocol)
 {
     return (protocol == GpgME::OpenPGP) && QGpgME::SignEncryptArchiveJob::isSupported();
 }
 
 void SignEncryptTask::doStart()
 {
     kleo_assert(!d->job);
     if (d->sign) {
         kleo_assert(!d->signers.empty());
         if (d->archive) {
             kleo_assert(!d->detached && !d->clearsign);
         }
     }
 
     const auto proto = protocol();
     if (d->archive && archiveJobsCanBeUsed(proto)) {
         d->startSignEncryptArchiveJob(proto);
     } else {
         d->startSignEncryptJob(proto);
     }
 }
 
 QString SignEncryptTask::Private::inputLabel() const
 {
     if (input) {
         return input->label();
     }
     if (!inputFileNames.empty()) {
         const auto firstFile = QFileInfo{inputFileNames.front()}.fileName();
         return inputFileNames.size() == 1 ? firstFile : i18nc("<name of first file>, ...", "%1, ...", firstFile);
     }
     return {};
 }
 
 QString SignEncryptTask::Private::outputLabel() const
 {
     return output ? output->label() : QFileInfo{outputFileName}.fileName();
 }
 
 bool SignEncryptTask::Private::removeExistingOutputFile()
 {
     if (QFile::exists(outputFileName)) {
         bool fileRemoved = false;
         // we should already have asked the user for overwrite permission
         if (m_overwritePolicy && (m_overwritePolicy->policy() == OverwritePolicy::Overwrite)) {
             qCDebug(KLEOPATRA_LOG) << __func__ << "going to remove file for overwriting" << outputFileName;
             fileRemoved = QFile::remove(outputFileName);
             if (!fileRemoved) {
                 qCDebug(KLEOPATRA_LOG) << __func__ << "removing file to overwrite failed";
             }
         } else {
             qCDebug(KLEOPATRA_LOG) << __func__ << "we have no permission to overwrite" << outputFileName;
         }
         if (!fileRemoved) {
             QMetaObject::invokeMethod(
                 q,
                 [this]() {
                     slotResult(nullptr, SigningResult{}, EncryptionResult{Error::fromCode(GPG_ERR_EEXIST)});
                 },
                 Qt::QueuedConnection);
             return false;
         }
     }
 
     return true;
 }
 
 void SignEncryptTask::Private::startSignEncryptJob(GpgME::Protocol proto)
 {
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
     if (proto == GpgME::OpenPGP) {
         // either input and output are both set (e.g. when encrypting the notepad),
         // or they are both unset (when encrypting files)
         kleo_assert((!input && !output) || (input && output));
     } else {
         kleo_assert(input);
 
         if (!output) {
             output = Output::createFromFile(outputFileName, m_overwritePolicy);
         }
     }
 #else
     kleo_assert(input);
 
     if (!output) {
         output = Output::createFromFile(outputFileName, m_overwritePolicy);
     }
 #endif
 
     if (encrypt || symmetric) {
         Context::EncryptionFlags flags{Context::None};
         if (proto == GpgME::OpenPGP) {
             flags = static_cast<Context::EncryptionFlags>(flags | Context::AlwaysTrust);
         }
         if (symmetric) {
             flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric);
             qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag";
         }
         if (sign) {
             std::unique_ptr<QGpgME::SignEncryptJob> job = createSignEncryptJob(proto);
             kleo_assert(job.get());
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
             if (proto == GpgME::OpenPGP && !input && !output) {
                 kleo_assert(inputFileNames.size() == 1);
                 job->setSigners(signers);
                 job->setRecipients(recipients);
                 job->setInputFile(inputFileNames.front());
                 job->setOutputFile(outputFileName);
                 job->setEncryptionFlags(flags);
                 if (!removeExistingOutputFile()) {
                     return;
                 }
                 job->startIt();
             } else {
                 if (inputFileNames.size() == 1) {
                     job->setFileName(inputFileNames.front());
                 }
                 job->start(signers, recipients, input->ioDevice(), output->ioDevice(), flags);
             }
 #else
             if (inputFileNames.size() == 1) {
                 job->setFileName(inputFileNames.front());
             }
             job->start(signers, recipients, input->ioDevice(), output->ioDevice(), flags);
 #endif
             this->job = job.release();
         } else {
             std::unique_ptr<QGpgME::EncryptJob> job = createEncryptJob(proto);
             kleo_assert(job.get());
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
             if (proto == GpgME::OpenPGP && !input && !output) {
                 kleo_assert(inputFileNames.size() == 1);
                 job->setRecipients(recipients);
                 job->setInputFile(inputFileNames.front());
                 job->setOutputFile(outputFileName);
                 job->setEncryptionFlags(flags);
                 if (!removeExistingOutputFile()) {
                     return;
                 }
                 job->startIt();
             } else {
                 if (inputFileNames.size() == 1) {
                     job->setFileName(inputFileNames.front());
                 }
                 job->start(recipients, input->ioDevice(), output->ioDevice(), flags);
             }
 #else
             if (inputFileNames.size() == 1) {
                 job->setFileName(inputFileNames.front());
             }
             job->start(recipients, input->ioDevice(), output->ioDevice(), flags);
 #endif
             this->job = job.release();
         }
     } else if (sign) {
         std::unique_ptr<QGpgME::SignJob> job = createSignJob(proto);
         kleo_assert(job.get());
         kleo_assert(!(detached && clearsign));
         const GpgME::SignatureMode sigMode = detached ? GpgME::Detached : clearsign ? GpgME::Clearsigned : GpgME::NormalSignatureMode;
 #if QGPGME_FILE_JOBS_SUPPORT_DIRECT_FILE_IO
         if (proto == GpgME::OpenPGP && !input && !output) {
             kleo_assert(inputFileNames.size() == 1);
             job->setSigners(signers);
             job->setInputFile(inputFileNames.front());
             job->setOutputFile(outputFileName);
             job->setSigningFlags(sigMode);
             if (QFile::exists(outputFileName) && m_overwritePolicy && (m_overwritePolicy->policy() == OverwritePolicy::Append)) {
                 job->setAppendSignature(true);
             } else if (!removeExistingOutputFile()) {
                 return;
             }
             job->startIt();
         } else {
             job->start(signers, input->ioDevice(), output->ioDevice(), sigMode);
         }
 #else
         job->start(signers, input->ioDevice(), output->ioDevice(), sigMode);
 #endif
         this->job = job.release();
     } else {
         kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!");
     }
 }
 
 void SignEncryptTask::cancel()
 {
     qCDebug(KLEOPATRA_LOG) << this << __func__;
     if (d->job) {
         d->job->slotCancel();
     }
 }
 
 std::unique_ptr<QGpgME::SignJob> SignEncryptTask::Private::createSignJob(GpgME::Protocol proto)
 {
     const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
     kleo_assert(backend);
     std::unique_ptr<QGpgME::SignJob> signJob(backend->signJob(q->asciiArmor(), /*textmode=*/false));
     kleo_assert(signJob.get());
     connect(signJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress);
     connect(signJob.get(), &QGpgME::SignJob::result, q, [this](const GpgME::SigningResult &signingResult, const QByteArray &) {
         slotResult(signingResult);
     });
     return signJob;
 }
 
 std::unique_ptr<QGpgME::SignEncryptJob> SignEncryptTask::Private::createSignEncryptJob(GpgME::Protocol proto)
 {
     const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
     kleo_assert(backend);
     std::unique_ptr<QGpgME::SignEncryptJob> signEncryptJob(backend->signEncryptJob(q->asciiArmor(), /*textmode=*/false));
     kleo_assert(signEncryptJob.get());
     connect(signEncryptJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress);
     connect(signEncryptJob.get(),
             &QGpgME::SignEncryptJob::result,
             q,
             [this](const GpgME::SigningResult &signingResult, const GpgME::EncryptionResult &encryptionResult) {
                 slotResult(signingResult, encryptionResult);
             });
     return signEncryptJob;
 }
 
 std::unique_ptr<QGpgME::EncryptJob> SignEncryptTask::Private::createEncryptJob(GpgME::Protocol proto)
 {
     const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
     kleo_assert(backend);
     std::unique_ptr<QGpgME::EncryptJob> encryptJob(backend->encryptJob(q->asciiArmor(), /*textmode=*/false));
     kleo_assert(encryptJob.get());
     connect(encryptJob.get(), &QGpgME::Job::jobProgress, q, &SignEncryptTask::setProgress);
     connect(encryptJob.get(), &QGpgME::EncryptJob::result, q, [this](const GpgME::EncryptionResult &encryptionResult) {
         slotResult(encryptionResult);
     });
     return encryptJob;
 }
 
 void SignEncryptTask::Private::startSignEncryptArchiveJob(GpgME::Protocol proto)
 {
     kleo_assert(!input);
     kleo_assert(!output);
 
     const auto baseDirectory = heuristicBaseDirectory(inputFileNames);
     if (baseDirectory.isEmpty()) {
         throw Kleo::Exception(GPG_ERR_CONFLICT, i18n("Cannot find common base directory for these files:\n%1", inputFileNames.join(QLatin1Char('\n'))));
     }
     qCDebug(KLEOPATRA_LOG) << "heuristicBaseDirectory(" << inputFileNames << ") ->" << baseDirectory;
     const auto tempPaths = makeRelativeTo(baseDirectory, inputFileNames);
     const auto relativePaths = std::vector<QString>{tempPaths.begin(), tempPaths.end()};
     qCDebug(KLEOPATRA_LOG) << "relative paths:" << relativePaths;
 
     if (encrypt || symmetric) {
         Context::EncryptionFlags flags{Context::None};
         if (proto == GpgME::OpenPGP) {
             flags = static_cast<Context::EncryptionFlags>(flags | Context::AlwaysTrust);
         }
         if (symmetric) {
             flags = static_cast<Context::EncryptionFlags>(flags | Context::Symmetric);
             qCDebug(KLEOPATRA_LOG) << "Adding symmetric flag";
         }
         if (sign) {
             labelText = i18nc("@info", "Creating signed and encrypted archive ...");
             std::unique_ptr<QGpgME::SignEncryptArchiveJob> job = createSignEncryptArchiveJob(proto);
             kleo_assert(job.get());
             job->setBaseDirectory(baseDirectory);
             job->setSigners(signers);
             job->setRecipients(recipients);
             job->setInputPaths(relativePaths);
             job->setOutputFile(outputFileName);
             job->setEncryptionFlags(flags);
             if (!removeExistingOutputFile()) {
                 return;
             }
             job->startIt();
 
             this->job = job.release();
         } else {
             labelText = i18nc("@info", "Creating encrypted archive ...");
             std::unique_ptr<QGpgME::EncryptArchiveJob> job = createEncryptArchiveJob(proto);
             kleo_assert(job.get());
             job->setBaseDirectory(baseDirectory);
             job->setRecipients(recipients);
             job->setInputPaths(relativePaths);
             job->setOutputFile(outputFileName);
             job->setEncryptionFlags(flags);
             if (!removeExistingOutputFile()) {
                 return;
             }
             job->startIt();
 
             this->job = job.release();
         }
     } else if (sign) {
         labelText = i18nc("@info", "Creating signed archive ...");
         std::unique_ptr<QGpgME::SignArchiveJob> job = createSignArchiveJob(proto);
         kleo_assert(job.get());
         job->setBaseDirectory(baseDirectory);
         job->setSigners(signers);
         job->setInputPaths(relativePaths);
         job->setOutputFile(outputFileName);
         if (!removeExistingOutputFile()) {
             return;
         }
         job->startIt();
 
         this->job = job.release();
     } else {
         kleo_assert(!"Either 'sign' or 'encrypt' or 'symmetric' must be set!");
     }
 }
 
 std::unique_ptr<QGpgME::SignArchiveJob> SignEncryptTask::Private::createSignArchiveJob(GpgME::Protocol proto)
 {
     const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
     kleo_assert(backend);
     std::unique_ptr<QGpgME::SignArchiveJob> signJob(backend->signArchiveJob(q->asciiArmor()));
     auto job = signJob.get();
     kleo_assert(job);
     connect(job, &QGpgME::SignArchiveJob::dataProgress, q, &SignEncryptTask::setProgress);
     connect(job, &QGpgME::SignArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult) {
         slotResult(job, signResult, EncryptionResult{});
     });
     return signJob;
 }
 
 std::unique_ptr<QGpgME::SignEncryptArchiveJob> SignEncryptTask::Private::createSignEncryptArchiveJob(GpgME::Protocol proto)
 {
     const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
     kleo_assert(backend);
     std::unique_ptr<QGpgME::SignEncryptArchiveJob> signEncryptJob(backend->signEncryptArchiveJob(q->asciiArmor()));
     auto job = signEncryptJob.get();
     kleo_assert(job);
     connect(job, &QGpgME::SignEncryptArchiveJob::dataProgress, q, &SignEncryptTask::setProgress);
     connect(job, &QGpgME::SignEncryptArchiveJob::result, q, [this, job](const GpgME::SigningResult &signResult, const GpgME::EncryptionResult &encryptResult) {
         slotResult(job, signResult, encryptResult);
     });
     return signEncryptJob;
 }
 
 std::unique_ptr<QGpgME::EncryptArchiveJob> SignEncryptTask::Private::createEncryptArchiveJob(GpgME::Protocol proto)
 {
     const QGpgME::Protocol *const backend = (proto == GpgME::OpenPGP) ? QGpgME::openpgp() : QGpgME::smime();
     kleo_assert(backend);
     std::unique_ptr<QGpgME::EncryptArchiveJob> encryptJob(backend->encryptArchiveJob(q->asciiArmor()));
     auto job = encryptJob.get();
     kleo_assert(job);
     connect(job, &QGpgME::EncryptArchiveJob::dataProgress, q, &SignEncryptTask::setProgress);
     connect(job, &QGpgME::EncryptArchiveJob::result, q, [this, job](const GpgME::EncryptionResult &encryptResult) {
         slotResult(job, SigningResult{}, encryptResult);
     });
     return encryptJob;
 }
 
 void SignEncryptTask::Private::slotResult(const SigningResult &result)
 {
     slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), result, EncryptionResult{});
 }
 
 void SignEncryptTask::Private::slotResult(const SigningResult &sresult, const EncryptionResult &eresult)
 {
     slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), sresult, eresult);
 }
 
 void SignEncryptTask::Private::slotResult(const EncryptionResult &result)
 {
     slotResult(qobject_cast<const QGpgME::Job *>(q->sender()), SigningResult{}, result);
 }
 
 void SignEncryptTask::Private::slotResult(const QGpgME::Job *job, const SigningResult &sresult, const EncryptionResult &eresult)
 {
     qCDebug(KLEOPATRA_LOG) << q << __func__ << "job:" << job << "signing result:" << QGpgME::toLogString(sresult)
                            << "encryption result:" << QGpgME::toLogString(eresult);
     const AuditLogEntry auditLog = AuditLogEntry::fromJob(job);
     if (input && input->failed()) {
         if (output) {
             output->cancel();
         }
         q->emitResult(makeErrorResult(Error::fromCode(GPG_ERR_EIO), i18n("Input error: %1", escape(input->errorString())), auditLog));
         return;
     } else if (sresult.error().code() || eresult.error().code()) {
         if (output) {
             output->cancel();
         }
         if (!outputFileName.isEmpty() && eresult.error().code() != GPG_ERR_EEXIST) {
             // ensure that the output file is removed if the task was canceled or an error occurred;
             // unless a "file exists" error occurred because this means that the file with the name
             // of outputFileName wasn't created as result of this task
             if (QFile::exists(outputFileName)) {
                 qCDebug(KLEOPATRA_LOG) << __func__ << "Removing output file" << outputFileName << "after error or cancel";
                 if (!QFile::remove(outputFileName)) {
                     qCDebug(KLEOPATRA_LOG) << __func__ << "Removing output file" << outputFileName << "failed";
                 }
             }
         }
     } else {
         try {
             kleo_assert(!sresult.isNull() || !eresult.isNull());
             if (output) {
                 output->finalize();
             }
             if (input) {
                 input->finalize();
             }
         } catch (const GpgME::Exception &e) {
             q->emitResult(makeErrorResult(e.error(), QString::fromLocal8Bit(e.what()), auditLog));
             return;
         }
     }
 
     const LabelAndError inputInfo{inputLabel(), input ? input->errorString() : QString{}, inputFileNames};
     const LabelAndError outputInfo{outputLabel(), output ? output->errorString() : QString{}, {}};
     auto result = std::shared_ptr<Result>(new SignEncryptFilesResult(sresult, eresult, inputInfo, outputInfo, auditLog));
     result->setDataSource(dataSource);
     q->emitResult(result);
 }
 
 QString SignEncryptFilesResult::overview() const
 {
     if (dataSource() == Task::Notepad) {
         if (!m_sresult.isNull() && m_sresult.error()) {
             return i18nc("@info", "Failed to sign the notepad: %1", Formatting::errorAsString(m_sresult.error()));
         }
         if (!m_eresult.isNull() && m_eresult.error()) {
             return i18nc("@info", "Failed to encrypt the notepad: %1", Formatting::errorAsString(m_eresult.error()));
         }
 
         if (!m_sresult.isNull() && !m_eresult.isNull()) {
             return i18nc("@info", "Successfully encrypted and signed the notepad");
         } else if (!m_eresult.isNull()) {
             return i18nc("@info", "Successfully encrypted the notepad");
         } else {
             return i18nc("@info", "Successfully signed the notepad");
         }
         return {};
     }
 
+    if (dataSource() == Task::Clipboard) {
+        if (!m_sresult.isNull() && m_sresult.error()) {
+            return i18nc("@info", "Failed to sign the clipboard: %1", Formatting::errorAsString(m_sresult.error()));
+        }
+        if (!m_eresult.isNull() && m_eresult.error()) {
+            return i18nc("@info", "Failed to encrypt the clipboard: %1", Formatting::errorAsString(m_eresult.error()));
+        }
+
+        if (!m_sresult.isNull() && !m_eresult.isNull()) {
+            return i18nc("@info", "Successfully encrypted and signed the clipboard");
+        } else if (!m_eresult.isNull()) {
+            return i18nc("@info", "Successfully encrypted the clipboard");
+        } else {
+            return i18nc("@info", "Successfully signed the clipboard");
+        }
+        return {};
+    }
+
     return formatResultLine(m_input.fileNames,
                             m_output.label,
                             !m_sresult.isNull(),
                             !m_eresult.isNull(),
                             m_sresult.error(),
                             m_eresult.error(),
                             m_sresult.error().code() ? m_sresult.error() : m_eresult.error());
 }
 
 QString SignEncryptFilesResult::details() const
 {
     return errorString();
 }
 
 GpgME::Error SignEncryptFilesResult::error() const
 {
     if (m_sresult.error().code()) {
         return m_sresult.error();
     }
     if (m_eresult.error().code()) {
         return m_eresult.error();
     }
     return {};
 }
 
 QString SignEncryptFilesResult::errorString() const
 {
     const bool sign = !m_sresult.isNull();
     const bool encrypt = !m_eresult.isNull();
 
     kleo_assert(sign || encrypt);
 
     if (sign && encrypt) {
         return m_sresult.error().code() ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString)
             : m_eresult.error().code()  ? makeResultDetails(m_eresult, m_input.errorString, m_output.errorString)
                                         : QString();
     }
 
     return sign ? makeResultDetails(m_sresult, m_input.errorString, m_output.errorString) //
                 : makeResultDetails(m_eresult, m_input.errorString, m_output.errorString);
 }
 
 AuditLogEntry SignEncryptFilesResult::auditLog() const
 {
     return m_auditLog;
 }
 
 void SignEncryptTask::setDataSource(Task::DataSource dataSource)
 {
     d->dataSource = dataSource;
 }
 
 #include "moc_signencrypttask.cpp"
diff --git a/src/dialogs/signencryptclipboarddialog.cpp b/src/dialogs/signencryptclipboarddialog.cpp
new file mode 100644
index 000000000..9d029c55e
--- /dev/null
+++ b/src/dialogs/signencryptclipboarddialog.cpp
@@ -0,0 +1,178 @@
+// SPDX-FileCopyrightText: 2024 g10 Code GmbH
+// SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#include "signencryptclipboarddialog.h"
+
+#include "crypto/gui/resultpage.h"
+#include "crypto/gui/signencryptwidget.h"
+#include "crypto/signencrypttask.h"
+#include "crypto/task.h"
+#include "crypto/taskcollection.h"
+#include "utils/input.h"
+#include "utils/output.h"
+#include "utils/scrollarea.h"
+
+#include <Libkleo/Compliance>
+
+#include <gpgme++/key.h>
+
+#include <KLocalizedString>
+#include <KMessageBox>
+
+#include <QApplication>
+#include <QClipboard>
+#include <QDialogButtonBox>
+#include <QPushButton>
+#include <QStackedLayout>
+#include <QTimer>
+#include <QVBoxLayout>
+
+using namespace Kleo;
+using namespace Kleo::Crypto;
+
+class SignEncryptPage : public QWidget
+{
+public:
+    explicit SignEncryptPage(QWidget *parent = nullptr)
+        : QWidget(parent)
+    {
+        auto mainLayout = new QVBoxLayout(this);
+        mainLayout->setContentsMargins({});
+        auto scrollArea = new Kleo::ScrollArea;
+        mainLayout->addWidget(scrollArea);
+
+        auto wrapper = new QWidget;
+        scrollArea->setWidget(wrapper);
+
+        scrollArea->setFrameStyle(0);
+        auto vLay = new QVBoxLayout(wrapper);
+        vLay->setContentsMargins({});
+        m_widget = new SignEncryptWidget(this, true);
+        vLay->addWidget(m_widget);
+    }
+
+    std::vector<GpgME::Key> recipients() const
+    {
+        return m_widget->recipients();
+    }
+
+    GpgME::Key signer() const
+    {
+        const auto key = m_widget->signUserId().parent();
+        if (!key.isNull()) {
+            return key;
+        }
+        return {};
+    }
+
+    SignEncryptWidget *signEncryptWidget() const
+    {
+        return m_widget;
+    }
+
+private:
+    SignEncryptWidget *m_widget;
+};
+
+SignEncryptClipboardDialog::SignEncryptClipboardDialog(QWidget *parent)
+    : QDialog(parent)
+{
+    setWindowTitle(i18nc("@title:dialog", "Sign/Encrypt Clipboard"));
+    auto layout = new QVBoxLayout(this);
+    auto stackedLayout = new QStackedLayout;
+
+    auto signEncryptPage = new SignEncryptPage;
+    stackedLayout->addWidget(signEncryptPage);
+
+    auto resultPage = new Kleo::Crypto::Gui::ResultPage;
+    stackedLayout->addWidget(resultPage);
+
+    layout->addLayout(stackedLayout);
+
+    auto buttons = new QDialogButtonBox;
+
+    QPushButton *labelButton = nullptr;
+
+    if (DeVSCompliance::isActive()) {
+        /* We use a custom button to display a label next to the
+           buttons. */
+        labelButton = buttons->addButton(QString(), QDialogButtonBox::ActionRole);
+        /* We style the button so that it looks and acts like a
+           label.  */
+        labelButton->setStyleSheet(QStringLiteral("border: none"));
+        labelButton->setFocusPolicy(Qt::NoFocus);
+    }
+
+    auto okButton = buttons->addButton(i18nc("@action:button", "Continue"), QDialogButtonBox::ActionRole);
+    buttons->addButton(QDialogButtonBox::Cancel);
+
+    layout->addWidget(buttons);
+
+    connect(signEncryptPage->signEncryptWidget(), &SignEncryptWidget::operationChanged, this, [okButton, signEncryptPage, labelButton]() {
+        okButton->setText(signEncryptPage->signEncryptWidget()->continueButtonText());
+        if (signEncryptPage->signEncryptWidget()->isComplete() && DeVSCompliance::isActive()) {
+            const bool de_vs = DeVSCompliance::isCompliant() && signEncryptPage->signEncryptWidget()->isDeVsAndValid();
+            DeVSCompliance::decorate(okButton, de_vs);
+
+            okButton->setToolTip(DeVSCompliance::name(de_vs));
+            labelButton->setText(DeVSCompliance::name(de_vs));
+        } else {
+            if (labelButton) {
+                labelButton->setText({});
+            }
+            okButton->setIcon(QIcon());
+            okButton->setStyleSheet(QString());
+        }
+        okButton->setEnabled(signEncryptPage->signEncryptWidget()->isComplete());
+    });
+
+    connect(okButton, &QPushButton::clicked, this, [this, signEncryptPage, stackedLayout, resultPage, okButton]() {
+        if (stackedLayout->currentIndex() == 0) {
+            m_task = std::make_shared<SignEncryptTask>();
+            m_task->setDataSource(Task::Clipboard);
+            auto output = Output::createFromClipboard();
+            m_task->setInput(m_input);
+            m_task->setOutput(output);
+
+            auto recipients = signEncryptPage->recipients();
+            auto signer = signEncryptPage->signer();
+
+            m_task->setRecipients(recipients);
+            m_task->setEncrypt(recipients.size() > 0);
+            m_task->setSigners({signer});
+            m_task->setSign(!signer.isNull());
+            m_task->setClearsign(!signer.isNull() && recipients.size() == 0 && signer.protocol() == GpgME::OpenPGP);
+            m_task->setEncryptSymmetric(signEncryptPage->signEncryptWidget()->encryptSymmetric());
+            m_task->setAsciiArmor(true);
+            m_task->start();
+
+            stackedLayout->setCurrentIndex(1);
+            okButton->setText(i18nc("@action:button", "Finish"));
+
+            std::shared_ptr<TaskCollection> coll(new TaskCollection);
+            coll->setTasks({m_task});
+            resultPage->setTaskCollection(coll);
+        } else {
+            accept();
+        }
+    });
+
+    QTimer::singleShot(100, this, [this]() {
+        if (qApp->clipboard()->text().isEmpty()) {
+            KMessageBox::information(this,
+                                     i18nc("@info", "The clipboard does not contain data that can be encrypted"),
+                                     i18nc("@title:dialog", "Sign/Encrypt Clipboard"));
+            reject();
+        } else {
+            m_input = Input::createFromClipboard();
+        }
+    });
+}
+
+SignEncryptClipboardDialog::~SignEncryptClipboardDialog()
+{
+    if (m_task) {
+        m_task->cancel();
+    }
+}
diff --git a/src/dialogs/signencryptclipboarddialog.h b/src/dialogs/signencryptclipboarddialog.h
new file mode 100644
index 000000000..75b4ef2d4
--- /dev/null
+++ b/src/dialogs/signencryptclipboarddialog.h
@@ -0,0 +1,32 @@
+// SPDX-FileCopyrightText: 2024 g10 Code GmbH
+// SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com>
+// SPDX-License-Identifier: GPL-2.0-or-later
+
+#pragma once
+
+#include <QDialog>
+
+namespace Kleo
+{
+class Input;
+namespace Crypto
+{
+class SignEncryptTask;
+}
+}
+
+class SignEncryptClipboardDialog : public QDialog
+{
+    Q_OBJECT
+
+public:
+    explicit SignEncryptClipboardDialog(QWidget *parent = nullptr);
+    ~SignEncryptClipboardDialog();
+
+    void setPlainText(const QString &plainText);
+
+private:
+    QString m_plainText;
+    std::shared_ptr<Kleo::Crypto::SignEncryptTask> m_task;
+    std::shared_ptr<Kleo::Input> m_input;
+};
diff --git a/src/systrayicon.cpp b/src/systrayicon.cpp
index 4317a0ce9..fe399f8d6 100644
--- a/src/systrayicon.cpp
+++ b/src/systrayicon.cpp
@@ -1,225 +1,224 @@
 /* -*- mode: c++; c-basic-offset:4 -*-
     systemtrayicon.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 <config-kleopatra.h>
 
 #include "systrayicon.h"
 
 #ifndef QT_NO_SYSTEMTRAYICON
 
 #include "kleopatraapplication.h"
 #include "mainwindow.h"
 
 #include <smartcard/readerstatus.h>
 
 #include <utils/clipboardmenu.h>
 
 #include <commands/decryptverifyclipboardcommand.h>
-#include <commands/encryptclipboardcommand.h>
 #include <commands/importcertificatefromclipboardcommand.h>
 #include <commands/setinitialpincommand.h>
-#include <commands/signclipboardcommand.h>
+#include <commands/signencryptclipboardcommand.h>
 
 #include <KAboutApplicationDialog>
 #include <KAboutData>
 #include <KActionMenu>
 #include <KLocalizedString>
 #include <QEventLoopLocker>
 #include <QIcon>
 
 #include <QAction>
 #include <QApplication>
 #include <QMenu>
 #include <QPointer>
 #include <QSignalBlocker>
 
 using namespace Kleo;
 using namespace Kleo::Commands;
 using namespace Kleo::SmartCard;
 
 class SysTrayIcon::Private
 {
     friend class ::SysTrayIcon;
     SysTrayIcon *const q;
 
 public:
     explicit Private(SysTrayIcon *qq);
     ~Private();
 
 private:
     void slotAbout()
     {
         if (!aboutDialog) {
             aboutDialog = new KAboutApplicationDialog(KAboutData::applicationData());
             aboutDialog->setAttribute(Qt::WA_DeleteOnClose);
         }
 
         if (aboutDialog->isVisible()) {
             aboutDialog->raise();
         } else {
             aboutDialog->show();
         }
     }
 
     void enableDisableActions()
     {
         openCertificateManagerAction.setEnabled(!q->mainWindow() || !q->mainWindow()->isVisible());
         setInitialPinAction.setEnabled(!firstCardWithNullPin.empty());
 
         q->setAttentionWanted(!firstCardWithNullPin.empty() && !q->attentionWindow());
     }
 
     void slotSetInitialPin()
     {
         if (!firstCardWithNullPin.empty()) {
             auto cmd = new SetInitialPinCommand(firstCardWithNullPin);
             q->setAttentionWindow(cmd->dialog());
             startCommand(cmd);
         }
     }
 
     void startCommand(Command *cmd)
     {
         Q_ASSERT(cmd);
         cmd->setParent(q->mainWindow());
         cmd->start();
     }
 
 private:
     std::string firstCardWithNullPin;
 
     QMenu menu;
     QAction openCertificateManagerAction;
     QAction configureAction;
     QAction aboutAction;
     QAction quitAction;
 
     ClipboardMenu clipboardMenu;
 
     QMenu cardMenu;
     QAction updateCardStatusAction;
     QAction setInitialPinAction;
 
     QPointer<KAboutApplicationDialog> aboutDialog;
 };
 
 SysTrayIcon::Private::Private(SysTrayIcon *qq)
     : q(qq)
     , menu()
     , openCertificateManagerAction(i18nc("@action:inmenu", "&Open Certificate Manager..."), q)
     , configureAction(QIcon::fromTheme(QStringLiteral("configure")),
                       xi18nc("@action:inmenu", "&Configure <application>%1</application>...", KAboutData::applicationData().displayName()),
                       q)
     , aboutAction(QIcon::fromTheme(QStringLiteral("kleopatra")),
                   xi18nc("@action:inmenu", "&About <application>%1</application>...", KAboutData::applicationData().displayName()),
                   q)
     , quitAction(QIcon::fromTheme(QStringLiteral("application-exit")),
                  xi18nc("@action:inmenu", "&Shutdown <application>%1</application>", KAboutData::applicationData().displayName()),
                  q)
     , clipboardMenu(q)
     , cardMenu(i18nc("@title:menu", "SmartCard"))
     , updateCardStatusAction(i18nc("@action:inmenu", "Update Card Status"), q)
     , setInitialPinAction(i18nc("@action:inmenu", "Set NetKey v3 Initial PIN..."), q)
     , aboutDialog()
 {
 #ifdef Q_OS_WIN
     q->setNormalIcon(QIcon::fromTheme(QStringLiteral("kleopatra")));
 #else
     q->setNormalIcon(QIcon::fromTheme(QStringLiteral("kleopatra-symbolic")));
 #endif
     q->setAttentionIcon(QIcon::fromTheme(QStringLiteral("auth-sim-locked")));
 
     Q_SET_OBJECT_NAME(menu);
     Q_SET_OBJECT_NAME(openCertificateManagerAction);
     Q_SET_OBJECT_NAME(configureAction);
     Q_SET_OBJECT_NAME(aboutAction);
     Q_SET_OBJECT_NAME(quitAction);
     Q_SET_OBJECT_NAME(clipboardMenu);
     Q_SET_OBJECT_NAME(cardMenu);
     Q_SET_OBJECT_NAME(setInitialPinAction);
 
     connect(&openCertificateManagerAction, SIGNAL(triggered()), qApp, SLOT(openOrRaiseMainWindow()));
     connect(&configureAction, SIGNAL(triggered()), qApp, SLOT(openOrRaiseConfigDialog()));
     connect(&aboutAction, SIGNAL(triggered()), q, SLOT(slotAbout()));
     connect(&quitAction, &QAction::triggered, QCoreApplication::instance(), &QCoreApplication::quit);
     connect(&updateCardStatusAction, &QAction::triggered, ReaderStatus::instance(), &ReaderStatus::updateStatus);
     connect(&setInitialPinAction, SIGNAL(triggered()), q, SLOT(slotSetInitialPin()));
 
     menu.addAction(&openCertificateManagerAction);
     menu.addAction(&configureAction);
     menu.addAction(&aboutAction);
     menu.addSeparator();
     menu.addMenu(clipboardMenu.clipboardMenu()->menu());
     menu.addSeparator();
     menu.addMenu(&cardMenu);
     cardMenu.addAction(&updateCardStatusAction);
     cardMenu.addAction(&setInitialPinAction);
     menu.addSeparator();
     menu.addAction(&quitAction);
 
     q->setContextMenu(&menu);
     clipboardMenu.setMainWindow(q->mainWindow());
 }
 
 SysTrayIcon::Private::~Private()
 {
 }
 
 SysTrayIcon::SysTrayIcon(QObject *p)
     : SystemTrayIcon(p)
     , d(new Private(this))
 {
     slotEnableDisableActions();
 }
 
 SysTrayIcon::~SysTrayIcon()
 {
 }
 
 MainWindow *SysTrayIcon::mainWindow() const
 {
     return static_cast<MainWindow *>(SystemTrayIcon::mainWindow());
 }
 
 QDialog *SysTrayIcon::attentionWindow() const
 {
     return static_cast<QDialog *>(SystemTrayIcon::attentionWindow());
 }
 
 void SysTrayIcon::doActivated()
 {
     if (const QWidget *const aw = attentionWindow())
         if (aw->isVisible()) {
             return; // ignore clicks while an attention window is open.
         }
     if (!d->firstCardWithNullPin.empty()) {
         d->slotSetInitialPin();
     } else {
         // Toggle visibility of MainWindow
         KleopatraApplication::instance()->toggleMainWindowVisibility();
     }
 }
 
 void SysTrayIcon::setFirstCardWithNullPin(const std::string &serialNumber)
 {
     if (d->firstCardWithNullPin == serialNumber) {
         return;
     }
     d->firstCardWithNullPin = serialNumber;
     slotEnableDisableActions();
 }
 
 void SysTrayIcon::slotEnableDisableActions()
 {
     d->enableDisableActions();
 }
 
 #include "moc_systrayicon.cpp"
 
 #endif // QT_NO_SYSTEMTRAYICON
diff --git a/src/utils/clipboardmenu.cpp b/src/utils/clipboardmenu.cpp
index f4a04ad4a..4bb842935 100644
--- a/src/utils/clipboardmenu.cpp
+++ b/src/utils/clipboardmenu.cpp
@@ -1,149 +1,115 @@
 /*
   SPDX-FileCopyrightText: 2014-2021 Laurent Montel <montel@kde.org>
 
   SPDX-License-Identifier: GPL-2.0-only
 */
 
 #include <config-kleopatra.h>
 
 #include "clipboardmenu.h"
 
 #include "kdtoolsglobal.h"
 #include "mainwindow.h"
 
 #include <settings.h>
 
 #include <commands/decryptverifyclipboardcommand.h>
-#include <commands/encryptclipboardcommand.h>
 #include <commands/importcertificatefromclipboardcommand.h>
-#include <commands/signclipboardcommand.h>
+#include <commands/signencryptclipboardcommand.h>
 
 #include <Libkleo/Algorithm>
 #include <Libkleo/Compat>
 #include <Libkleo/KeyCache>
 
 #include <KActionMenu>
 #include <KLocalizedString>
 
 #include <QAction>
 #include <QApplication>
 #include <QClipboard>
 #include <QSignalBlocker>
 
 #include <gpgme++/key.h>
 
 using namespace Kleo;
 
 using namespace Kleo::Commands;
 
 ClipboardMenu::ClipboardMenu(QObject *parent)
     : QObject{parent}
 {
     mClipboardMenu = new KActionMenu(i18n("Clipboard"), this);
     mImportClipboardAction = new QAction(i18nc("@action", "Certificate Import"), this);
-    mEncryptClipboardAction = new QAction(i18nc("@action", "Encrypt..."), this);
+    mSignEncryptClipboardAction = new QAction(i18nc("@action", "Sign/Encrypt..."), this);
     const Kleo::Settings settings{};
-    if (settings.cmsEnabled() && settings.cmsSigningAllowed()) {
-        mSmimeSignClipboardAction = new QAction(i18nc("@action", "S/MIME-Sign..."), this);
-        Q_SET_OBJECT_NAME(mSmimeSignClipboardAction);
-    }
-    mOpenPGPSignClipboardAction = new QAction(i18nc("@action", "OpenPGP-Sign..."), this);
     mDecryptVerifyClipboardAction = new QAction(i18nc("@action", "Decrypt/Verify..."), this);
 
     Q_SET_OBJECT_NAME(mClipboardMenu);
     Q_SET_OBJECT_NAME(mImportClipboardAction);
-    Q_SET_OBJECT_NAME(mEncryptClipboardAction);
-    Q_SET_OBJECT_NAME(mOpenPGPSignClipboardAction);
     Q_SET_OBJECT_NAME(mDecryptVerifyClipboardAction);
 
     connect(mImportClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotImportClipboard);
-    connect(mEncryptClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotEncryptClipboard);
-    if (mSmimeSignClipboardAction) {
-        connect(mSmimeSignClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotSMIMESignClipboard);
-    }
-    connect(mOpenPGPSignClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotOpenPGPSignClipboard);
+    connect(mSignEncryptClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotSignEncryptClipboard);
     connect(mDecryptVerifyClipboardAction, &QAction::triggered, this, &ClipboardMenu::slotDecryptVerifyClipboard);
     mClipboardMenu->addAction(mImportClipboardAction);
-    mClipboardMenu->addAction(mEncryptClipboardAction);
-    if (mSmimeSignClipboardAction) {
-        mClipboardMenu->addAction(mSmimeSignClipboardAction);
-    }
-    mClipboardMenu->addAction(mOpenPGPSignClipboardAction);
+    mClipboardMenu->addAction(mSignEncryptClipboardAction);
     mClipboardMenu->addAction(mDecryptVerifyClipboardAction);
-    connect(QApplication::clipboard(), &QClipboard::changed, this, &ClipboardMenu::slotEnableDisableActions);
+    connect(QApplication::clipboard(), &QClipboard::dataChanged, this, &ClipboardMenu::slotEnableDisableActions);
     connect(KeyCache::instance().get(), &KeyCache::keyListingDone, this, &ClipboardMenu::slotEnableDisableActions);
     slotEnableDisableActions();
 }
 
 ClipboardMenu::~ClipboardMenu() = default;
 
 void ClipboardMenu::setMainWindow(MainWindow *window)
 {
     mWindow = window;
 }
 
 KActionMenu *ClipboardMenu::clipboardMenu() const
 {
     return mClipboardMenu;
 }
 
 void ClipboardMenu::startCommand(Command *cmd)
 {
     Q_ASSERT(cmd);
     cmd->setParent(mWindow);
     cmd->start();
 }
 
 void ClipboardMenu::slotImportClipboard()
 {
     startCommand(new ImportCertificateFromClipboardCommand(nullptr));
 }
 
-void ClipboardMenu::slotEncryptClipboard()
-{
-    startCommand(new EncryptClipboardCommand(nullptr));
-}
-
-void ClipboardMenu::slotOpenPGPSignClipboard()
-{
-    startCommand(new SignClipboardCommand(GpgME::OpenPGP, nullptr));
-}
-
-void ClipboardMenu::slotSMIMESignClipboard()
+void ClipboardMenu::slotSignEncryptClipboard()
 {
-    startCommand(new SignClipboardCommand(GpgME::CMS, nullptr));
+    startCommand(new SignEncryptClipboardCommand(nullptr));
 }
 
 void ClipboardMenu::slotDecryptVerifyClipboard()
 {
     startCommand(new DecryptVerifyClipboardCommand(nullptr));
 }
 
-namespace
-{
-
-bool hasSigningKeys(GpgME::Protocol protocol)
-{
-    if (!KeyCache::instance()->initialized()) {
-        return false;
-    }
-    return std::ranges::any_of(KeyCache::instance()->keys(), [protocol](const auto &k) {
-        return k.hasSecret() && Kleo::keyHasSign(k) && (k.protocol() == protocol);
-    });
-}
-
-}
-
 void ClipboardMenu::slotEnableDisableActions()
 {
     const QSignalBlocker blocker(QApplication::clipboard());
-    mImportClipboardAction->setEnabled(ImportCertificateFromClipboardCommand::canImportCurrentClipboard());
-    mEncryptClipboardAction->setEnabled(EncryptClipboardCommand::canEncryptCurrentClipboard());
-    mOpenPGPSignClipboardAction->setEnabled(SignClipboardCommand::canSignCurrentClipboard() && hasSigningKeys(GpgME::OpenPGP));
-    if (mSmimeSignClipboardAction) {
-        mSmimeSignClipboardAction->setEnabled(SignClipboardCommand::canSignCurrentClipboard() && hasSigningKeys(GpgME::CMS));
+#ifdef Q_OS_UNIX
+    // We can't reliable monitor the clipboard on wayland when the app doesn't have focus, so we always enable the actions there
+    // and show an error when an action is done on invalid data
+    if (qApp->platformName() == QStringLiteral("wayland")) {
+        mImportClipboardAction->setEnabled(true);
+        mSignEncryptClipboardAction->setEnabled(true);
+        mDecryptVerifyClipboardAction->setEnabled(true);
+    } else
+#endif
+    {
+        mImportClipboardAction->setEnabled(ImportCertificateFromClipboardCommand::canImportCurrentClipboard());
+        mSignEncryptClipboardAction->setEnabled(SignEncryptClipboardCommand::canSignEncryptCurrentClipboard());
+        mDecryptVerifyClipboardAction->setEnabled(DecryptVerifyClipboardCommand::canDecryptVerifyCurrentClipboard());
     }
-    mDecryptVerifyClipboardAction->setEnabled(DecryptVerifyClipboardCommand::canDecryptVerifyCurrentClipboard());
 }
 
 #include "moc_clipboardmenu.cpp"
diff --git a/src/utils/clipboardmenu.h b/src/utils/clipboardmenu.h
index 1e0cfd51f..b8a7f1097 100644
--- a/src/utils/clipboardmenu.h
+++ b/src/utils/clipboardmenu.h
@@ -1,50 +1,46 @@
 /*
   SPDX-FileCopyrightText: 2014-2021 Laurent Montel <montel@kde.org>
 
   SPDX-License-Identifier: GPL-2.0-only
 */
 
 #pragma once
 
 #include <QObject>
 #include <QPointer>
 
 class KActionMenu;
 class QAction;
 class MainWindow;
 
 namespace Kleo
 {
 class Command;
 }
 
 class ClipboardMenu : public QObject
 {
     Q_OBJECT
 public:
     explicit ClipboardMenu(QObject *parent = nullptr);
     ~ClipboardMenu() override;
 
     void setMainWindow(MainWindow *window);
 
     KActionMenu *clipboardMenu() const;
 
 private Q_SLOTS:
     void slotImportClipboard();
-    void slotEncryptClipboard();
-    void slotOpenPGPSignClipboard();
-    void slotSMIMESignClipboard();
+    void slotSignEncryptClipboard();
     void slotDecryptVerifyClipboard();
     void slotEnableDisableActions();
 
 private:
     void startCommand(Kleo::Command *cmd);
 
     QPointer<KActionMenu> mClipboardMenu;
     QPointer<QAction> mImportClipboardAction;
-    QPointer<QAction> mEncryptClipboardAction;
-    QPointer<QAction> mSmimeSignClipboardAction;
-    QPointer<QAction> mOpenPGPSignClipboardAction;
+    QPointer<QAction> mSignEncryptClipboardAction;
     QPointer<QAction> mDecryptVerifyClipboardAction;
     QPointer<MainWindow> mWindow;
 };