Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F36623968
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
56 KB
Subscribers
None
View Options
diff --git a/src/commands/importcertificatescommand.cpp b/src/commands/importcertificatescommand.cpp
index c07d32ef5..a8bc69a62 100644
--- a/src/commands/importcertificatescommand.cpp
+++ b/src/commands/importcertificatescommand.cpp
@@ -1,676 +1,677 @@
/* -*- mode: c++; c-basic-offset:4 -*-
commands/importcertificatescommand.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007, 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 <config-kleopatra.h>
#include "importcertificatescommand.h"
#include "importcertificatescommand_p.h"
#include "certifycertificatecommand.h"
#include "kleopatra_debug.h"
+#include <Libkleo/KeyList>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/KeyCache>
#include <Libkleo/Predicates>
#include <Libkleo/Formatting>
#include <Libkleo/Stl_Util>
#include <QGpgME/KeyListJob>
#include <QGpgME/Protocol>
#include <QGpgME/ImportJob>
#include <QGpgME/ImportFromKeyserverJob>
#include <QGpgME/ChangeOwnerTrustJob>
#include <gpgme++/global.h>
#include <gpgme++/importresult.h>
#include <gpgme++/context.h>
#include <gpgme++/key.h>
#include <gpgme++/keylistresult.h>
#include <KLocalizedString>
#include <KMessageBox>
#include <QByteArray>
#include <QEventLoop>
#include <QString>
#include <QWidget>
#include <QTreeView>
#include <QTextDocument> // for Qt::escape
#include <memory>
#include <algorithm>
#include <map>
#include <set>
using namespace GpgME;
using namespace Kleo;
using namespace QGpgME;
namespace
{
make_comparator_str(ByImportFingerprint, .fingerprint());
class ImportResultProxyModel : public AbstractKeyListSortFilterProxyModel
{
Q_OBJECT
public:
ImportResultProxyModel(const std::vector<ImportResult> &results, const QStringList &ids, QObject *parent = nullptr)
: AbstractKeyListSortFilterProxyModel(parent)
{
updateFindCache(results, ids);
}
~ImportResultProxyModel() override {}
ImportResultProxyModel *clone() const override
{
// compiler-generated copy ctor is fine!
return new ImportResultProxyModel(*this);
}
void setImportResults(const std::vector<ImportResult> &results, const QStringList &ids)
{
updateFindCache(results, ids);
invalidateFilter();
}
protected:
QVariant data(const QModelIndex &index, int role) const override
{
if (!index.isValid() || role != Qt::ToolTipRole) {
return AbstractKeyListSortFilterProxyModel::data(index, role);
}
- const QString fpr = index.data(FingerprintRole).toString();
+ const QString fpr = index.data(KeyList::FingerprintRole).toString();
// find information:
const std::vector<Import>::const_iterator it
= qBinaryFind(m_importsByFingerprint.begin(), m_importsByFingerprint.end(),
fpr.toLatin1().constData(),
ByImportFingerprint<std::less>());
if (it == m_importsByFingerprint.end()) {
return AbstractKeyListSortFilterProxyModel::data(index, role);
} else {
QStringList rv;
const auto ids = m_idsByFingerprint[it->fingerprint()];
rv.reserve(ids.size());
std::copy(ids.cbegin(), ids.cend(), std::back_inserter(rv));
return Formatting::importMetaData(*it, rv);
}
}
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
{
//
// 0. Keep parents of matching children:
//
const QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
Q_ASSERT(index.isValid());
for (int i = 0, end = sourceModel()->rowCount(index); i != end; ++i)
if (filterAcceptsRow(i, index)) {
return true;
}
//
// 1. Check that this is an imported key:
//
- const QString fpr = index.data(FingerprintRole).toString();
+ const QString fpr = index.data(KeyList::FingerprintRole).toString();
return std::binary_search(m_importsByFingerprint.begin(), m_importsByFingerprint.end(),
fpr.toLatin1().constData(),
ByImportFingerprint<std::less>());
}
private:
void updateFindCache(const std::vector<ImportResult> &results, const QStringList &ids)
{
Q_ASSERT(results.size() == static_cast<unsigned>(ids.size()));
m_importsByFingerprint.clear();
m_idsByFingerprint.clear();
m_results = results;
for (unsigned int i = 0, end = results.size(); i != end; ++i) {
const std::vector<Import> imports = results[i].imports();
m_importsByFingerprint.insert(m_importsByFingerprint.end(), imports.begin(), imports.end());
const QString &id = ids[i];
for (std::vector<Import>::const_iterator it = imports.begin(), end = imports.end(); it != end; ++it) {
m_idsByFingerprint[it->fingerprint()].insert(id);
}
}
std::sort(m_importsByFingerprint.begin(), m_importsByFingerprint.end(),
ByImportFingerprint<std::less>());
}
private:
mutable std::vector<Import> m_importsByFingerprint;
mutable std::map< const char *, std::set<QString>, ByImportFingerprint<std::less> > m_idsByFingerprint;
std::vector<ImportResult> m_results;
};
}
ImportCertificatesCommand::Private::Private(ImportCertificatesCommand *qq, KeyListController *c)
: Command::Private(qq, c),
waitForMoreJobs(false),
containedExternalCMSCerts(false),
nonWorkingProtocols(),
idsByJob(),
jobs(),
results(),
ids()
{
}
ImportCertificatesCommand::Private::~Private() {}
#define d d_func()
#define q q_func()
ImportCertificatesCommand::ImportCertificatesCommand(KeyListController *p)
: Command(new Private(this, p))
{
}
ImportCertificatesCommand::ImportCertificatesCommand(QAbstractItemView *v, KeyListController *p)
: Command(v, new Private(this, p))
{
}
ImportCertificatesCommand::~ImportCertificatesCommand() {}
static QString format_ids(const QStringList &ids)
{
QStringList escapedIds;
for (const QString &id : ids) {
if (!id.isEmpty()) {
escapedIds << id.toHtmlEscaped();
}
}
return escapedIds.join(QLatin1String("<br>"));
}
static QString make_tooltip(const QStringList &ids)
{
if (ids.empty()) {
return QString();
}
if (ids.size() == 1)
if (ids.front().isEmpty()) {
return QString();
} else
return i18nc("@info:tooltip",
"Imported Certificates from %1",
ids.front().toHtmlEscaped());
else
return i18nc("@info:tooltip",
"Imported certificates from these sources:<br/>%1",
format_ids(ids));
}
void ImportCertificatesCommand::Private::setImportResultProxyModel(const std::vector<ImportResult> &results, const QStringList &ids)
{
if (std::none_of(results.cbegin(), results.cend(), std::mem_fn(&ImportResult::numConsidered))) {
return;
}
q->addTemporaryView(i18nc("@title:tab", "Imported Certificates"),
new ImportResultProxyModel(results, ids),
make_tooltip(ids));
if (QTreeView *const tv = qobject_cast<QTreeView *>(parentWidgetOrView())) {
tv->expandAll();
}
}
int sum(const std::vector<ImportResult> &res, int (ImportResult::*fun)() const)
{
return kdtools::accumulate_transform(res.begin(), res.end(), std::mem_fn(fun), 0);
}
static QString make_report(const std::vector<ImportResult> &res, const QString &id = QString())
{
const KLocalizedString normalLine = ki18n("<tr><td align=\"right\">%1</td><td>%2</td></tr>");
const KLocalizedString boldLine = ki18n("<tr><td align=\"right\"><b>%1</b></td><td>%2</td></tr>");
const KLocalizedString headerLine = ki18n("<tr><th colspan=\"2\" align=\"center\">%1</th></tr>");
#define SUM( x ) sum( res, &ImportResult::x )
QStringList lines;
if (!id.isEmpty()) {
lines.push_back(headerLine.subs(id).toString());
}
lines.push_back(normalLine.subs(i18n("Total number processed:"))
.subs(SUM(numConsidered)).toString());
lines.push_back(normalLine.subs(i18n("Imported:"))
.subs(SUM(numImported)).toString());
if (const int n = SUM(newSignatures))
lines.push_back(normalLine.subs(i18n("New signatures:"))
.subs(n).toString());
if (const int n = SUM(newUserIDs))
lines.push_back(normalLine.subs(i18n("New user IDs:"))
.subs(n).toString());
if (const int n = SUM(numKeysWithoutUserID))
lines.push_back(normalLine.subs(i18n("Certificates without user IDs:"))
.subs(n).toString());
if (const int n = SUM(newSubkeys))
lines.push_back(normalLine.subs(i18n("New subkeys:"))
.subs(n).toString());
if (const int n = SUM(newRevocations))
lines.push_back(boldLine.subs(i18n("Newly revoked:"))
.subs(n).toString());
if (const int n = SUM(notImported))
lines.push_back(boldLine.subs(i18n("Not imported:"))
.subs(n).toString());
if (const int n = SUM(numUnchanged))
lines.push_back(normalLine.subs(i18n("Unchanged:"))
.subs(n).toString());
if (const int n = SUM(numSecretKeysConsidered))
lines.push_back(normalLine.subs(i18n("Secret keys processed:"))
.subs(n).toString());
if (const int n = SUM(numSecretKeysImported))
lines.push_back(normalLine.subs(i18n("Secret keys imported:"))
.subs(n).toString());
if (const int n = SUM(numSecretKeysConsidered) - SUM(numSecretKeysImported) - SUM(numSecretKeysUnchanged))
if (n > 0)
lines.push_back(boldLine.subs(i18n("Secret keys <em>not</em> imported:"))
.subs(n).toString());
if (const int n = SUM(numSecretKeysUnchanged))
lines.push_back(normalLine.subs(i18n("Secret keys unchanged:"))
.subs(n).toString());
if (const int n = SUM(numV3KeysSkipped))
lines.push_back(normalLine.subs(i18n("Deprecated PGP-2 keys skipped:"))
.subs(n).toString());
#undef sum
return lines.join(QString());
}
static QString make_message_report(const std::vector<ImportResult> &res, const QStringList &ids)
{
Q_ASSERT(res.size() == static_cast<unsigned>(ids.size()));
if (res.empty()) {
return i18n("No imports (should not happen, please report a bug).");
}
if (res.size() == 1)
return ids.front().isEmpty()
? i18n("<qt><p>Detailed results of certificate import:</p>"
"<table width=\"100%\">%1</table></qt>", make_report(res))
: i18n("<qt><p>Detailed results of importing %1:</p>"
"<table width=\"100%\">%2</table></qt>", ids.front(), make_report(res));
return i18n("<qt><p>Detailed results of certificate import:</p>"
"<table width=\"100%\">%1</table></qt>", make_report(res, i18n("Totals")));
}
// Returns false on error, true if please certify was shown.
bool ImportCertificatesCommand::Private::showPleaseCertify(const GpgME::Import &imp)
{
const char *fpr = imp.fingerprint();
if (!fpr) {
// WTF
qCWarning(KLEOPATRA_LOG) << "Import without fingerprint";
return false;
}
// Exactly one public key imported. Let's see if it is openpgp. We are async here so
// we can just fetch it.
auto ctx = GpgME::Context::createForProtocol(GpgME::OpenPGP);
if (!ctx) {
// WTF
qCWarning(KLEOPATRA_LOG) << "Failed to create OpenPGP proto";
return false;
}
GpgME::Error err;
auto key = ctx->key(fpr, err, false);
delete ctx;
if (key.isNull() || err) {
// No such key most likely not OpenPGP
return false;
}
for (const auto &uid: key.userIDs()) {
if (uid.validity() >= GpgME::UserID::Marginal) {
// Already marginal so don't bug the user
return false;
}
}
const QStringList suggestions = QStringList() << i18n("A phone call to the person.")
<< i18n("Using a business card.")
<< i18n("Confirming it on a trusted website.");
auto sel = KMessageBox::questionYesNo(parentWidgetOrView(),
i18n("In order to mark the certificate as valid (green) it needs to be certified.") + QStringLiteral("<br>") +
i18n("Certifying means that you check the Fingerprint.") + QStringLiteral("<br>") +
i18n("Some suggestions to do this are:") +
QStringLiteral("<li><ul>%1").arg(suggestions.join(QStringLiteral("</ul><ul>"))) +
QStringLiteral("</ul></li>") +
i18n("Do you wish to start this process now?"),
i18nc("@title", "You have imported a new certificate (public key)"),
KStandardGuiItem::yes(), KStandardGuiItem::no(), QStringLiteral("CertifyQuestion"));
if (sel == KMessageBox::Yes) {
QEventLoop loop;
auto cmd = new Commands::CertifyCertificateCommand(key);
cmd->setParentWidget(parentWidgetOrView());
loop.connect(cmd, SIGNAL(finished()), SLOT(quit()));
QMetaObject::invokeMethod(cmd, &Commands::CertifyCertificateCommand::start, Qt::QueuedConnection);
loop.exec();
}
return true;
}
void ImportCertificatesCommand::Private::showDetails(QWidget *parent, const std::vector<ImportResult> &res, const QStringList &ids)
{
if (res.size() == 1 && res[0].numImported() == 1 && res[0].imports().size() == 1) {
if (showPleaseCertify(res[0].imports()[0])) {
return;
}
}
setImportResultProxyModel(res, ids);
KMessageBox::information(parent, make_message_report(res, ids), i18n("Certificate Import Result"));
}
void ImportCertificatesCommand::Private::showDetails(const std::vector<ImportResult> &res, const QStringList &ids)
{
showDetails(parentWidgetOrView(), res, ids);
}
static QString make_error_message(const Error &err, const QString &id)
{
Q_ASSERT(err);
Q_ASSERT(!err.isCanceled());
return id.isEmpty()
? i18n("<qt><p>An error occurred while trying "
"to import the certificate:</p>"
"<p><b>%1</b></p></qt>",
QString::fromLocal8Bit(err.asString()))
: i18n("<qt><p>An error occurred while trying "
"to import the certificate %1:</p>"
"<p><b>%2</b></p></qt>",
id, QString::fromLocal8Bit(err.asString()));
}
void ImportCertificatesCommand::Private::showError(QWidget *parent, const Error &err, const QString &id)
{
if (parent) {
KMessageBox::error(parent, make_error_message(err, id), i18n("Certificate Import Failed"));
} else {
showError(err, id);
}
}
void ImportCertificatesCommand::Private::showError(const Error &err, const QString &id)
{
error(make_error_message(err, id), i18n("Certificate Import Failed"));
}
void ImportCertificatesCommand::Private::setWaitForMoreJobs(bool wait)
{
if (wait == waitForMoreJobs) {
return;
}
waitForMoreJobs = wait;
tryToFinish();
}
void ImportCertificatesCommand::Private::importResult(const ImportResult &result)
{
jobs.erase(std::remove(jobs.begin(), jobs.end(), q->sender()), jobs.end());
importResult(result, idsByJob[q->sender()]);
}
void ImportCertificatesCommand::Private::importResult(const ImportResult &result, const QString &id)
{
results.push_back(result);
ids.push_back(id);
tryToFinish();
}
static void handleOwnerTrust(const std::vector<GpgME::ImportResult> &results)
{
//iterate over all imported certificates
for (const ImportResult &result : results) {
//when a new certificate got a secret key
if (result.numSecretKeysImported() >= 1) {
const char *fingerPr = result.imports()[0].fingerprint();
GpgME::Error err;
QScopedPointer<Context>
ctx(Context::createForProtocol(GpgME::Protocol::OpenPGP));
if (!ctx){
qCWarning(KLEOPATRA_LOG) << "Failed to get context";
continue;
}
const Key toTrustOwner = ctx->key(fingerPr, err , false);
if (toTrustOwner.isNull()) {
return;
}
QStringList uids;
uids.reserve(toTrustOwner.userIDs().size());
Q_FOREACH (const UserID &uid, toTrustOwner.userIDs()) {
uids << Formatting::prettyNameAndEMail(uid);
}
const QString str = xi18nc("@info",
"<title>You have imported a Secret Key.</title>"
"<para>The key has the fingerprint:<nl/>"
"<numid>%1</numid>"
"</para>"
"<para>And claims the User IDs:"
"<list><item>%2</item></list>"
"</para>"
"Is this your own key? (Set trust level to ultimate)",
QString::fromUtf8(fingerPr),
uids.join(QLatin1String("</item><item>")));
int k = KMessageBox::questionYesNo(nullptr, str, i18nc("@title:window",
"Secret key imported"));
if (k == KMessageBox::Yes) {
//To use the ChangeOwnerTrustJob over
//the CryptoBackendFactory
const QGpgME::Protocol *const backend = QGpgME::openpgp();
if (!backend){
qCWarning(KLEOPATRA_LOG) << "Failed to get CryptoBackend";
return;
}
ChangeOwnerTrustJob *const j = backend->changeOwnerTrustJob();
j->start(toTrustOwner, Key::Ultimate);
}
}
}
}
void ImportCertificatesCommand::Private::handleExternalCMSImports()
{
QStringList fingerprints;
// For external CMS Imports we have to manually do a keylist
// with validation to get the intermediate and root ca imported
// automatically if trusted-certs and extra-certs are used.
for (const ImportResult &result : qAsConst(results)) {
const auto imports = result.imports();
for (const Import &import : imports) {
if (!import.fingerprint()) {
continue;
}
fingerprints << QString::fromLatin1(import.fingerprint());
}
}
auto job = QGpgME::smime()->keyListJob(false, true, true);
// Old connect here because of Windows.
connect(job, SIGNAL(result(GpgME::KeyListResult,std::vector<GpgME::Key>,QString,GpgME::Error)),
q, SLOT(keyListDone(GpgME::KeyListResult,std::vector<GpgME::Key>,QString,GpgME::Error)));
job->start(fingerprints, false);
}
void ImportCertificatesCommand::Private::keyListDone(const GpgME::KeyListResult &,
const std::vector<GpgME::Key> &keys,
const QString &, const GpgME::Error&)
{
KeyCache::mutableInstance()->refresh(keys);
showDetails(results, ids);
auto tv = dynamic_cast<QTreeView *> (view());
if (!tv) {
qCDebug(KLEOPATRA_LOG) << "Failed to find treeview";
} else {
tv->expandAll();
}
finished();
}
void ImportCertificatesCommand::Private::tryToFinish()
{
if (waitForMoreJobs || !jobs.empty()) {
return;
}
if (std::any_of(results.cbegin(), results.cend(),
[](const GpgME::ImportResult &result) {
return result.error().code();
})) {
setImportResultProxyModel(results, ids);
if (std::all_of(results.cbegin(), results.cend(),
[](const GpgME::ImportResult &result) {
return result.error().isCanceled();
})) {
Q_EMIT q->canceled();
} else {
for (unsigned int i = 0, end = results.size(); i != end; ++i)
if (const Error err = results[i].error()) {
showError(err, ids[i]);
}
}
} else {
if (containedExternalCMSCerts) {
handleExternalCMSImports();
// We emit finished and do show details
// after the keylisting.
return;
} else {
handleOwnerTrust(results);
}
showDetails(results, ids);
}
finished();
}
static std::unique_ptr<ImportJob> get_import_job(GpgME::Protocol protocol)
{
Q_ASSERT(protocol != UnknownProtocol);
if (const auto backend = (protocol == GpgME::OpenPGP ? QGpgME::openpgp() : QGpgME::smime())) {
return std::unique_ptr<ImportJob>(backend->importJob());
} else {
return std::unique_ptr<ImportJob>();
}
}
void ImportCertificatesCommand::Private::startImport(GpgME::Protocol protocol, const QByteArray &data, const QString &id)
{
Q_ASSERT(protocol != UnknownProtocol);
if (std::find(nonWorkingProtocols.cbegin(), nonWorkingProtocols.cend(), protocol) != nonWorkingProtocols.cend()) {
return;
}
std::unique_ptr<ImportJob> job = get_import_job(protocol);
if (!job.get()) {
nonWorkingProtocols.push_back(protocol);
error(i18n("The type of this certificate (%1) is not supported by this Kleopatra installation.",
Formatting::displayName(protocol)),
i18n("Certificate Import Failed"));
importResult(ImportResult(), id);
return;
}
connect(job.get(), SIGNAL(result(GpgME::ImportResult)),
q, SLOT(importResult(GpgME::ImportResult)));
connect(job.get(), &Job::progress,
q, &Command::progress);
const GpgME::Error err = job->start(data);
if (err.code()) {
importResult(ImportResult(err), id);
} else {
jobs.push_back(job.release());
idsByJob[jobs.back()] = id;
}
}
static std::unique_ptr<ImportFromKeyserverJob> get_import_from_keyserver_job(GpgME::Protocol protocol)
{
Q_ASSERT(protocol != UnknownProtocol);
if (const auto backend = (protocol == GpgME::OpenPGP ? QGpgME::openpgp() : QGpgME::smime())) {
return std::unique_ptr<ImportFromKeyserverJob>(backend->importFromKeyserverJob());
} else {
return std::unique_ptr<ImportFromKeyserverJob>();
}
}
void ImportCertificatesCommand::Private::startImport(GpgME::Protocol protocol, const std::vector<Key> &keys, const QString &id)
{
Q_ASSERT(protocol != UnknownProtocol);
if (std::find(nonWorkingProtocols.cbegin(), nonWorkingProtocols.cend(), protocol) != nonWorkingProtocols.cend()) {
return;
}
std::unique_ptr<ImportFromKeyserverJob> job = get_import_from_keyserver_job(protocol);
if (!job.get()) {
nonWorkingProtocols.push_back(protocol);
error(i18n("The type of this certificate (%1) is not supported by this Kleopatra installation.",
Formatting::displayName(protocol)),
i18n("Certificate Import Failed"));
importResult(ImportResult(), id);
return;
}
if (protocol == GpgME::CMS) {
containedExternalCMSCerts = true;
}
connect(job.get(), SIGNAL(result(GpgME::ImportResult)),
q, SLOT(importResult(GpgME::ImportResult)));
connect(job.get(), &Job::progress,
q, &Command::progress);
const GpgME::Error err = job->start(keys);
if (err.code()) {
importResult(ImportResult(err), id);
} else {
jobs.push_back(job.release());
idsByJob[jobs.back()] = id;
}
}
void ImportCertificatesCommand::doCancel()
{
std::for_each(d->jobs.begin(), d->jobs.end(), [](Job *job) { job->slotCancel(); });
}
#undef d
#undef q
#include "moc_importcertificatescommand.cpp"
#include "importcertificatescommand.moc"
diff --git a/src/crypto/gui/certificatelineedit.cpp b/src/crypto/gui/certificatelineedit.cpp
index 2eccd2c37..218556c60 100644
--- a/src/crypto/gui/certificatelineedit.cpp
+++ b/src/crypto/gui/certificatelineedit.cpp
@@ -1,259 +1,260 @@
/* 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-License-Identifier: GPL-2.0-or-later
*/
#include "certificatelineedit.h"
#include <QCompleter>
#include <QPushButton>
#include <QAction>
#include <QSignalBlocker>
#include "kleopatra_debug.h"
#include "commands/detailscommand.h"
#include <Libkleo/KeyCache>
#include <Libkleo/KeyFilter>
+#include <Libkleo/KeyList>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/Formatting>
#include <KLocalizedString>
#include <KIconLoader>
#include <gpgme++/key.h>
#include <QGpgME/KeyForMailboxJob>
#include <QGpgME/Protocol>
using namespace Kleo;
using namespace Kleo::Dialogs;
using namespace GpgME;
Q_DECLARE_METATYPE(GpgME::Key)
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,
- Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
+ const auto key = KeyListSortFilterProxyModel::data(index, KeyList::KeyRole).value<GpgME::Key>();
Q_ASSERT(!key.isNull());
if (key.isNull()) {
return QVariant();
}
return Kleo::Formatting::iconForUid(key.userID(0));
}
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<KeyFilter>(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(KeyListModelInterface::Summary);
+ completer->setCompletionColumn(KeyList::Summary);
completer->setFilterMode(Qt::MatchContains);
completer->setCaseSensitivity(Qt::CaseInsensitive);
setCompleter(completer);
mFilterModel->setSourceModel(model);
- mFilterModel->setFilterKeyColumn(KeyListModelInterface::Summary);
+ 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
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();
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) {
- newKey = mFilterModel->data(mFilterModel->index(0, 0), KeyListModelInterface::KeyRole).value<Key>();
+ const auto index = mFilterModel->index(0, 0);
+ newKey = mFilterModel->data(index, KeyList::KeyRole).value<Key>();
mLineAction->setToolTip(Formatting::validity(newKey.userID(0)) +
QStringLiteral("<br/>Click here for details."));
/* FIXME: This needs to be solved by a multiple UID supporting model */
mLineAction->setIcon(Formatting::iconForUid(newKey.userID(0)));
} else {
mLineAction->setIcon(QIcon::fromTheme(QStringLiteral("emblem-error")));
mLineAction->setToolTip(i18n("No matching certificates found.<br/>Click here to import a certificate."));
}
}
mKey = newKey;
if (mKey.isNull()) {
setToolTip(QString());
} else {
setToolTip(Formatting::toolTip(newKey, Formatting::ToolTipOption::AllOptions));
}
Q_EMIT keyChanged();
if (mailText.isEmpty()) {
Q_EMIT wantsRemoval(this);
}
}
Key CertificateLineEdit::key() const
{
if (isEnabled()) {
return mKey;
} else {
return Key();
}
}
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<Key> 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<KeyFilter> &filter)
{
mFilter = filter;
mFilterModel->setKeyFilter(filter);
mCompleterFilterModel->setKeyFilter(mFilter);
updateKey();
}
#include "certificatelineedit.moc"
diff --git a/src/view/keytreeview.cpp b/src/view/keytreeview.cpp
index abc2c1643..d998d58e1 100644
--- a/src/view/keytreeview.cpp
+++ b/src/view/keytreeview.cpp
@@ -1,697 +1,698 @@
/* -*- mode: c++; c-basic-offset:4 -*-
view/keytreeview.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "keytreeview.h"
+#include <Libkleo/KeyList>
#include <Libkleo/KeyListModel>
#include <Libkleo/KeyListSortFilterProxyModel>
#include <Libkleo/KeyRearrangeColumnsProxyModel>
#include <Libkleo/Predicates>
#include "utils/headerview.h"
#include "utils/tags.h"
#include <Libkleo/Stl_Util>
#include <Libkleo/KeyFilter>
#include <Libkleo/KeyCache>
#include <gpgme++/key.h>
#include "kleopatra_debug.h"
#include <QTimer>
#include <QTreeView>
#include <QHeaderView>
#include <QItemSelectionModel>
#include <QItemSelection>
#include <QLayout>
#include <QList>
#include <QMenu>
#include <QAction>
#include <QEvent>
#include <QContextMenuEvent>
#include <KSharedConfig>
#include <KLocalizedString>
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
# define GPGME_HAS_REMARKS
#endif
#define TAGS_COLUMN 13
using namespace Kleo;
using namespace GpgME;
Q_DECLARE_METATYPE(GpgME::Key)
namespace
{
class TreeView : public QTreeView
{
public:
explicit TreeView(QWidget *parent = nullptr) : QTreeView(parent)
{
header()->installEventFilter(this);
}
QSize minimumSizeHint() const override
{
const QSize min = QTreeView::minimumSizeHint();
return QSize(min.width(), min.height() + 5 * fontMetrics().height());
}
protected:
bool eventFilter(QObject *watched, QEvent *event) override
{
Q_UNUSED(watched)
if (event->type() == QEvent::ContextMenu) {
QContextMenuEvent *e = static_cast<QContextMenuEvent *>(event);
if (!mHeaderPopup) {
mHeaderPopup = new QMenu(this);
mHeaderPopup->setTitle(i18n("View Columns"));
for (int i = 0; i < model()->columnCount(); ++i) {
QAction *tmp
= mHeaderPopup->addAction(model()->headerData(i, Qt::Horizontal).toString());
tmp->setData(QVariant(i));
tmp->setCheckable(true);
mColumnActions << tmp;
}
connect(mHeaderPopup, &QMenu::triggered, this, [this] (QAction *action) {
const int col = action->data().toInt();
if ((col == TAGS_COLUMN) && action->isChecked()) {
Tags::enableTags();
}
if (action->isChecked()) {
showColumn(col);
} else {
hideColumn(col);
}
KeyTreeView *tv = qobject_cast<KeyTreeView *> (parent());
if (tv) {
tv->resizeColumns();
}
});
}
for (QAction *action : qAsConst(mColumnActions)) {
const int column = action->data().toInt();
action->setChecked(!isColumnHidden(column));
}
mHeaderPopup->popup(mapToGlobal(e->pos()));
return true;
}
return false;
}
private:
QMenu *mHeaderPopup = nullptr;
QList<QAction *> mColumnActions;
};
} // anon namespace
KeyTreeView::KeyTreeView(QWidget *parent)
: QWidget(parent),
m_proxy(new KeyListSortFilterProxyModel(this)),
m_additionalProxy(nullptr),
m_view(new TreeView(this)),
m_flatModel(nullptr),
m_hierarchicalModel(nullptr),
m_stringFilter(),
m_keyFilter(),
m_isHierarchical(true)
{
init();
}
KeyTreeView::KeyTreeView(const KeyTreeView &other)
: QWidget(nullptr),
m_proxy(new KeyListSortFilterProxyModel(this)),
m_additionalProxy(other.m_additionalProxy ? other.m_additionalProxy->clone() : nullptr),
m_view(new TreeView(this)),
m_flatModel(other.m_flatModel),
m_hierarchicalModel(other.m_hierarchicalModel),
m_stringFilter(other.m_stringFilter),
m_keyFilter(other.m_keyFilter),
m_group(other.m_group),
m_isHierarchical(other.m_isHierarchical)
{
init();
setColumnSizes(other.columnSizes());
setSortColumn(other.sortColumn(), other.sortOrder());
}
KeyTreeView::KeyTreeView(const QString &text, const std::shared_ptr<KeyFilter> &kf,
AbstractKeyListSortFilterProxyModel *proxy, QWidget *parent,
const KConfigGroup &group)
: QWidget(parent),
m_proxy(new KeyListSortFilterProxyModel(this)),
m_additionalProxy(proxy),
m_view(new TreeView(this)),
m_flatModel(nullptr),
m_hierarchicalModel(nullptr),
m_stringFilter(text),
m_keyFilter(kf),
m_group(group),
m_isHierarchical(true),
m_onceResized(false)
{
init();
}
void KeyTreeView::setColumnSizes(const std::vector<int> &sizes)
{
if (sizes.empty()) {
return;
}
Q_ASSERT(m_view);
Q_ASSERT(m_view->header());
Q_ASSERT(qobject_cast<HeaderView *>(m_view->header()) == static_cast<HeaderView *>(m_view->header()));
if (HeaderView *const hv = static_cast<HeaderView *>(m_view->header())) {
hv->setSectionSizes(sizes);
}
}
void KeyTreeView::setSortColumn(int sortColumn, Qt::SortOrder sortOrder)
{
Q_ASSERT(m_view);
m_view->sortByColumn(sortColumn, sortOrder);
}
int KeyTreeView::sortColumn() const
{
Q_ASSERT(m_view);
Q_ASSERT(m_view->header());
return m_view->header()->sortIndicatorSection();
}
Qt::SortOrder KeyTreeView::sortOrder() const
{
Q_ASSERT(m_view);
Q_ASSERT(m_view->header());
return m_view->header()->sortIndicatorOrder();
}
std::vector<int> KeyTreeView::columnSizes() const
{
Q_ASSERT(m_view);
Q_ASSERT(m_view->header());
Q_ASSERT(qobject_cast<HeaderView *>(m_view->header()) == static_cast<HeaderView *>(m_view->header()));
if (HeaderView *const hv = static_cast<HeaderView *>(m_view->header())) {
return hv->sectionSizes();
} else {
return std::vector<int>();
}
}
void KeyTreeView::init()
{
KDAB_SET_OBJECT_NAME(m_proxy);
KDAB_SET_OBJECT_NAME(m_view);
if (!m_group.isValid()) {
m_group = KSharedConfig::openConfig()->group("KeyTreeView_default");
} else {
// Reopen as non const
KConfig *conf = m_group.config();
m_group = conf->group(m_group.name());
}
if (m_additionalProxy && m_additionalProxy->objectName().isEmpty()) {
KDAB_SET_OBJECT_NAME(m_additionalProxy);
}
QLayout *layout = new QVBoxLayout(this);
KDAB_SET_OBJECT_NAME(layout);
layout->setContentsMargins(0, 0, 0, 0);
layout->addWidget(m_view);
HeaderView *headerView = new HeaderView(Qt::Horizontal);
KDAB_SET_OBJECT_NAME(headerView);
headerView->installEventFilter(m_view);
headerView->setSectionsMovable(true);
m_view->setHeader(headerView);
m_view->setSelectionBehavior(QAbstractItemView::SelectRows);
m_view->setSelectionMode(QAbstractItemView::ExtendedSelection);
//m_view->setAlternatingRowColors( true );
m_view->setAllColumnsShowFocus(true);
m_view->setSortingEnabled(true);
if (model()) {
if (m_additionalProxy) {
m_additionalProxy->setSourceModel(model());
} else {
m_proxy->setSourceModel(model());
}
}
if (m_additionalProxy) {
m_proxy->setSourceModel(m_additionalProxy);
if (!m_additionalProxy->parent()) {
m_additionalProxy->setParent(this);
}
}
m_proxy->setFilterFixedString(m_stringFilter);
m_proxy->setKeyFilter(m_keyFilter);
m_proxy->setSortCaseSensitivity(Qt::CaseInsensitive);
KeyRearrangeColumnsProxyModel *rearangingModel = new KeyRearrangeColumnsProxyModel(this);
rearangingModel->setSourceModel(m_proxy);
- rearangingModel->setSourceColumns(QVector<int>() << KeyListModelInterface::PrettyName
- << KeyListModelInterface::PrettyEMail
- << KeyListModelInterface::Validity
- << KeyListModelInterface::ValidFrom
- << KeyListModelInterface::ValidUntil
- << KeyListModelInterface::TechnicalDetails
- << KeyListModelInterface::KeyID
- << KeyListModelInterface::Fingerprint
- << KeyListModelInterface::OwnerTrust
- << KeyListModelInterface::Origin
- << KeyListModelInterface::LastUpdate
- << KeyListModelInterface::Issuer
- << KeyListModelInterface::SerialNumber
+ rearangingModel->setSourceColumns(QVector<int>() << KeyList::PrettyName
+ << KeyList::PrettyEMail
+ << KeyList::Validity
+ << KeyList::ValidFrom
+ << KeyList::ValidUntil
+ << KeyList::TechnicalDetails
+ << KeyList::KeyID
+ << KeyList::Fingerprint
+ << KeyList::OwnerTrust
+ << KeyList::Origin
+ << KeyList::LastUpdate
+ << KeyList::Issuer
+ << KeyList::SerialNumber
#ifdef GPGME_HAS_REMARKS
// If a column is added before this TAGS_COLUMN define has to be updated accordingly
- << KeyListModelInterface::Remarks
+ << KeyList::Remarks
#endif
);
m_view->setModel(rearangingModel);
/* Handle expansion state */
m_expandedKeys = m_group.readEntry("Expanded", QStringList());
connect(m_view, &QTreeView::expanded, this, [this] (const QModelIndex &index) {
if (!index.isValid()) {
return;
}
- const auto &key = index.data(Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
+ const auto &key = index.data(KeyList::KeyRole).value<GpgME::Key>();
const auto fpr = QString::fromLatin1(key.primaryFingerprint());
if (m_expandedKeys.contains(fpr)) {
return;
}
m_expandedKeys << fpr;
m_group.writeEntry("Expanded", m_expandedKeys);
});
connect(m_view, &QTreeView::collapsed, this, [this] (const QModelIndex &index) {
if (!index.isValid()) {
return;
}
- const auto &key = index.data(Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
+ const auto &key = index.data(KeyList::KeyRole).value<GpgME::Key>();
m_expandedKeys.removeAll(QString::fromLatin1(key.primaryFingerprint()));
m_group.writeEntry("Expanded", m_expandedKeys);
});
connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, [this] () {
/* We use a single shot timer here to ensure that the keysMayHaveChanged
* handlers are all handled before we restore the expand state so that
* the model is already populated. */
QTimer::singleShot(0, [this] () {
restoreExpandState();
setUpTagKeys();
if (!m_onceResized) {
m_onceResized = true;
resizeColumns();
}
});
});
resizeColumns();
restoreLayout();
}
void KeyTreeView::restoreExpandState()
{
if (!KeyCache::instance()->initialized()) {
qCWarning(KLEOPATRA_LOG) << "Restore expand state before keycache available. Aborting.";
return;
}
for (const auto &fpr: qAsConst(m_expandedKeys)) {
const KeyListModelInterface *km = dynamic_cast<const KeyListModelInterface*> (m_view->model());
if (!km) {
qCWarning(KLEOPATRA_LOG) << "invalid model";
return;
}
const auto key = KeyCache::instance()->findByFingerprint(fpr.toLatin1().constData());
if (key.isNull()) {
qCDebug(KLEOPATRA_LOG) << "Cannot find:" << fpr << "anymore in cache";
m_expandedKeys.removeAll(fpr);
return;
}
const auto idx = km->index(key);
if (!idx.isValid()) {
qCDebug(KLEOPATRA_LOG) << "Cannot find:" << fpr << "anymore in model";
m_expandedKeys.removeAll(fpr);
return;
}
m_view->expand(idx);
}
}
void KeyTreeView::setUpTagKeys()
{
#ifdef GPGME_HAS_REMARKS
const auto tagKeys = Tags::tagKeys();
if (m_hierarchicalModel) {
m_hierarchicalModel->setRemarkKeys(tagKeys);
}
if (m_flatModel) {
m_flatModel->setRemarkKeys(tagKeys);
}
#endif
}
void KeyTreeView::saveLayout()
{
QHeaderView *header = m_view->header();
QVariantList columnVisibility;
QVariantList columnOrder;
QVariantList columnWidths;
const int headerCount = header->count();
columnVisibility.reserve(headerCount);
columnWidths.reserve(headerCount);
columnOrder.reserve(headerCount);
for (int i = 0; i < headerCount; ++i) {
columnVisibility << QVariant(!m_view->isColumnHidden(i));
columnWidths << QVariant(header->sectionSize(i));
columnOrder << QVariant(header->visualIndex(i));
}
m_group.writeEntry("ColumnVisibility", columnVisibility);
m_group.writeEntry("ColumnOrder", columnOrder);
m_group.writeEntry("ColumnWidths", columnWidths);
m_group.writeEntry("SortAscending", (int)header->sortIndicatorOrder());
if (header->isSortIndicatorShown()) {
m_group.writeEntry("SortColumn", header->sortIndicatorSection());
} else {
m_group.writeEntry("SortColumn", -1);
}
}
void KeyTreeView::restoreLayout()
{
QHeaderView *header = m_view->header();
QVariantList columnVisibility = m_group.readEntry("ColumnVisibility", QVariantList());
QVariantList columnOrder = m_group.readEntry("ColumnOrder", QVariantList());
QVariantList columnWidths = m_group.readEntry("ColumnWidths", QVariantList());
if (columnVisibility.isEmpty()) {
// if config is empty then use default settings
// The numbers have to be in line with the order in
// setsSourceColumns above
m_view->hideColumn(5);
for (int i = 7; i < m_view->model()->columnCount(); ++i) {
m_view->hideColumn(i);
}
if (KeyCache::instance()->initialized()) {
QTimer::singleShot(0, this, &KeyTreeView::resizeColumns);
}
} else {
for (int i = 0; i < header->count(); ++i) {
if (i >= columnOrder.size() || i >= columnWidths.size() || i >= columnVisibility.size()) {
// An additional column that was not around last time we saved.
// We default to hidden.
m_view->hideColumn(i);
continue;
}
bool visible = columnVisibility[i].toBool();
int width = columnWidths[i].toInt();
int order = columnOrder[i].toInt();
header->resizeSection(i, width ? width : 100);
header->moveSection(header->visualIndex(i), order);
if ((i == TAGS_COLUMN) && visible) {
Tags::enableTags();
}
if (!visible) {
m_view->hideColumn(i);
}
}
m_onceResized = true;
}
int sortOrder = m_group.readEntry("SortAscending", (int)Qt::AscendingOrder);
int sortColumn = m_group.readEntry("SortColumn", -1);
if (sortColumn >= 0) {
m_view->sortByColumn(sortColumn, (Qt::SortOrder)sortOrder);
}
}
KeyTreeView::~KeyTreeView()
{
saveLayout();
}
static QAbstractProxyModel *find_last_proxy(QAbstractProxyModel *pm)
{
Q_ASSERT(pm);
while (QAbstractProxyModel *const sm = qobject_cast<QAbstractProxyModel *>(pm->sourceModel())) {
pm = sm;
}
return pm;
}
void KeyTreeView::setFlatModel(AbstractKeyListModel *model)
{
if (model == m_flatModel) {
return;
}
m_flatModel = model;
if (!m_isHierarchical)
// TODO: this fails when called after setHierarchicalView( false )...
{
find_last_proxy(m_proxy)->setSourceModel(model);
}
}
void KeyTreeView::setHierarchicalModel(AbstractKeyListModel *model)
{
if (model == m_hierarchicalModel) {
return;
}
m_hierarchicalModel = model;
if (m_isHierarchical) {
find_last_proxy(m_proxy)->setSourceModel(model);
m_view->expandAll();
for (int column = 0; column < m_view->header()->count(); ++column) {
m_view->header()->resizeSection(column, qMax(m_view->header()->sectionSize(column), m_view->header()->sectionSizeHint(column)));
}
}
}
void KeyTreeView::setStringFilter(const QString &filter)
{
if (filter == m_stringFilter) {
return;
}
m_stringFilter = filter;
m_proxy->setFilterFixedString(filter);
Q_EMIT stringFilterChanged(filter);
}
void KeyTreeView::setKeyFilter(const std::shared_ptr<KeyFilter> &filter)
{
if (filter == m_keyFilter || (filter && m_keyFilter && filter->id() == m_keyFilter->id())) {
return;
}
m_keyFilter = filter;
m_proxy->setKeyFilter(filter);
Q_EMIT keyFilterChanged(filter);
}
static QItemSelection itemSelectionFromKeys(const std::vector<Key> &keys, const KeyListSortFilterProxyModel &proxy)
{
QItemSelection result;
for (const Key &key : keys) {
const QModelIndex mi = proxy.index(key);
if (mi.isValid()) {
result.merge(QItemSelection(mi, mi), QItemSelectionModel::Select);
}
}
return result;
}
void KeyTreeView::selectKeys(const std::vector<Key> &keys)
{
m_view->selectionModel()->select(itemSelectionFromKeys(keys, *m_proxy), QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
std::vector<Key> KeyTreeView::selectedKeys() const
{
return m_proxy->keys(m_view->selectionModel()->selectedRows());
}
void KeyTreeView::setHierarchicalView(bool on)
{
if (on == m_isHierarchical) {
return;
}
if (on && !hierarchicalModel()) {
qCWarning(KLEOPATRA_LOG) << "hierarchical view requested, but no hierarchical model set";
return;
}
if (!on && !flatModel()) {
qCWarning(KLEOPATRA_LOG) << "flat view requested, but no flat model set";
return;
}
const std::vector<Key> selectedKeys = m_proxy->keys(m_view->selectionModel()->selectedRows());
const Key currentKey = m_proxy->key(m_view->currentIndex());
m_isHierarchical = on;
find_last_proxy(m_proxy)->setSourceModel(model());
if (on) {
m_view->expandAll();
}
selectKeys(selectedKeys);
if (!currentKey.isNull()) {
const QModelIndex currentIndex = m_proxy->index(currentKey);
if (currentIndex.isValid()) {
m_view->selectionModel()->setCurrentIndex(m_proxy->index(currentKey), QItemSelectionModel::NoUpdate);
m_view->scrollTo(currentIndex);
}
}
Q_EMIT hierarchicalChanged(on);
}
void KeyTreeView::setKeys(const std::vector<Key> &keys)
{
std::vector<Key> sorted = keys;
_detail::sort_by_fpr(sorted);
_detail::remove_duplicates_by_fpr(sorted);
m_keys = sorted;
if (m_flatModel) {
m_flatModel->setKeys(sorted);
}
if (m_hierarchicalModel) {
m_hierarchicalModel->setKeys(sorted);
}
}
void KeyTreeView::addKeysImpl(const std::vector<Key> &keys, bool select)
{
if (keys.empty()) {
return;
}
if (m_keys.empty()) {
setKeys(keys);
return;
}
std::vector<Key> sorted = keys;
_detail::sort_by_fpr(sorted);
_detail::remove_duplicates_by_fpr(sorted);
std::vector<Key> newKeys = _detail::union_by_fpr(sorted, m_keys);
m_keys.swap(newKeys);
if (m_flatModel) {
m_flatModel->addKeys(sorted);
}
if (m_hierarchicalModel) {
m_hierarchicalModel->addKeys(sorted);
}
if (select) {
selectKeys(sorted);
}
}
void KeyTreeView::addKeysSelected(const std::vector<Key> &keys)
{
addKeysImpl(keys, true);
}
void KeyTreeView::addKeysUnselected(const std::vector<Key> &keys)
{
addKeysImpl(keys, false);
}
void KeyTreeView::removeKeys(const std::vector<Key> &keys)
{
if (keys.empty()) {
return;
}
std::vector<Key> sorted = keys;
_detail::sort_by_fpr(sorted);
_detail::remove_duplicates_by_fpr(sorted);
std::vector<Key> newKeys;
newKeys.reserve(m_keys.size());
std::set_difference(m_keys.begin(), m_keys.end(),
sorted.begin(), sorted.end(),
std::back_inserter(newKeys),
_detail::ByFingerprint<std::less>());
m_keys.swap(newKeys);
if (m_flatModel) {
std::for_each(sorted.cbegin(), sorted.cend(),
[this](const Key &key) { m_flatModel->removeKey(key); });
}
if (m_hierarchicalModel) {
std::for_each(sorted.cbegin(), sorted.cend(),
[this](const Key &key) { m_hierarchicalModel->removeKey(key); });
}
}
static const struct {
const char *signal;
const char *slot;
} connections[] = {
{
SIGNAL(stringFilterChanged(QString)),
SLOT(setStringFilter(QString))
},
{
SIGNAL(keyFilterChanged(std::shared_ptr<Kleo::KeyFilter>)),
SLOT(setKeyFilter(std::shared_ptr<Kleo::KeyFilter>))
},
};
static const unsigned int numConnections = sizeof connections / sizeof * connections;
void KeyTreeView::disconnectSearchBar(const QObject *bar)
{
for (unsigned int i = 0; i < numConnections; ++i) {
disconnect(this, connections[i].signal, bar, connections[i].slot);
disconnect(bar, connections[i].signal, this, connections[i].slot);
}
}
bool KeyTreeView::connectSearchBar(const QObject *bar)
{
for (unsigned int i = 0; i < numConnections; ++i)
if (!connect(this, connections[i].signal, bar, connections[i].slot) ||
!connect(bar, connections[i].signal, this, connections[i].slot)) {
return false;
}
return true;
}
void KeyTreeView::resizeColumns()
{
- m_view->setColumnWidth(KeyListModelInterface::PrettyName, 260);
- m_view->setColumnWidth(KeyListModelInterface::PrettyEMail, 260);
+ m_view->setColumnWidth(KeyList::PrettyName, 260);
+ m_view->setColumnWidth(KeyList::PrettyEMail, 260);
for (int i = 2; i < m_view->model()->columnCount(); ++i) {
m_view->resizeColumnToContents(i);
}
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Thu, Feb 26, 7:04 PM (22 h, 13 s)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
87/31/c9df5a8b7644190eccbc763b9ac2
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment