diff --git a/src/crypto/gui/certificatelineedit.cpp b/src/crypto/gui/certificatelineedit.cpp index 218556c60..74c7f27e7 100644 --- a/src/crypto/gui/certificatelineedit.cpp +++ b/src/crypto/gui/certificatelineedit.cpp @@ -1,260 +1,292 @@ /* crypto/gui/certificatelineedit.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-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "certificatelineedit.h" #include #include #include #include #include "kleopatra_debug.h" #include "commands/detailscommand.h" #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Dialogs; using namespace GpgME; Q_DECLARE_METATYPE(GpgME::Key) +Q_DECLARE_METATYPE(KeyGroup) static QStringList s_lookedUpKeys; namespace { class ProxyModel : public KeyListSortFilterProxyModel { Q_OBJECT public: ProxyModel(QObject *parent = nullptr) : KeyListSortFilterProxyModel(parent) { } QVariant data(const QModelIndex &index, int role) const override { if (!index.isValid()) { return QVariant(); } switch (role) { case Qt::DecorationRole: { const auto key = KeyListSortFilterProxyModel::data(index, KeyList::KeyRole).value(); - Q_ASSERT(!key.isNull()); - if (key.isNull()) { - return QVariant(); + if (!key.isNull()) { + return Kleo::Formatting::iconForUid(key.userID(0)); } - return Kleo::Formatting::iconForUid(key.userID(0)); + + const auto group = KeyListSortFilterProxyModel::data(index, KeyList::GroupRole).value(); + if (!group.isNull()) { + return QIcon::fromTheme(QStringLiteral("group")); + } + + Q_ASSERT(!key.isNull() || !group.isNull()); + return QVariant(); } default: return KeyListSortFilterProxyModel::data(index, role); } } }; } // namespace CertificateLineEdit::CertificateLineEdit(AbstractKeyListModel *model, QWidget *parent, KeyFilter *filter) : QLineEdit(parent), mFilterModel(new KeyListSortFilterProxyModel(this)), mCompleterFilterModel(new ProxyModel(this)), mFilter(std::shared_ptr(filter)), mLineAction(new QAction(this)) { setPlaceholderText(i18n("Please enter a name or email address...")); setClearButtonEnabled(true); addAction(mLineAction, QLineEdit::LeadingPosition); mCompleterFilterModel->setKeyFilter(mFilter); mCompleterFilterModel->setSourceModel(model); auto *completer = new QCompleter(this); completer->setModel(mCompleterFilterModel); completer->setCompletionColumn(KeyList::Summary); completer->setFilterMode(Qt::MatchContains); completer->setCaseSensitivity(Qt::CaseInsensitive); setCompleter(completer); mFilterModel->setSourceModel(model); mFilterModel->setFilterKeyColumn(KeyList::Summary); if (filter) { mFilterModel->setKeyFilter(mFilter); } connect(KeyCache::instance().get(), &Kleo::KeyCache::keyListingDone, this, &CertificateLineEdit::updateKey); connect(this, &QLineEdit::editingFinished, this, &CertificateLineEdit::editFinished); connect(this, &QLineEdit::textChanged, this, &CertificateLineEdit::editChanged); connect(mLineAction, &QAction::triggered, this, &CertificateLineEdit::dialogRequested); connect(this, &QLineEdit::editingFinished, this, &CertificateLineEdit::checkLocate); updateKey(); /* Take ownership of the model to prevent double deletion when the * filter models are deleted */ model->setParent(parent ? parent : this); } void CertificateLineEdit::editChanged() { updateKey(); if (!mEditStarted) { Q_EMIT editingStarted(); mEditStarted = true; } mEditFinished = false; } void CertificateLineEdit::editFinished() { mEditStarted = false; mEditFinished = true; updateKey(); } void CertificateLineEdit::checkLocate() { - if (!key().isNull()) { - // Already have a key + if (!key().isNull() || !group().isNull()) { + // Already have a key or group return; } // Only check once per mailbox const auto mailText = text(); if (s_lookedUpKeys.contains(mailText)) { return; } s_lookedUpKeys << mailText; qCDebug(KLEOPATRA_LOG) << "Lookup job for" << mailText; QGpgME::KeyForMailboxJob *job = QGpgME::openpgp()->keyForMailboxJob(); job->start(mailText); } void CertificateLineEdit::updateKey() { const auto mailText = text(); auto newKey = Key(); + auto newGroup = KeyGroup(); if (mailText.isEmpty()) { mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("resource-group-new"))); mLineAction->setToolTip(i18n("Open selection dialog.")); } else { mFilterModel->setFilterFixedString(mailText); if (mFilterModel->rowCount() > 1) { if (mEditFinished) { mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("question")).pixmap(KIconLoader::SizeSmallMedium)); mLineAction->setToolTip(i18n("Multiple certificates")); } else { mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("resource-group-new"))); mLineAction->setToolTip(i18n("Open selection dialog.")); } } else if (mFilterModel->rowCount() == 1) { const auto index = mFilterModel->index(0, 0); newKey = mFilterModel->data(index, KeyList::KeyRole).value(); - mLineAction->setToolTip(Formatting::validity(newKey.userID(0)) + - QStringLiteral("
Click here for details.")); - /* FIXME: This needs to be solved by a multiple UID supporting model */ - mLineAction->setIcon(Formatting::iconForUid(newKey.userID(0))); + newGroup = mFilterModel->data(index, KeyList::GroupRole).value(); + Q_ASSERT(!newKey.isNull() || !newGroup.isNull()); + if (!newKey.isNull()) { + /* FIXME: This needs to be solved by a multiple UID supporting model */ + mLineAction->setIcon(Formatting::iconForUid(newKey.userID(0))); + mLineAction->setToolTip(Formatting::validity(newKey.userID(0)) + + QStringLiteral("
Click here for details.")); + } else if (!newGroup.isNull()) { + mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("group"))); + mLineAction->setToolTip(QString()); + } else { + mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("emblem-error"))); + mLineAction->setToolTip(i18n("No matching certificates found.
Click here to import a certificate.")); + } } else { mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("emblem-error"))); mLineAction->setToolTip(i18n("No matching certificates found.
Click here to import a certificate.")); } } mKey = newKey; + mGroup = newGroup; - if (mKey.isNull()) { + if (!mKey.isNull()) { + setToolTip(Formatting::toolTip(mKey, Formatting::ToolTipOption::AllOptions)); + } else if (!mGroup.isNull()) { setToolTip(QString()); } else { - setToolTip(Formatting::toolTip(newKey, Formatting::ToolTipOption::AllOptions)); + setToolTip(QString()); } Q_EMIT keyChanged(); if (mailText.isEmpty()) { Q_EMIT wantsRemoval(this); } } Key CertificateLineEdit::key() const { if (isEnabled()) { return mKey; } else { return Key(); } } +KeyGroup CertificateLineEdit::group() const +{ + if (isEnabled()) { + return mGroup; + } else { + return KeyGroup(); + } +} + void CertificateLineEdit::dialogRequested() { if (!mKey.isNull()) { auto cmd = new Commands::DetailsCommand(mKey, nullptr); cmd->start(); return; } CertificateSelectionDialog *const dlg = new CertificateSelectionDialog(this); dlg->setKeyFilter(mFilter); if (dlg->exec()) { const std::vector keys = dlg->selectedCertificates(); if (!keys.size()) { return; } for (unsigned int i = 0; i < keys.size(); i++) { if (!i) { setKey(keys[i]); } else { Q_EMIT addRequested(keys[i]); } } } delete dlg; updateKey(); } void CertificateLineEdit::setKey(const Key &k) { QSignalBlocker blocky(this); qCDebug(KLEOPATRA_LOG) << "Setting Key. " << Formatting::summaryLine(k); setText(Formatting::summaryLine(k)); updateKey(); } bool CertificateLineEdit::isEmpty() const { return text().isEmpty(); } void CertificateLineEdit::setKeyFilter(const std::shared_ptr &filter) { mFilter = filter; mFilterModel->setKeyFilter(filter); mCompleterFilterModel->setKeyFilter(mFilter); updateKey(); } #include "certificatelineedit.moc" diff --git a/src/crypto/gui/certificatelineedit.h b/src/crypto/gui/certificatelineedit.h index a3ec15d13..033be1490 100644 --- a/src/crypto/gui/certificatelineedit.h +++ b/src/crypto/gui/certificatelineedit.h @@ -1,98 +1,105 @@ /* crypto/gui/certificatelineedit.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-FileCopyrightText: 2021 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef CRYPTO_GUI_CERTIFICATELINEEDIT_H #define CRYPTO_GUI_CERTIFICATELINEEDIT_H +#include + #include #include #include "dialogs/certificateselectiondialog.h" #include class QLabel; class QAction; namespace Kleo { class AbstractKeyListModel; class KeyFilter; class KeyListSortFilterProxyModel; /** Line edit and completion based Certificate Selection Widget. * * Shows the status of the selection with a status label and icon. * * The widget will use a single line HBox Layout. For larger dialog * see certificateslectiondialog. */ class CertificateLineEdit: public QLineEdit { Q_OBJECT public: /** Create the certificate selection line. * * If parent is not NULL the model is not taken * over but the parent argument used as the parent of the model. * * @param model: The keylistmodel to use. * @param parent: The usual widget parent. * @param filter: The filters to use. See certificateselectiondialog. */ CertificateLineEdit(AbstractKeyListModel *model, QWidget *parent = nullptr, KeyFilter *filter = nullptr); /** Get the selected key */ GpgME::Key key() const; + KeyGroup group() const; + /** Check if the text is empty */ bool isEmpty() const; /** Set the preselected Key for this widget. */ void setKey(const GpgME::Key &key); /** Set the used keyfilter. */ void setKeyFilter(const std::shared_ptr &filter); Q_SIGNALS: /** Emitted when the selected key changed. */ void keyChanged(); /** Emitted when the entry is empty and editing is finished. */ void wantsRemoval(CertificateLineEdit *w); /** Emitted when the entry is no longer empty. */ void editingStarted(); /** Emitted when the certselectiondialog resulted in multiple certificates. */ void addRequested(const GpgME::Key &key); private Q_SLOTS: void updateKey(); void dialogRequested(); void editChanged(); void editFinished(); void checkLocate(); private: KeyListSortFilterProxyModel *const mFilterModel; KeyListSortFilterProxyModel *const mCompleterFilterModel; QLabel *mStatusLabel, *mStatusIcon; GpgME::Key mKey; + KeyGroup mGroup; GpgME::Protocol mCurrentProto; std::shared_ptr mFilter; bool mEditStarted = false; bool mEditFinished = false; QAction *const mLineAction; }; } #endif