diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 0958d74fb..0a561867d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -1,391 +1,392 @@ add_subdirectory(icons) 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) add_subdirectory(kconf_update) if(WIN32) set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_win.cpp) set(_kleopatra_extra_SRCS utils/gnupg-registry.c selftest/registrycheck.cpp utils/windowsprocessdevice.cpp utils/userinfo_win.cpp ) else() set(_kleopatra_extra_uiserver_SRCS uiserver/uiserver_unix.cpp) set(_kleopatra_extra_SRCS) endif() set(_kleopatra_uiserver_SRCS uiserver/sessiondata.cpp uiserver/uiserver.cpp ${_kleopatra_extra_uiserver_SRCS} uiserver/assuanserverconnection.cpp uiserver/echocommand.cpp uiserver/decryptverifycommandemailbase.cpp uiserver/decryptverifycommandfilesbase.cpp uiserver/signcommand.cpp uiserver/signencryptfilescommand.cpp uiserver/prepencryptcommand.cpp uiserver/prepsigncommand.cpp uiserver/encryptcommand.cpp uiserver/selectcertificatecommand.cpp uiserver/importfilescommand.cpp uiserver/createchecksumscommand.cpp uiserver/verifychecksumscommand.cpp selftest/uiservercheck.cpp ) if(ASSUAN2_FOUND) include_directories(${ASSUAN2_INCLUDES}) set(_kleopatra_uiserver_extra_libs ${ASSUAN2_LIBRARIES}) else() include_directories(${ASSUAN_INCLUDES}) if(WIN32) set(_kleopatra_uiserver_extra_libs ${ASSUAN_VANILLA_LIBRARIES}) else() set(_kleopatra_uiserver_extra_libs ${ASSUAN_PTHREAD_LIBRARIES}) endif() endif() 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() ki18n_wrap_ui(_kleopatra_uiserver_SRCS crypto/gui/signingcertificateselectionwidget.ui) if("${Gpgmepp_VERSION}" VERSION_GREATER_EQUAL "1.14.1") set(_kleopatra_deviceinfowatcher_files smartcard/deviceinfowatcher.cpp ) else() set(_kleopatra_deviceinfowatcher_files) endif() set(_kleopatra_SRCS utils/gui-helper.cpp utils/filedialog.cpp utils/kdpipeiodevice.cpp utils/headerview.cpp utils/scrollarea.cpp utils/dragqueen.cpp utils/multivalidator.cpp utils/systemtrayicon.cpp utils/hex.cpp utils/path-helper.cpp utils/input.cpp utils/output.cpp utils/validation.cpp utils/wsastarter.cpp utils/iodevicelogger.cpp utils/log.cpp utils/action_data.cpp utils/types.cpp utils/archivedefinition.cpp utils/auditlog.cpp utils/clipboardmenu.cpp utils/kuniqueservice.cpp utils/tags.cpp utils/writecertassuantransaction.cpp utils/keyparameters.cpp utils/userinfo.cpp selftest/selftest.cpp selftest/enginecheck.cpp selftest/gpgconfcheck.cpp selftest/gpgagentcheck.cpp selftest/libkleopatrarccheck.cpp ${_kleopatra_extra_SRCS} + view/htmllabel.cpp view/keylistcontroller.cpp view/keytreeview.cpp view/searchbar.cpp view/smartcardwidget.cpp view/openpgpkeycardwidget.cpp view/padwidget.cpp view/pgpcardwidget.cpp view/pivcardwidget.cpp view/p15cardwidget.cpp view/netkeywidget.cpp view/nullpinwidget.cpp view/tabwidget.cpp view/keycacheoverlay.cpp view/urllabel.cpp view/waitwidget.cpp view/welcomewidget.cpp dialogs/certificateselectiondialog.cpp dialogs/certifywidget.cpp dialogs/expirydialog.cpp dialogs/lookupcertificatesdialog.cpp dialogs/ownertrustdialog.cpp dialogs/selftestdialog.cpp dialogs/certifycertificatedialog.cpp dialogs/revokecertificationwidget.cpp dialogs/revokecertificationdialog.cpp dialogs/adduseriddialog.cpp dialogs/addemaildialog.cpp dialogs/deletecertificatesdialog.cpp dialogs/setinitialpindialog.cpp dialogs/certificatedetailsdialog.cpp dialogs/certificatedetailswidget.cpp dialogs/trustchainwidget.cpp dialogs/weboftrustwidget.cpp dialogs/weboftrustdialog.cpp dialogs/exportdialog.cpp dialogs/subkeyswidget.cpp dialogs/gencardkeydialog.cpp dialogs/updatenotification.cpp dialogs/pivcardapplicationadministrationkeyinputdialog.cpp dialogs/certificatedetailsinputwidget.cpp dialogs/createcsrforcardkeydialog.cpp dialogs/groupdetailsdialog.cpp dialogs/editgroupdialog.cpp crypto/controller.cpp crypto/certificateresolver.cpp crypto/sender.cpp crypto/recipient.cpp crypto/task.cpp crypto/taskcollection.cpp crypto/decryptverifytask.cpp crypto/decryptverifyemailcontroller.cpp crypto/decryptverifyfilescontroller.cpp crypto/autodecryptverifyfilescontroller.cpp crypto/encryptemailtask.cpp crypto/encryptemailcontroller.cpp crypto/newsignencryptemailcontroller.cpp crypto/signencrypttask.cpp crypto/signencryptfilescontroller.cpp crypto/signemailtask.cpp crypto/signemailcontroller.cpp crypto/createchecksumscontroller.cpp crypto/verifychecksumscontroller.cpp crypto/gui/wizard.cpp crypto/gui/wizardpage.cpp crypto/gui/certificateselectionline.cpp crypto/gui/certificatelineedit.cpp crypto/gui/signingcertificateselectionwidget.cpp crypto/gui/signingcertificateselectiondialog.cpp crypto/gui/resultitemwidget.cpp crypto/gui/resultlistwidget.cpp crypto/gui/resultpage.cpp crypto/gui/newresultpage.cpp crypto/gui/signencryptfileswizard.cpp crypto/gui/signencryptemailconflictdialog.cpp crypto/gui/decryptverifyoperationwidget.cpp crypto/gui/decryptverifyfileswizard.cpp crypto/gui/decryptverifyfilesdialog.cpp crypto/gui/objectspage.cpp crypto/gui/resolverecipientspage.cpp crypto/gui/signerresolvepage.cpp crypto/gui/encryptemailwizard.cpp crypto/gui/signemailwizard.cpp crypto/gui/signencryptwidget.cpp crypto/gui/signencryptwizard.cpp crypto/gui/unknownrecipientwidget.cpp crypto/gui/verifychecksumsdialog.cpp commands/command.cpp commands/gnupgprocesscommand.cpp commands/detailscommand.cpp commands/exportcertificatecommand.cpp commands/importcertificatescommand.cpp commands/importcertificatefromfilecommand.cpp commands/importcertificatefromclipboardcommand.cpp commands/importcertificatefromdatacommand.cpp commands/lookupcertificatescommand.cpp commands/reloadkeyscommand.cpp commands/refreshx509certscommand.cpp commands/refreshopenpgpcertscommand.cpp commands/deletecertificatescommand.cpp commands/decryptverifyfilescommand.cpp commands/signencryptfilescommand.cpp commands/signencryptfoldercommand.cpp commands/encryptclipboardcommand.cpp commands/signclipboardcommand.cpp commands/decryptverifyclipboardcommand.cpp commands/clearcrlcachecommand.cpp commands/dumpcrlcachecommand.cpp commands/dumpcertificatecommand.cpp commands/importcrlcommand.cpp commands/changeexpirycommand.cpp commands/changeownertrustcommand.cpp commands/changeroottrustcommand.cpp commands/changepassphrasecommand.cpp commands/certifycertificatecommand.cpp commands/revokecertificationcommand.cpp commands/selftestcommand.cpp commands/exportsecretkeycommand.cpp commands/exportopenpgpcertstoservercommand.cpp commands/adduseridcommand.cpp commands/newcertificatecommand.cpp commands/setinitialpincommand.cpp commands/learncardkeyscommand.cpp commands/checksumcreatefilescommand.cpp commands/checksumverifyfilescommand.cpp commands/exportpaperkeycommand.cpp commands/importpaperkeycommand.cpp commands/genrevokecommand.cpp commands/keytocardcommand.cpp commands/cardcommand.cpp commands/pivgeneratecardkeycommand.cpp commands/changepincommand.cpp commands/authenticatepivcardapplicationcommand.cpp commands/setpivcardapplicationadministrationkeycommand.cpp commands/certificatetopivcardcommand.cpp commands/importcertificatefrompivcardcommand.cpp commands/createopenpgpkeyfromcardkeyscommand.cpp commands/createcsrforcardkeycommand.cpp ${_kleopatra_uiserver_files} conf/configuredialog.cpp conf/groupsconfigdialog.cpp conf/groupsconfigpage.cpp conf/groupsconfigwidget.cpp newcertificatewizard/listwidget.cpp newcertificatewizard/newcertificatewizard.cpp smartcard/readerstatus.cpp smartcard/card.cpp smartcard/openpgpcard.cpp smartcard/netkeycard.cpp smartcard/pivcard.cpp smartcard/p15card.cpp smartcard/keypairinfo.cpp smartcard/utils.cpp ${_kleopatra_deviceinfowatcher_files} accessibility/accessiblerichtextlabel.cpp accessibility/accessiblewidgetfactory.cpp aboutdata.cpp systrayicon.cpp kleopatraapplication.cpp mainwindow.cpp main.cpp kleopatra.qrc ) if(WIN32) configure_file (versioninfo.rc.in versioninfo.rc) set(_kleopatra_SRCS ${CMAKE_CURRENT_BINARY_DIR}/versioninfo.rc ${_kleopatra_SRCS}) endif() set (_kleopatra_SRCS conf/kleopageconfigdialog.cpp ${_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/lookupcertificatesdialog.ui dialogs/ownertrustdialog.ui dialogs/selectchecklevelwidget.ui dialogs/selftestdialog.ui dialogs/adduseriddialog.ui dialogs/setinitialpindialog.ui dialogs/trustchainwidget.ui dialogs/subkeyswidget.ui newcertificatewizard/listwidget.ui newcertificatewizard/chooseprotocolpage.ui newcertificatewizard/enterdetailspage.ui newcertificatewizard/keycreationpage.ui newcertificatewizard/resultpage.ui newcertificatewizard/advancedsettingsdialog.ui ) kconfig_add_kcfg_files(_kleopatra_SRCS kcfg/tooltippreferences.kcfgc kcfg/emailoperationspreferences.kcfgc kcfg/fileoperationspreferences.kcfgc kcfg/smimevalidationpreferences.kcfgc kcfg/tagspreferences.kcfgc kcfg/settings.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 QGpgme ${_kleopatra_extra_libs} KF5::Libkleo KF5::Mime KF5::I18n KF5::XmlGui KF5::IconThemes KF5::WindowSystem KF5::CoreAddons KF5::ItemModels KF5::Crash Qt::Network Qt::PrintSupport # Printing secret keys ${_kleopatra_uiserver_extra_libs} ${_kleopatra_dbusaddons_libs} kleopatraclientcore ${_kleopatra_platform_libs} ) 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_signencryptfiles.desktop data/kleopatra_signencryptfolders.desktop data/kleopatra_decryptverifyfiles.desktop data/kleopatra_decryptverifyfolders.desktop DESTINATION ${KDE_INSTALL_KSERVICES5DIR}/ServiceMenus ) diff --git a/src/accessibility/accessiblewidgetfactory.cpp b/src/accessibility/accessiblewidgetfactory.cpp index b2517430d..4959216a9 100644 --- a/src/accessibility/accessiblewidgetfactory.cpp +++ b/src/accessibility/accessiblewidgetfactory.cpp @@ -1,28 +1,31 @@ /* accessibility/accessiblewidgetfactory.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "accessiblewidgetfactory.h" #include "accessiblerichtextlabel_p.h" +#include "view/htmllabel.h" #include "view/urllabel.h" QAccessibleInterface *Kleo::accessibleWidgetFactory(const QString &classname, QObject *object) { QAccessibleInterface *iface = nullptr; if (!object || !object->isWidgetType()) return iface; QWidget *widget = static_cast(object); - if (classname == QString::fromLatin1(Kleo::UrlLabel::staticMetaObject.className())) { + + if (classname == QString::fromLatin1(Kleo::HtmlLabel::staticMetaObject.className()) + || classname == QString::fromLatin1(Kleo::UrlLabel::staticMetaObject.className())) { iface = new AccessibleRichTextLabel{widget}; } return iface; } diff --git a/src/crypto/gui/resultitemwidget.cpp b/src/crypto/gui/resultitemwidget.cpp index 57798aba1..9b608b479 100644 --- a/src/crypto/gui/resultitemwidget.cpp +++ b/src/crypto/gui/resultitemwidget.cpp @@ -1,354 +1,351 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/resultitemwidget.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 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 #include "resultitemwidget.h" #include "utils/auditlog.h" #include "commands/command.h" #include "commands/importcertificatefromfilecommand.h" #include "commands/lookupcertificatescommand.h" #include "crypto/decryptverifytask.h" +#include "view/htmllabel.h" #include "view/urllabel.h" #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; namespace { // TODO move out of here static QColor colorForVisualCode(Task::Result::VisualCode code) { switch (code) { case Task::Result::AllGood: return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::PositiveBackground).color(); case Task::Result::NeutralError: case Task::Result::Warning: return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NormalBackground).color(); case Task::Result::Danger: return KColorScheme(QPalette::Active, KColorScheme::View).background(KColorScheme::NegativeBackground).color(); case Task::Result::NeutralSuccess: default: return QColor(0x00, 0x80, 0xFF); // light blue } } static QColor txtColorForVisualCode(Task::Result::VisualCode code) { switch (code) { case Task::Result::AllGood: return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::PositiveText).color(); case Task::Result::NeutralError: case Task::Result::Warning: return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NormalText).color(); case Task::Result::Danger: return KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color(); case Task::Result::NeutralSuccess: default: return QColor(0xFF, 0xFF, 0xFF); // white } } } class ResultItemWidget::Private { ResultItemWidget *const q; public: explicit Private(const std::shared_ptr &result, ResultItemWidget *qq) : q(qq), m_result(result) { Q_ASSERT(m_result); } void slotLinkActivated(const QString &); void updateShowDetailsLabel(); void addKeyImportButton(QBoxLayout *lay, bool search); void addIgnoreMDCButton(QBoxLayout *lay); void oneImportFinished(); const std::shared_ptr m_result; - QLabel *m_detailsLabel = nullptr; UrlLabel *m_auditLogLabel = nullptr; QPushButton *m_closeButton = nullptr; bool m_importCanceled = false; }; void ResultItemWidget::Private::oneImportFinished() { if (m_importCanceled) { return; } if (m_result->parentTask()) { m_result->parentTask()->start(); } q->setVisible(false); } void ResultItemWidget::Private::addIgnoreMDCButton(QBoxLayout *lay) { if (!m_result || !lay) { return; } const auto dvResult = dynamic_cast(m_result.get()); if (!dvResult) { return; } const auto decResult = dvResult->decryptionResult(); if (decResult.isNull() || !decResult.error() || !decResult.isLegacyCipherNoMDC()) { return; } auto btn = new QPushButton(i18n("Force decryption")); btn->setFixedSize(btn->sizeHint()); connect (btn, &QPushButton::clicked, q, [this] () { if (m_result->parentTask()) { const auto dvTask = dynamic_cast(m_result->parentTask().data()); dvTask->setIgnoreMDCError(true); dvTask->start(); q->setVisible(false); } else { qCWarning(KLEOPATRA_LOG) << "Failed to get parent task"; } }); lay->addWidget(btn); } void ResultItemWidget::Private::addKeyImportButton(QBoxLayout *lay, bool search) { if (!m_result || !lay) { return; } const auto dvResult = dynamic_cast(m_result.get()); if (!dvResult) { return; } const auto verifyResult = dvResult->verificationResult(); if (verifyResult.isNull()) { return; } for (const auto &sig: verifyResult.signatures()) { if (!(sig.summary() & GpgME::Signature::KeyMissing)) { continue; } auto btn = new QPushButton; QString suffix; const auto keyid = QLatin1String(sig.fingerprint()); if (verifyResult.numSignatures() > 1) { suffix = QLatin1Char(' ') + keyid; } btn = new QPushButton(search ? i18nc("1 is optional keyid. No space is intended as it can be empty.", "Search%1", suffix) : i18nc("1 is optional keyid. No space is intended as it can be empty.", "Import%1", suffix)); if (search) { btn->setIcon(QIcon::fromTheme(QStringLiteral("edit-find"))); connect (btn, &QPushButton::clicked, q, [this, btn, keyid] () { btn->setEnabled(false); m_importCanceled = false; auto cmd = new Kleo::Commands::LookupCertificatesCommand(keyid, nullptr); connect(cmd, &Kleo::Commands::LookupCertificatesCommand::canceled, q, [this]() { m_importCanceled = true; }); connect(cmd, &Kleo::Commands::LookupCertificatesCommand::finished, q, [this, btn]() { btn->setEnabled(true); oneImportFinished(); }); cmd->setParentWidget(q); cmd->start(); }); } else { btn->setIcon(QIcon::fromTheme(QStringLiteral("view-certificate-import"))); connect (btn, &QPushButton::clicked, q, [this, btn] () { btn->setEnabled(false); m_importCanceled = false; auto cmd = new Kleo::ImportCertificateFromFileCommand(); connect(cmd, &Kleo::ImportCertificateFromFileCommand::canceled, q, [this]() { m_importCanceled = true; }); connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished, q, [this, btn]() { btn->setEnabled(true); oneImportFinished(); }); cmd->setParentWidget(q); cmd->start(); }); } btn->setFixedSize(btn->sizeHint()); lay->addWidget(btn); } } static QUrl auditlog_url_template() { QUrl url(QStringLiteral("kleoresultitem://showauditlog")); return url; } void ResultItemWidget::Private::updateShowDetailsLabel() { const auto auditLogUrl = m_result->auditLog().asUrl(auditlog_url_template()); const auto auditLogLinkText = m_result->hasError() ? i18n("Diagnostics") : i18nc("The Audit Log is a detailed error log from the gnupg backend", "Show Audit Log"); m_auditLogLabel->setUrl(auditLogUrl, auditLogLinkText); m_auditLogLabel->setVisible(!auditLogUrl.isEmpty()); } ResultItemWidget::ResultItemWidget(const std::shared_ptr &result, QWidget *parent, Qt::WindowFlags flags) : QWidget(parent, flags), d(new Private(result, this)) { const QColor color = colorForVisualCode(d->m_result->code()); const QColor txtColor = txtColorForVisualCode(d->m_result->code()); + const QColor linkColor = txtColor; const QString styleSheet = QStringLiteral("QFrame,QLabel { background-color: %1; margin: 0px; }" "QFrame#resultFrame{ border-color: %2; border-style: solid; border-radius: 3px; border-width: 1px }" "QLabel { color: %3; padding: 5px; border-radius: 3px }").arg(color.name()).arg(color.darker(150).name()).arg(txtColor.name()); auto topLayout = new QVBoxLayout(this); auto frame = new QFrame; frame->setObjectName(QStringLiteral("resultFrame")); frame->setStyleSheet(styleSheet); topLayout->addWidget(frame); auto layout = new QHBoxLayout(frame); auto vlay = new QVBoxLayout(); - auto overview = new QLabel; + auto overview = new HtmlLabel; overview->setWordWrap(true); - overview->setTextFormat(Qt::RichText); - overview->setText(d->m_result->overview()); - overview->setFocusPolicy(Qt::StrongFocus); + overview->setHtml(d->m_result->overview()); overview->setStyleSheet(styleSheet); + overview->setLinkColor(linkColor); setFocusPolicy(overview->focusPolicy()); setFocusProxy(overview); - connect(overview, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); + connect(overview, &QLabel::linkActivated, + this, [this](const auto &link) { d->slotLinkActivated(link); }); vlay->addWidget(overview); layout->addLayout(vlay); - const QString details = d->m_result->details(); - auto actionLayout = new QVBoxLayout; layout->addLayout(actionLayout); d->addKeyImportButton(actionLayout, false); // TODO: Only show if auto-key-retrieve is not set. d->addKeyImportButton(actionLayout, true); d->addIgnoreMDCButton(actionLayout); d->m_auditLogLabel = new UrlLabel; - connect(d->m_auditLogLabel, &UrlLabel::linkActivated, + connect(d->m_auditLogLabel, &QLabel::linkActivated, this, [this](const auto &link) { d->slotLinkActivated(link); }); actionLayout->addWidget(d->m_auditLogLabel); d->m_auditLogLabel->setStyleSheet(styleSheet); - d->m_auditLogLabel->setLinkColor(txtColor); - - d->m_detailsLabel = new QLabel; - d->m_detailsLabel->setWordWrap(true); - d->m_detailsLabel->setTextFormat(Qt::RichText); - d->m_detailsLabel->setText(details); - d->m_detailsLabel->setFocusPolicy(Qt::StrongFocus); - d->m_detailsLabel->setTextInteractionFlags(Qt::TextBrowserInteraction); - d->m_detailsLabel->setStyleSheet(styleSheet); - - connect(d->m_detailsLabel, SIGNAL(linkActivated(QString)), this, SLOT(slotLinkActivated(QString))); - vlay->addWidget(d->m_detailsLabel); + d->m_auditLogLabel->setLinkColor(linkColor); + + auto detailsLabel = new HtmlLabel; + detailsLabel->setWordWrap(true); + detailsLabel->setHtml(d->m_result->details()); + detailsLabel->setStyleSheet(styleSheet); + detailsLabel->setLinkColor(linkColor); + connect(detailsLabel, &QLabel::linkActivated, + this, [this](const auto &link) { d->slotLinkActivated(link); }); + vlay->addWidget(detailsLabel); d->m_closeButton = new QPushButton; KGuiItem::assign(d->m_closeButton, KStandardGuiItem::close()); d->m_closeButton->setFixedSize(d->m_closeButton->sizeHint()); connect(d->m_closeButton, &QAbstractButton::clicked, this, &ResultItemWidget::closeButtonClicked); actionLayout->addWidget(d->m_closeButton); d->m_closeButton->setVisible(false); layout->setStretch(0, 1); actionLayout->addStretch(-1); vlay->addStretch(-1); d->updateShowDetailsLabel(); setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Maximum); } ResultItemWidget::~ResultItemWidget() { } void ResultItemWidget::showCloseButton(bool show) { d->m_closeButton->setVisible(show); } bool ResultItemWidget::hasErrorResult() const { return d->m_result->hasError(); } void ResultItemWidget::Private::slotLinkActivated(const QString &link) { Q_ASSERT(m_result); qCDebug(KLEOPATRA_LOG) << "Link activated: " << link; if (link.startsWith(QLatin1String("key:"))) { auto split = link.split(QLatin1Char(':')); auto fpr = split.value(1); if (split.size() == 2 && isFingerprint(fpr)) { /* There might be a security consideration here if somehow * a short keyid is used in a link and it collides with another. * So we additionally check that it really is a fingerprint. */ auto cmd = Command::commandForQuery(fpr); cmd->setParentWId(q->effectiveWinId()); cmd->start(); } else { qCWarning(KLEOPATRA_LOG) << "key link invalid " << link; } return; } const QUrl url(link); if (url.host() == QLatin1String("showauditlog")) { q->showAuditLog(); return; } qCWarning(KLEOPATRA_LOG) << "Unexpected link scheme: " << link; } void ResultItemWidget::showAuditLog() { MessageBox::auditLog(parentWidget(), d->m_result->auditLog().text()); } #include "moc_resultitemwidget.cpp" diff --git a/src/view/htmllabel.cpp b/src/view/htmllabel.cpp new file mode 100644 index 000000000..a79fe4a87 --- /dev/null +++ b/src/view/htmllabel.cpp @@ -0,0 +1,85 @@ +/* + view/htmllabel.cpp + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#include + +#include "htmllabel.h" + +#include + +using namespace Kleo; + +class HtmlLabel::Private +{ + HtmlLabel *q; +public: + Private(HtmlLabel *q) : q{q} {} + + void updateText(const QString &newText = {}); + + QColor linkColor; +}; + +void HtmlLabel::Private::updateText(const QString &newText) +{ + static const QString styleTemplate{QLatin1String{""}}; + + if (newText.isEmpty() && q->text().isEmpty()) { + return; + } + + const auto styleTag = styleTemplate.arg(linkColor.isValid() ? linkColor.name() : q->palette().link().color().name()); + if (newText.isEmpty()) { + q->setText(styleTag + q->text().mid(styleTag.size())); + } else { + q->setText(styleTag + newText); + } +} + +HtmlLabel::HtmlLabel(QWidget *parent) + : HtmlLabel{{}, parent} +{ +} + +HtmlLabel::HtmlLabel(const QString &html, QWidget *parent) + : QLabel{parent} + , d{new Private{this}} +{ + setTextFormat(Qt::RichText); + setTextInteractionFlags(Qt::TextBrowserInteraction); + setHtml(html); +} + +HtmlLabel::~HtmlLabel() = default; + +void HtmlLabel::setHtml(const QString &html) +{ + if (html.isEmpty()) { + clear(); + return; + } + d->updateText(html); +} + +void HtmlLabel::setLinkColor(const QColor &color) +{ + d->linkColor = color; + d->updateText(); +} + +bool HtmlLabel::focusNextPrevChild(bool next) +{ + const bool result = QLabel::focusNextPrevChild(next); + if (hasFocus() && hasSelectedText()) { + QAccessibleTextSelectionEvent ev(this, selectionStart(), selectionStart() + selectedText().size()); + QAccessible::updateAccessibility(&ev); + } + return result; +} diff --git a/src/view/htmllabel.h b/src/view/htmllabel.h new file mode 100644 index 000000000..92dc1e985 --- /dev/null +++ b/src/view/htmllabel.h @@ -0,0 +1,43 @@ +/* + view/htmllabel.h + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +#include + +namespace Kleo +{ + +class HtmlLabel : public QLabel +{ + Q_OBJECT +public: + explicit HtmlLabel(QWidget *parent = nullptr); + explicit HtmlLabel(const QString &html, QWidget *parent = nullptr); + ~HtmlLabel() override; + + void setHtml(const QString &html); + + void setLinkColor(const QColor &color); + +protected: + bool focusNextPrevChild(bool next) override; + +private: + using QLabel::setText; + +private: + class Private; + std::unique_ptr d; +}; + +}