summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDaniel Vrátil <dvratil@kde.org>2016-08-04 14:28:25 (GMT)
committerDaniel Vrátil <dvratil@kde.org>2016-08-04 14:28:32 (GMT)
commitea628492350015675d8131c5347cdc4e1cf39d92 (patch)
treeffbcac8269dfb6ac1b9affdee89125a7221b6bb6
parent69fa56358784f3793129dc8021cd7dc8f70668a9 (diff)
Introduce KeySelectionCombo class
A generic combobox backed by KeyListModel. It supports adding custom items by application to insert actions like 'Generate keys' or 'No key' option. Differential Revision: https://phabricator.kde.org/D2324
-rw-r--r--src/CMakeLists.txt2
-rw-r--r--src/models/keycache.cpp2
-rw-r--r--src/tests/CMakeLists.txt1
-rw-r--r--src/tests/test_keyselectioncombo.cpp93
-rw-r--r--src/ui/keyselectioncombo.cpp284
-rw-r--r--src/ui/keyselectioncombo.h66
6 files changed, 447 insertions, 1 deletions
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index d5df812..931881a 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -108,6 +108,7 @@ set(libkleo_ui_SRCS # make this a separate lib.
ui/keyselectiondialog.cpp
ui/keyrequester.cpp
ui/keyapprovaldialog.cpp
+ ui/keyselectioncombo.cpp
)
ki18n_wrap_ui(libkleo_ui_common_SRCS
@@ -238,6 +239,7 @@ ecm_generate_headers(libkleo_CamelCase_ui_HEADERS
ProgressDialog
KeyApprovalDialog
KeySelectionDialog
+ KeySelectionCombo
FileNameRequester
KeyRequester
MessageBox
diff --git a/src/models/keycache.cpp b/src/models/keycache.cpp
index 5a01531..63d14be 100644
--- a/src/models/keycache.cpp
+++ b/src/models/keycache.cpp
@@ -297,9 +297,9 @@ void KeyCache::addFileSystemWatcher(const shared_ptr<FileSystemWatcher> &watcher
void KeyCache::Private::refreshJobDone(const KeyListResult &result)
{
- Q_EMIT q->keyListingDone(result);
q->enableFileSystemWatcher(true);
m_initalized = true;
+ Q_EMIT q->keyListingDone(result);
}
const Key &KeyCache::findByFingerprint(const char *fpr) const
diff --git a/src/tests/CMakeLists.txt b/src/tests/CMakeLists.txt
index 5185b76..d08ddc4 100644
--- a/src/tests/CMakeLists.txt
+++ b/src/tests/CMakeLists.txt
@@ -17,3 +17,4 @@ add_kleo_test(test_keygen.cpp)
add_kleo_test(test_keylister.cpp)
add_kleo_test(test_auditlog.cpp)
add_kleo_test(test_keyformailbox.cpp)
+add_kleo_test(test_keyselectioncombo.cpp)
diff --git a/src/tests/test_keyselectioncombo.cpp b/src/tests/test_keyselectioncombo.cpp
new file mode 100644
index 0000000..0308fea
--- /dev/null
+++ b/src/tests/test_keyselectioncombo.cpp
@@ -0,0 +1,93 @@
+/*
+ This file is part of libkleopatra's test suite.
+ Copyright (c) 2016 Klarälvdalens Datakonsult AB
+
+ Libkleopatra is free software; you can redistribute it and/or
+ modify it under the terms of the GNU General Public License,
+ version 2, as published by the Free Software Foundation.
+
+ Libkleopatra is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "ui/keyselectioncombo.h"
+#include "kleo/defaultkeyfilter.h"
+#include <gpgme++/key.h>
+
+#include <KAboutData>
+#include <QDebug>
+
+#include <vector>
+#include <QApplication>
+#include <KLocalizedString>
+#include <QCommandLineParser>
+#include <QVBoxLayout>
+
+int main(int argc, char **argv)
+{
+ QApplication app(argc, argv);
+ KAboutData aboutData(QStringLiteral("test_keyselectioncombo"), i18n("KeySelectionCombo Test"), QStringLiteral("0.1"));
+ QCommandLineParser parser;
+ QCommandLineOption openpgpOption(QStringLiteral("openpgp"), i18n("Show OpenPGP keys"));
+ parser.addOption(openpgpOption);
+ QCommandLineOption smimeOption(QStringLiteral("smime"), i18n("Show S/MIME keys"));
+ parser.addOption(smimeOption);
+ QCommandLineOption encryptOption(QStringLiteral("encryption"), i18n("Show keys for encryption"));
+ parser.addOption(encryptOption);
+ QCommandLineOption signingOption(QStringLiteral("signing"), i18n("Show keys for signing"));
+ parser.addOption(signingOption);
+
+ KAboutData::setApplicationData(aboutData);
+ parser.addVersionOption();
+ parser.addHelpOption();
+ aboutData.setupCommandLine(&parser);
+ parser.process(app);
+ aboutData.processCommandLine(&parser);
+
+ QWidget window;
+ QVBoxLayout layout(&window);
+
+ Kleo::KeySelectionCombo combo;
+ layout.addWidget(&combo);
+
+ boost::shared_ptr<const Kleo::DefaultKeyFilter> filter(new Kleo::DefaultKeyFilter);
+ filter->setCanSign(parser.isSet(signingOption) ? Kleo::DefaultKeyFilter::Set : Kleo::DefaultKeyFilter::DoesNotMatter);
+ filter->setCanEncrypt(parser.isSet(encryptOption) ? Kleo::DefaultKeyFilter::Set : Kleo::DefaultKeyFilter::DoesNotMatter);
+ filter->setIsOpenPGP(parser.isSet(openpgpOption) ? Kleo::DefaultKeyFilter::Set : Kleo::DefaultKeyFilter::NotSet);
+ filter->setHasSecret(Kleo::DefaultKeyFilter::Set);
+ //filter->setOwnerTrust(Kleo::DefaultKeyFilter::IsAtLeast);
+ //filter->setOwnerTrustReferenceLevel(GpgME::Key::Ultimate);
+ combo.setKeyFilter(filter);
+
+ combo.prependCustomItem(QIcon(), i18n("No key"), QStringLiteral("no-key"));
+ QObject::connect(&combo, &Kleo::KeySelectionCombo::currentKeyChanged,
+ [](const GpgME::Key &key) {
+ qDebug() << "Current key changed:" << key.keyID();
+ });
+ QObject::connect(&combo, &Kleo::KeySelectionCombo::customItemSelected,
+ [](const QVariant &data) {
+ qDebug() << "Custom item selected:" << data.toString();
+ });
+
+ window.show();
+
+ /*
+ if (dlg.exec() == QDialog::Accepted) {
+ qDebug() << "accepted; selected key:" << (dlg.selectedKey().userID(0).id() ? dlg.selectedKey().userID(0).id() : "<null>") << "\nselected _keys_:";
+ for (std::vector<GpgME::Key>::const_iterator it = dlg.selectedKeys().begin(); it != dlg.selectedKeys().end(); ++it) {
+ qDebug() << (it->userID(0).id() ? it->userID(0).id() : "<null>");
+ }
+ } else {
+ qDebug() << "rejected";
+ }
+ */
+
+ return app.exec();
+}
+
diff --git a/src/ui/keyselectioncombo.cpp b/src/ui/keyselectioncombo.cpp
new file mode 100644
index 0000000..7c7c4cd
--- /dev/null
+++ b/src/ui/keyselectioncombo.cpp
@@ -0,0 +1,284 @@
+/* This file is part of Kleopatra, the KDE keymanager
+ Copyright (c) 2016 Klarälvdalens Datakonsult AB
+
+ Kleopatra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Kleopatra is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#include "keyselectioncombo.h"
+#include <kleo_ui_debug.h>
+
+#include "kleo/cryptobackendfactory.h"
+#include "kleo/dn.h"
+#include "models/keylistmodel.h"
+#include "models/keylistsortfilterproxymodel.h"
+#include "models/keycache.h"
+#include "utils/formatting.h"
+#include "progressbar.h"
+
+#include <gpgme++/key.h>
+
+#include <QSortFilterProxyModel>
+#include <QVector>
+
+#include <KLocalizedString>
+#include <KMessageBox>
+
+Q_DECLARE_METATYPE(GpgME::Key)
+
+namespace
+{
+
+class ProxyModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+private:
+ struct CustomItem {
+ QIcon icon;
+ QString text;
+ QVariant data;
+ };
+public:
+ ProxyModel(QObject *parent = Q_NULLPTR)
+ : QSortFilterProxyModel(parent)
+ {
+ }
+
+ ~ProxyModel()
+ {
+ qDeleteAll(mFrontItems);
+ qDeleteAll(mBackItems);
+ }
+
+ bool isCustomItem(const int row) const
+ {
+ return row < mFrontItems.count() || row >= mFrontItems.count() + QSortFilterProxyModel::rowCount();
+ }
+
+ void prependItem(const QIcon &icon, const QString &text, const QVariant &data)
+ {
+ beginInsertRows(QModelIndex(), 0, 0);
+ mFrontItems.push_front(new CustomItem{ icon, text, data });
+ endInsertRows();
+ }
+
+ void appendItem(const QIcon &icon, const QString &text, const QVariant &data)
+ {
+ beginInsertRows(QModelIndex(), rowCount(), rowCount());
+ mBackItems.push_back(new CustomItem{ icon, text, data });
+ endInsertRows();
+ }
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE
+ {
+ return mFrontItems.count() + QSortFilterProxyModel::rowCount(parent) + mBackItems.count();
+ }
+
+ QModelIndex mapToSource(const QModelIndex &index) const Q_DECL_OVERRIDE
+ {
+ if (!isCustomItem(index.row())) {
+ const int row = index.row() - mFrontItems.count();
+ const QModelIndex idx = createIndex(row, index.column(), index.internalPointer());
+ return QSortFilterProxyModel::mapToSource(idx);
+ } else {
+ return QModelIndex();
+ }
+ }
+
+ QModelIndex mapFromSource(const QModelIndex &source_index) const Q_DECL_OVERRIDE
+ {
+ const QModelIndex idx = QSortFilterProxyModel::mapFromSource(source_index);
+ return createIndex(mFrontItems.count() + idx.row(), idx.column(), idx.internalPointer());
+ }
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE
+ {
+ if (row < 0 || row >= rowCount()) {
+ return QModelIndex();
+ }
+ if (row < mFrontItems.count()) {
+ return createIndex(row, column, mFrontItems[row]);
+ } else if (row >= mFrontItems.count() + QSortFilterProxyModel::rowCount()) {
+ return createIndex(row, column, mBackItems[row - mFrontItems.count() - QSortFilterProxyModel::rowCount()]);
+ } else {
+ const QModelIndex mi = QSortFilterProxyModel::index(row - mFrontItems.count(), column, parent);
+ return createIndex(row, column, mi.internalPointer());
+ }
+ }
+
+ Qt::ItemFlags flags(const QModelIndex &index) const Q_DECL_OVERRIDE
+ {
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
+ }
+
+ QModelIndex parent(const QModelIndex &) const Q_DECL_OVERRIDE
+ {
+ // Flat list
+ return QModelIndex();
+ }
+
+ QVariant data(const QModelIndex &index, int role) const Q_DECL_OVERRIDE
+ {
+ if (isCustomItem(index.row())) {
+ Q_ASSERT(!mFrontItems.isEmpty() || !mBackItems.isEmpty());
+ CustomItem *ci = static_cast<CustomItem*>(index.internalPointer());
+ switch (role) {
+ case Qt::DisplayRole:
+ return ci->text;
+ case Qt::DecorationRole:
+ return ci->icon;
+ case Qt::UserRole:
+ return ci->data;
+ default:
+ return QVariant();
+ }
+ }
+
+ const auto key = QSortFilterProxyModel::data(index, Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
+ Q_ASSERT(!key.isNull());
+ if (key.isNull()) {
+ return QVariant();
+ }
+
+ switch (role) {
+ case Qt::DisplayRole: {
+ const auto userID = key.userID(0);
+ QString name, email;
+
+ if (key.protocol() == GpgME::OpenPGP) {
+ name = userID.name();
+ email = userID.email();
+ } else {
+ const Kleo::DN dn(userID.id());
+ name = dn[QStringLiteral("CN")];
+ email = dn[QStringLiteral("EMAIL")];
+ }
+ return i18nc("Name <email> (type, created: date, 0xkeyID)", "%1 (%2, created: %3, %4)",
+ email.isEmpty() ? name : name.isEmpty() ? email : i18nc("Name <email>", "%1 <%2>", name, email),
+ key.protocol() == GpgME::OpenPGP ? i18n("OpenPGP") : i18n("S/MIME"),
+ Kleo::Formatting::creationDateString(key),
+ Kleo::Formatting::prettyKeyID(key.shortKeyID()));
+ }
+ case Qt::DecorationRole:
+ return Kleo::Formatting::iconForUid(key.userID(0));
+ default:
+ return QSortFilterProxyModel::data(index, role);
+ }
+
+ return QVariant();
+ }
+
+private:
+ QVector<CustomItem*> mFrontItems;
+ QVector<CustomItem*> mBackItems;
+};
+
+
+} // anonymous namespace
+
+namespace Kleo
+{
+class KeySelectionComboPrivate
+{
+public:
+ KeySelectionComboPrivate(KeySelectionCombo *parent)
+ : q(parent)
+ {
+ }
+
+ Kleo::AbstractKeyListModel *model;
+ Kleo::KeyListSortFilterProxyModel *sortFilterProxy;
+ ProxyModel *proxyModel;
+ boost::shared_ptr<Kleo::KeyCache> cache;
+
+private:
+ KeySelectionCombo * const q;
+};
+
+}
+
+using namespace Kleo;
+
+
+KeySelectionCombo::KeySelectionCombo(QWidget* parent)
+ : QComboBox(parent)
+ , d(new KeySelectionComboPrivate(this))
+{
+ setEnabled(false);
+
+ d->cache = Kleo::KeyCache::mutableInstance();
+ connect(d->cache.get(), &Kleo::KeyCache::keyListingDone,
+ this, [this]() {
+ qDebug() << "Key listing done";
+ d->model->useKeyCache(true, true);
+ setEnabled(true);
+ setCurrentIndex(0);
+ });
+ d->cache->startKeyListing();
+
+ d->model = Kleo::AbstractKeyListModel::createFlatKeyListModel(this);
+ d->sortFilterProxy = new Kleo::KeyListSortFilterProxyModel(this);
+ d->sortFilterProxy->setSourceModel(d->model);
+
+ d->proxyModel = new ProxyModel(this);
+ d->proxyModel->setSourceModel(d->sortFilterProxy);
+
+ setModel(d->proxyModel);
+ connect(this, static_cast<void(KeySelectionCombo::*)(int)>(&KeySelectionCombo::currentIndexChanged),
+ this, [this](int row) {
+ if (d->proxyModel->isCustomItem(row)) {
+ Q_EMIT customItemSelected(d->proxyModel->index(row, 0).data(Qt::UserRole));
+ } else {
+ Q_EMIT currentKeyChanged(currentKey());
+ }
+ });
+}
+
+KeySelectionCombo::~KeySelectionCombo()
+{
+ delete d;
+}
+
+void KeySelectionCombo::setKeyFilter(const boost::shared_ptr<const KeyFilter> &kf)
+{
+ d->sortFilterProxy->setKeyFilter(kf);
+}
+
+GpgME::Key Kleo::KeySelectionCombo::currentKey() const
+{
+ return currentData(Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
+}
+
+void Kleo::KeySelectionCombo::setCurrentKey(const GpgME::Key &key)
+{
+ const int idx = findData(QVariant::fromValue(key), Kleo::KeyListModelInterface::KeyRole, Qt::MatchExactly);
+ if (idx > -1) {
+ setCurrentIndex(idx);
+ }
+}
+
+void KeySelectionCombo::appendCustomItem(const QIcon &icon, const QString &text, const QVariant &data)
+{
+ d->proxyModel->appendItem(icon, text, data);
+}
+
+void KeySelectionCombo::prependCustomItem(const QIcon &icon, const QString &text, const QVariant &data)
+{
+ d->proxyModel->prependItem(icon, text, data);
+}
+
+
+
+#include "keyselectioncombo.moc"
diff --git a/src/ui/keyselectioncombo.h b/src/ui/keyselectioncombo.h
new file mode 100644
index 0000000..a40020f
--- /dev/null
+++ b/src/ui/keyselectioncombo.h
@@ -0,0 +1,66 @@
+/* This file is part of Kleopatra, the KDE keymanager
+ Copyright (c) 2016 Klarälvdalens Datakonsult AB
+
+ Kleopatra is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ Kleopatra is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+*/
+
+#ifndef KLEO_KEYSELECTIONCOMBO_H
+#define KLEO_KEYSELECTIONCOMBO_H
+
+#include <QComboBox>
+
+#include <gpgme++/global.h>
+
+#include <kleo_export.h>
+#include <libkleo/enum.h>
+
+#include <boost/shared_ptr.hpp>
+
+namespace GpgME
+{
+class Key;
+}
+
+namespace Kleo
+{
+class KeyFilter;
+class KeySelectionComboPrivate;
+
+class KLEO_EXPORT KeySelectionCombo : public QComboBox
+{
+ Q_OBJECT
+
+public:
+ explicit KeySelectionCombo(QWidget *parent = Q_NULLPTR);
+ virtual ~KeySelectionCombo();
+
+ void setKeyFilter(const boost::shared_ptr<const KeyFilter> &kf);
+
+ GpgME::Key currentKey() const;
+ void setCurrentKey(const GpgME::Key &key);
+
+ void prependCustomItem(const QIcon &icon, const QString &text, const QVariant &data);
+ void appendCustomItem(const QIcon &icon, const QString &text, const QVariant &data);
+
+Q_SIGNALS:
+ void customItemSelected(const QVariant &data);
+ void currentKeyChanged(const GpgME::Key &key);
+
+private:
+ KeySelectionComboPrivate * const d;
+};
+
+}
+#endif