diff --git a/src/dialogs/certificateselectiondialog.cpp b/src/dialogs/certificateselectiondialog.cpp index a7a3d901a..60bd8da28 100644 --- a/src/dialogs/certificateselectiondialog.cpp +++ b/src/dialogs/certificateselectiondialog.cpp @@ -1,455 +1,449 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/certificateselectiondialog.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 #include "certificateselectiondialog.h" #include #include #include #include "utils/tags.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if GPGMEPP_VERSION >= 0x10E00 // 1.14.0 # define GPGME_HAS_REMARKS #endif using namespace Kleo; using namespace Kleo::Dialogs; using namespace Kleo::Commands; using namespace GpgME; class CertificateSelectionDialog::Private { friend class ::Kleo::Dialogs::CertificateSelectionDialog; CertificateSelectionDialog *const q; public: explicit Private(CertificateSelectionDialog *qq); private: void reload() { Command *const cmd = new ReloadKeysCommand(nullptr); cmd->setParentWidget(q); cmd->start(); } void create() { NewCertificateCommand *cmd = new NewCertificateCommand(nullptr); cmd->setParentWidget(q); if ((options & AnyFormat) != AnyFormat) { cmd->setProtocol((options & OpenPGPFormat) ? OpenPGP : CMS); } cmd->start(); } void lookup() { Command *const cmd = new LookupCertificatesCommand(nullptr); cmd->setParentWidget(q); cmd->start(); } void slotKeysMayHaveChanged(); void slotCurrentViewChanged(QAbstractItemView *newView); void slotSelectionChanged(); void slotDoubleClicked(const QModelIndex &idx); private: bool acceptable(const std::vector &keys, const std::vector &groups) { return !keys.empty() || !groups.empty(); } void updateLabelText() { ui.label.setText(!customLabelText.isEmpty() ? customLabelText : (options & MultiSelection) ? i18n("Please select one or more of the following certificates:") : i18n("Please select one of the following certificates:")); } private: QPointer lastView; QString customLabelText; Options options; struct UI { QLabel label; SearchBar searchBar; TabWidget tabWidget; QDialogButtonBox buttonBox; - QVBoxLayout vlay; - - explicit UI(CertificateSelectionDialog *q) - : label(q), - searchBar(q), - tabWidget(q), - buttonBox(q), - vlay(q) - { - KDAB_SET_OBJECT_NAME(label); - KDAB_SET_OBJECT_NAME(searchBar); - KDAB_SET_OBJECT_NAME(tabWidget); - KDAB_SET_OBJECT_NAME(buttonBox); - KDAB_SET_OBJECT_NAME(vlay); - - vlay.addWidget(&label); - vlay.addWidget(&searchBar); - vlay.addWidget(&tabWidget, 1); - vlay.addWidget(&buttonBox); - - QPushButton *const ok = buttonBox.addButton(QDialogButtonBox::Ok); - ok->setEnabled(false); - QPushButton *const cancel = buttonBox.addButton(QDialogButtonBox::Close); - Q_UNUSED(cancel) - QPushButton *const reload = buttonBox.addButton(i18n("Reload"), QDialogButtonBox::ActionRole); - QPushButton *const import = buttonBox.addButton(i18n("Import..."), QDialogButtonBox::ActionRole); - QPushButton *const lookup = buttonBox.addButton(i18n("Lookup..."), QDialogButtonBox::ActionRole); - QPushButton *const create = buttonBox.addButton(i18n("New..."), QDialogButtonBox::ActionRole); - - import->setToolTip(i18nc("@info:tooltip", "Import certificate from file")); - lookup->setToolTip(i18nc("@info:tooltip", "Lookup certificates on server")); - reload->setToolTip(i18nc("@info:tooltip", "Refresh certificate list")); - create->setToolTip(i18nc("@info:tooltip", "Create a new certificate")); - - connect(&buttonBox, &QDialogButtonBox::accepted, q, &CertificateSelectionDialog::accept); - connect(&buttonBox, &QDialogButtonBox::rejected, q, &CertificateSelectionDialog::reject); - connect(reload, SIGNAL(clicked()), q, SLOT(reload())); - connect(lookup, SIGNAL(clicked()), q, SLOT(lookup())); - connect(create, SIGNAL(clicked()), q, SLOT(create())); - connect(KeyCache::instance().get(), SIGNAL(keysMayHaveChanged()), - q, SLOT(slotKeysMayHaveChanged())); - - connect(import, &QPushButton::clicked, q, [import, q] () { - import->setEnabled(false); - auto cmd = new Kleo::ImportCertificateFromFileCommand(); - connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished, - q, [import]() { - import->setEnabled(true); - }); - cmd->setParentWidget(q); - cmd->start(); - }); - } } ui; + + void setUpUI(CertificateSelectionDialog *q) + { + KDAB_SET_OBJECT_NAME(ui.label); + KDAB_SET_OBJECT_NAME(ui.searchBar); + KDAB_SET_OBJECT_NAME(ui.tabWidget); + KDAB_SET_OBJECT_NAME(ui.buttonBox); + + auto *vlay = new QVBoxLayout(q); + vlay->addWidget(&ui.label); + vlay->addWidget(&ui.searchBar); + vlay->addWidget(&ui.tabWidget, 1); + vlay->addWidget(&ui.buttonBox); + + QPushButton *const ok = ui.buttonBox.addButton(QDialogButtonBox::Ok); + ok->setEnabled(false); + QPushButton *const cancel = ui.buttonBox.addButton(QDialogButtonBox::Close); + Q_UNUSED(cancel) + QPushButton *const reload = ui.buttonBox.addButton(i18n("Reload"), QDialogButtonBox::ActionRole); + QPushButton *const import = ui.buttonBox.addButton(i18n("Import..."), QDialogButtonBox::ActionRole); + QPushButton *const lookup = ui.buttonBox.addButton(i18n("Lookup..."), QDialogButtonBox::ActionRole); + QPushButton *const create = ui.buttonBox.addButton(i18n("New..."), QDialogButtonBox::ActionRole); + + import->setToolTip(i18nc("@info:tooltip", "Import certificate from file")); + lookup->setToolTip(i18nc("@info:tooltip", "Lookup certificates on server")); + reload->setToolTip(i18nc("@info:tooltip", "Refresh certificate list")); + create->setToolTip(i18nc("@info:tooltip", "Create a new certificate")); + + connect(&ui.buttonBox, &QDialogButtonBox::accepted, q, &CertificateSelectionDialog::accept); + connect(&ui.buttonBox, &QDialogButtonBox::rejected, q, &CertificateSelectionDialog::reject); + connect(reload, SIGNAL(clicked()), q, SLOT(reload())); + connect(lookup, SIGNAL(clicked()), q, SLOT(lookup())); + connect(create, SIGNAL(clicked()), q, SLOT(create())); + connect(KeyCache::instance().get(), SIGNAL(keysMayHaveChanged()), + q, SLOT(slotKeysMayHaveChanged())); + + connect(import, &QPushButton::clicked, q, [import, q] () { + import->setEnabled(false); + auto cmd = new Kleo::ImportCertificateFromFileCommand(); + connect(cmd, &Kleo::ImportCertificateFromFileCommand::finished, + q, [import]() { + import->setEnabled(true); + }); + cmd->setParentWidget(q); + cmd->start(); + }); + } }; CertificateSelectionDialog::Private::Private(CertificateSelectionDialog *qq) - : q(qq), - ui(q) + : q(qq) { + setUpUI(q); ui.tabWidget.setFlatModel(AbstractKeyListModel::createFlatKeyListModel(q)); ui.tabWidget.setHierarchicalModel(AbstractKeyListModel::createHierarchicalKeyListModel(q)); #ifdef GPGME_HAS_REMARKS const auto tagKeys = Tags::tagKeys(); ui.tabWidget.flatModel()->setRemarkKeys(tagKeys); ui.tabWidget.hierarchicalModel()->setRemarkKeys(tagKeys); #endif ui.tabWidget.connectSearchBar(&ui.searchBar); connect(&ui.tabWidget, SIGNAL(currentViewChanged(QAbstractItemView*)), q, SLOT(slotCurrentViewChanged(QAbstractItemView*))); updateLabelText(); q->setWindowTitle(i18nc("@title:window", "Certificate Selection")); } CertificateSelectionDialog::CertificateSelectionDialog(QWidget *parent) : QDialog(parent), d(new Private(this)) { const KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kleopatracertificateselectiondialogrc")); d->ui.tabWidget.loadViews(config.data()); const KConfigGroup geometry(config, "Geometry"); resize(geometry.readEntry("size", size())); d->slotKeysMayHaveChanged(); } CertificateSelectionDialog::~CertificateSelectionDialog() {} void CertificateSelectionDialog::setCustomLabelText(const QString &txt) { if (txt == d->customLabelText) { return; } d->customLabelText = txt; d->updateLabelText(); } QString CertificateSelectionDialog::customLabelText() const { return d->customLabelText; } void CertificateSelectionDialog::setOptions(Options options) { if (d->options == options) { return; } d->options = options; d->ui.tabWidget.setMultiSelection(options & MultiSelection); d->slotKeysMayHaveChanged(); } CertificateSelectionDialog::Options CertificateSelectionDialog::options() const { return d->options; } void CertificateSelectionDialog::setStringFilter(const QString &filter) { d->ui.tabWidget.setStringFilter(filter); } void CertificateSelectionDialog::setKeyFilter(const std::shared_ptr &filter) { d->ui.tabWidget.setKeyFilter(filter); } namespace { void selectRows(const QAbstractItemView *view, const QModelIndexList &indexes) { if (!view) { return; } QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); for (const QModelIndex &idx : qAsConst(indexes)) { if (idx.isValid()) { sm->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); } } } QModelIndexList getGroupIndexes(const KeyListModelInterface *model, const std::vector &groups) { QModelIndexList indexes; indexes.reserve(groups.size()); std::transform(groups.begin(), groups.end(), std::back_inserter(indexes), [model] (const KeyGroup &group) { return model->index(group); }); indexes.erase(std::remove_if(indexes.begin(), indexes.end(), [] (const QModelIndex &index) { return !index.isValid(); }), indexes.end()); return indexes; } } void CertificateSelectionDialog::selectCertificates(const std::vector &keys) { const auto *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); selectRows(d->ui.tabWidget.currentView(), model->indexes(keys)); } void CertificateSelectionDialog::selectCertificate(const Key &key) { selectCertificates(std::vector(1, key)); } void CertificateSelectionDialog::selectGroups(const std::vector &groups) { const auto *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); selectRows(d->ui.tabWidget.currentView(), getGroupIndexes(model, groups)); } namespace { QModelIndexList getSelectedRows(const QAbstractItemView *view) { if (!view) { return {}; } const QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); return sm->selectedRows(); } std::vector getGroups(const KeyListModelInterface *model, const QModelIndexList &indexes) { std::vector groups; groups.reserve(indexes.size()); std::transform(indexes.begin(), indexes.end(), std::back_inserter(groups), [model](const QModelIndex &idx) { return model->group(idx); }); groups.erase(std::remove_if(groups.begin(), groups.end(), std::mem_fn(&Kleo::KeyGroup::isNull)), groups.end()); return groups; } } std::vector CertificateSelectionDialog::selectedCertificates() const { const KeyListModelInterface *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); return model->keys(getSelectedRows(d->ui.tabWidget.currentView())); } Key CertificateSelectionDialog::selectedCertificate() const { const std::vector keys = selectedCertificates(); return keys.empty() ? Key() : keys.front(); } std::vector CertificateSelectionDialog::selectedGroups() const { const KeyListModelInterface *const model = d->ui.tabWidget.currentModel(); Q_ASSERT(model); return getGroups(model, getSelectedRows(d->ui.tabWidget.currentView())); } void CertificateSelectionDialog::hideEvent(QHideEvent *e) { KSharedConfig::Ptr config = KSharedConfig::openConfig(QStringLiteral("kleopatracertificateselectiondialogrc")); d->ui.tabWidget.saveViews(config.data()); KConfigGroup geometry(config, "Geometry"); geometry.writeEntry("size", size()); QDialog::hideEvent(e); } void CertificateSelectionDialog::Private::slotKeysMayHaveChanged() { q->setEnabled(true); std::vector keys = (options & SecretKeys) ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys(); q->filterAllowedKeys(keys, options); const std::vector groups = (options & IncludeGroups) ? KeyCache::instance()->groups() : std::vector(); const std::vector selectedKeys = q->selectedCertificates(); const std::vector selectedGroups = q->selectedGroups(); if (AbstractKeyListModel *const model = ui.tabWidget.flatModel()) { model->setKeys(keys); model->setGroups(groups); } if (AbstractKeyListModel *const model = ui.tabWidget.hierarchicalModel()) { model->setKeys(keys); model->setGroups(groups); } q->selectCertificates(selectedKeys); q->selectGroups(selectedGroups); } void CertificateSelectionDialog::filterAllowedKeys(std::vector &keys, int options) { std::vector::iterator end = keys.end(); switch (options & AnyFormat) { case OpenPGPFormat: end = std::remove_if(keys.begin(), end, [](const Key &key) { return key.protocol() != OpenPGP; }); break; case CMSFormat: end = std::remove_if(keys.begin(), end, [](const Key &key) { return key.protocol() != CMS; }); break; default: case AnyFormat: ; } switch (options & AnyCertificate) { case SignOnly: end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.canReallySign(); }); break; case EncryptOnly: end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.canEncrypt(); }); break; default: case AnyCertificate: ; } if (options & SecretKeys) { end = std::remove_if(keys.begin(), end, [](const Key &key) { return !key.hasSecret(); }); } keys.erase(end, keys.end()); } void CertificateSelectionDialog::Private::slotCurrentViewChanged(QAbstractItemView *newView) { if (lastView) { disconnect(lastView, SIGNAL(doubleClicked(QModelIndex)), q, SLOT(slotDoubleClicked(QModelIndex))); Q_ASSERT(lastView->selectionModel()); disconnect(lastView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(slotSelectionChanged())); } lastView = newView; if (newView) { connect(newView, SIGNAL(doubleClicked(QModelIndex)), q, SLOT(slotDoubleClicked(QModelIndex))); Q_ASSERT(newView->selectionModel()); connect(newView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(slotSelectionChanged())); } slotSelectionChanged(); } void CertificateSelectionDialog::Private::slotSelectionChanged() { if (QPushButton *const pb = ui.buttonBox.button(QDialogButtonBox::Ok)) { pb->setEnabled(acceptable(q->selectedCertificates(), q->selectedGroups())); } } void CertificateSelectionDialog::Private::slotDoubleClicked(const QModelIndex &idx) { QAbstractItemView *const view = ui.tabWidget.currentView(); Q_ASSERT(view); const auto *const model = ui.tabWidget.currentModel(); Q_ASSERT(model); Q_UNUSED(model) QItemSelectionModel *const sm = view->selectionModel(); Q_ASSERT(sm); sm->select(idx, QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows); QMetaObject::invokeMethod(q, [this]() {q->accept();}, Qt::QueuedConnection); } void CertificateSelectionDialog::accept() { if (d->acceptable(selectedCertificates(), selectedGroups())) { QDialog::accept(); } } #include "moc_certificateselectiondialog.cpp"