Page MenuHome GnuPG

No OneTemporary

diff --git a/src/view/keylistcontroller.cpp b/src/view/keylistcontroller.cpp
index b16984ed4..bfa3c4fdb 100644
--- a/src/view/keylistcontroller.cpp
+++ b/src/view/keylistcontroller.cpp
@@ -1,862 +1,853 @@
/* -*- mode: c++; c-basic-offset:4 -*-
controllers/keylistcontroller.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2022 Felix Tiede
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "keylistcontroller.h"
#include "tabwidget.h"
#include <smartcard/readerstatus.h>
#include <utils/action_data.h>
#include <settings.h>
#include "tooltippreferences.h"
#include "kleopatra_debug.h"
#include "commands/exportcertificatecommand.h"
#include "commands/exportopenpgpcertstoservercommand.h"
#ifdef MAILAKONADI_ENABLED
#include "commands/exportopenpgpcerttoprovidercommand.h"
#endif // MAILAKONADI_ENABLED
#ifdef QGPGME_SUPPORTS_SECRET_KEY_EXPORT
# include "commands/exportsecretkeycommand.h"
#else
# include "commands/exportsecretkeycommand_old.h"
#endif
#include "commands/importcertificatefromfilecommand.h"
#include "commands/changepassphrasecommand.h"
#include "commands/lookupcertificatescommand.h"
#include "commands/reloadkeyscommand.h"
#include "commands/refreshx509certscommand.h"
#include "commands/refreshopenpgpcertscommand.h"
#include "commands/detailscommand.h"
#include "commands/deletecertificatescommand.h"
#include "commands/decryptverifyfilescommand.h"
#include "commands/signencryptfilescommand.h"
#include "commands/signencryptfoldercommand.h"
#include "commands/clearcrlcachecommand.h"
#include "commands/dumpcrlcachecommand.h"
#include "commands/dumpcertificatecommand.h"
#include "commands/importcrlcommand.h"
#include "commands/changeexpirycommand.h"
#include "commands/changeownertrustcommand.h"
#include "commands/changeroottrustcommand.h"
#include "commands/certifycertificatecommand.h"
#include "commands/revokecertificationcommand.h"
#include "commands/adduseridcommand.h"
#include "commands/newcertificatecommand.h"
#include "commands/checksumverifyfilescommand.h"
#include "commands/checksumcreatefilescommand.h"
#include "commands/exportpaperkeycommand.h"
#include "commands/revokekeycommand.h"
#include <Libkleo/KeyCache>
#include <Libkleo/KeyListModel>
#include <Libkleo/Formatting>
#include <gpgme++/key.h>
#include <KActionCollection>
#include <KLocalizedString>
#include <QAbstractItemView>
#include <QPointer>
#include <QItemSelectionModel>
#include <QAction>
#include <algorithm>
#include <iterator>
// needed for GPGME_VERSION_NUMBER
#include <gpgme.h>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace Kleo::SmartCard;
using namespace GpgME;
#ifndef QGPGME_SUPPORTS_SECRET_KEY_EXPORT
using Kleo::Commands::Compat::ExportSecretKeyCommand;
#endif
class KeyListController::Private
{
friend class ::Kleo::KeyListController;
KeyListController *const q;
public:
explicit Private(KeyListController *qq);
~Private();
void connectView(QAbstractItemView *view);
void connectCommand(Command *cmd);
void connectTabWidget();
void disconnectTabWidget();
void addCommand(Command *cmd)
{
connectCommand(cmd);
commands.insert(std::lower_bound(commands.begin(), commands.end(), cmd), cmd);
}
void addView(QAbstractItemView *view)
{
connectView(view);
views.insert(std::lower_bound(views.begin(), views.end(), view), view);
}
void removeView(QAbstractItemView *view)
{
view->disconnect(q);
view->selectionModel()->disconnect(q);
views.erase(std::remove(views.begin(), views.end(), view), views.end());
}
public:
void slotDestroyed(QObject *o)
{
qCDebug(KLEOPATRA_LOG) << (void *)o;
views.erase(std::remove(views.begin(), views.end(), o), views.end());
commands.erase(std::remove(commands.begin(), commands.end(), o), commands.end());
}
void slotDoubleClicked(const QModelIndex &idx);
void slotActivated(const QModelIndex &idx);
void slotSelectionChanged(const QItemSelection &old, const QItemSelection &new_);
void slotContextMenu(const QPoint &pos);
void slotCommandFinished();
void slotAddKey(const Key &key);
void slotAboutToRemoveKey(const Key &key);
void slotProgress(const QString &what, int current, int total)
{
Q_EMIT q->progress(current, total);
if (!what.isEmpty()) {
Q_EMIT q->message(what);
}
}
- void slotActionTriggered();
+ void slotActionTriggered(QAction *action);
void slotCurrentViewChanged(QAbstractItemView *view)
{
if (view && !std::binary_search(views.cbegin(), views.cend(), view)) {
qCDebug(KLEOPATRA_LOG) << "you need to register view" << view << "before trying to set it as the current view!";
addView(view);
}
currentView = view;
q->enableDisableActions(view ? view->selectionModel() : nullptr);
}
private:
int toolTipOptions() const;
private:
static Command::Restrictions calculateRestrictionsMask(const QItemSelectionModel *sm);
private:
struct action_item {
QPointer<QAction> action;
Command::Restrictions restrictions;
Command *(*createCommand)(QAbstractItemView *, KeyListController *);
};
std::vector<action_item> actions;
std::vector<QAbstractItemView *> views;
std::vector<Command *> commands;
QPointer<QWidget> parentWidget;
QPointer<TabWidget> tabWidget;
QPointer<QAbstractItemView> currentView;
QPointer<AbstractKeyListModel> flatModel, hierarchicalModel;
+ std::vector<QMetaObject::Connection> m_connections;
};
KeyListController::Private::Private(KeyListController *qq)
: q(qq),
actions(),
views(),
commands(),
parentWidget(),
tabWidget(),
flatModel(),
hierarchicalModel()
{
connect(KeyCache::instance().get(), &KeyCache::added, q, [this](const GpgME::Key &key) { slotAddKey(key); });
connect(KeyCache::instance().get(), &KeyCache::aboutToRemove, q, [this](const GpgME::Key &key) { slotAboutToRemoveKey(key); });
}
KeyListController::Private::~Private() {}
KeyListController::KeyListController(QObject *p)
: QObject(p), d(new Private(this))
{
}
KeyListController::~KeyListController() {}
void KeyListController::Private::slotAddKey(const Key &key)
{
// ### make model act on keycache directly...
if (flatModel) {
flatModel->addKey(key);
}
if (hierarchicalModel) {
hierarchicalModel->addKey(key);
}
}
void KeyListController::Private::slotAboutToRemoveKey(const Key &key)
{
// ### make model act on keycache directly...
if (flatModel) {
flatModel->removeKey(key);
}
if (hierarchicalModel) {
hierarchicalModel->removeKey(key);
}
}
void KeyListController::addView(QAbstractItemView *view)
{
if (!view || std::binary_search(d->views.cbegin(), d->views.cend(), view)) {
return;
}
d->addView(view);
}
void KeyListController::removeView(QAbstractItemView *view)
{
if (!view || !std::binary_search(d->views.cbegin(), d->views.cend(), view)) {
return;
}
d->removeView(view);
}
void KeyListController::setCurrentView(QAbstractItemView *view)
{
d->slotCurrentViewChanged(view);
}
std::vector<QAbstractItemView *> KeyListController::views() const
{
return d->views;
}
void KeyListController::setFlatModel(AbstractKeyListModel *model)
{
if (model == d->flatModel) {
return;
}
d->flatModel = model;
if (model) {
model->clear();
if (KeyCache::instance()->initialized()) {
model->addKeys(KeyCache::instance()->keys());
}
model->setToolTipOptions(d->toolTipOptions());
}
}
void KeyListController::setHierarchicalModel(AbstractKeyListModel *model)
{
if (model == d->hierarchicalModel) {
return;
}
d->hierarchicalModel = model;
if (model) {
model->clear();
if (KeyCache::instance()->initialized()) {
model->addKeys(KeyCache::instance()->keys());
}
model->setToolTipOptions(d->toolTipOptions());
}
}
void KeyListController::setTabWidget(TabWidget *tabWidget)
{
if (tabWidget == d->tabWidget) {
return;
}
d->disconnectTabWidget();
d->tabWidget = tabWidget;
d->connectTabWidget();
d->slotCurrentViewChanged(tabWidget ? tabWidget->currentView() : nullptr);
}
void KeyListController::setParentWidget(QWidget *parent)
{
d->parentWidget = parent;
}
QWidget *KeyListController::parentWidget() const
{
return d->parentWidget;
}
-static const struct {
- const char *signal;
- const char *slot;
-} tabs2controller[] = {
- { SIGNAL(viewAdded(QAbstractItemView*)), SLOT(addView(QAbstractItemView*)) },
- { SIGNAL(viewAboutToBeRemoved(QAbstractItemView*)), SLOT(removeView(QAbstractItemView*)) },
- { SIGNAL(currentViewChanged(QAbstractItemView*)), SLOT(slotCurrentViewChanged(QAbstractItemView*)) },
-};
-static const unsigned int numTabs2Controller = sizeof tabs2controller / sizeof * tabs2controller;
-
void KeyListController::Private::connectTabWidget()
{
if (!tabWidget) {
return;
}
const auto views = tabWidget->views();
std::for_each(views.cbegin(), views.cend(),
[this](QAbstractItemView *view) { addView(view); });
- for (unsigned int i = 0; i < numTabs2Controller; ++i) {
- connect(tabWidget, tabs2controller[i].signal, q, tabs2controller[i].slot);
- }
+
+ m_connections.reserve(3);
+ m_connections.push_back(connect(tabWidget, &TabWidget::viewAdded, q, [this](QAbstractItemView *view) { addView(view);}));
+ m_connections.push_back(connect(tabWidget, &TabWidget::viewAboutToBeRemoved, q, [this](QAbstractItemView *view) { removeView(view); }));
+ m_connections.push_back(connect(tabWidget, &TabWidget::currentViewChanged, q, [this](QAbstractItemView *view) { slotCurrentViewChanged(view); }));
}
void KeyListController::Private::disconnectTabWidget()
{
if (!tabWidget) {
return;
}
- for (unsigned int i = 0; i < numTabs2Controller; ++i) {
- disconnect(tabWidget, tabs2controller[i].signal, q, tabs2controller[i].slot);
+ for (const auto &connection : m_connections) {
+ disconnect(connection);
}
+ m_connections.clear();
+
const auto views = tabWidget->views();
std::for_each(views.cbegin(), views.cend(),
[this](QAbstractItemView *view) { removeView(view); });
}
AbstractKeyListModel *KeyListController::flatModel() const
{
return d->flatModel;
}
AbstractKeyListModel *KeyListController::hierarchicalModel() const
{
return d->hierarchicalModel;
}
QAbstractItemView *KeyListController::currentView() const
{
return d->currentView;
}
TabWidget *KeyListController::tabWidget() const
{
return d->tabWidget;
}
void KeyListController::createActions(KActionCollection *coll)
{
const std::vector<action_data> common_and_openpgp_action_data = {
// File menu
{
"file_new_certificate", i18n("New Key Pair..."), QString(),
"view-certificate-add", nullptr, nullptr, QStringLiteral("Ctrl+N")
},
{
"file_export_certificates", i18n("Export..."), i18n("Export the selected certificate (public key) to a file"),
"view-certificate-export", nullptr, nullptr, QStringLiteral("Ctrl+E")
},
{
"file_export_certificates_to_server", i18n("Publish on Server..."), i18n("Publish the selected certificate (public key) on a public keyserver"),
"view-certificate-export-server", nullptr, nullptr, QStringLiteral("Ctrl+Shift+E")
},
#ifdef MAILAKONADI_ENABLED
{
"file_export_certificate_to_provider", i18n("Publish at Mail Provider..."), i18n("Publish the selected certificate (public key) at mail provider's Web Key Directory if offered"),
"view-certificate-export", nullptr, nullptr, QString()
},
#endif // MAILAKONADI_ENABLED
{
"file_export_secret_keys", i18n("Backup Secret Keys..."), QString(),
"view-certificate-export-secret", nullptr, nullptr, QString()
},
{
"file_export_paper_key", i18n("Print Secret Key..."), QString(),
"document-print", nullptr, nullptr, QString()
},
{
"file_lookup_certificates", i18n("Lookup on Server..."), i18n("Search for certificates online using a public keyserver"),
"edit-find", nullptr, nullptr, QStringLiteral("Shift+Ctrl+I")
},
{
"file_import_certificates", i18n("Import..."), i18n("Import a certificate from a file"),
"view-certificate-import", nullptr, nullptr, QStringLiteral("Ctrl+I")
},
{
"file_decrypt_verify_files", i18n("Decrypt/Verify..."), i18n("Decrypt and/or verify files"),
"document-edit-decrypt-verify", nullptr, nullptr, QString()
},
{
"file_sign_encrypt_files", i18n("Sign/Encrypt..."), i18n("Encrypt and/or sign files"),
"document-edit-sign-encrypt", nullptr, nullptr, QString()
},
{
"file_sign_encrypt_folder", i18n("Sign/Encrypt Folder..."), i18n("Encrypt and/or sign folders"),
nullptr/*"folder-edit-sign-encrypt"*/, nullptr, nullptr, QString()
},
{
"file_checksum_create_files", i18n("Create Checksum Files..."), QString(),
nullptr/*"document-checksum-create"*/, nullptr, nullptr, QString()
},
{
"file_checksum_verify_files", i18n("Verify Checksum Files..."), QString(),
nullptr/*"document-checksum-verify"*/, nullptr, nullptr, QString()
},
// View menu
{
"view_redisplay", i18n("Redisplay"), QString(),
"view-refresh", nullptr, nullptr, QStringLiteral("F5")
},
{
"view_stop_operations", i18n("Stop Operation"), QString(),
"process-stop", this, [this](bool) { cancelCommands(); }, QStringLiteral("Escape"), RegularQAction, Disabled
},
{
"view_certificate_details", i18n("Details"), QString(),
"dialog-information", nullptr, nullptr, QString()
},
// Certificate menu
#ifdef QGPGME_SUPPORTS_KEY_REVOCATION
{
"certificates_revoke", i18n("Revoke Certificate..."), i18n("Revoke the selected OpenPGP certificate"),
"view-certificate-revoke", nullptr, nullptr, {}
},
#endif
{
"certificates_delete", i18n("Delete"), i18n("Delete selected certificates"),
"edit-delete", nullptr, nullptr, QStringLiteral("Delete")
},
{
"certificates_certify_certificate", i18n("Certify..."), i18n("Certify the validity of the selected certificate"),
"view-certificate-sign", nullptr, nullptr, QString()
},
{
"certificates_revoke_certification", i18n("Revoke Certification..."), i18n("Revoke the certification of the selected certificate"),
"view-certificate-revoke", nullptr, nullptr, QString()
},
{
"certificates_change_expiry", i18n("Change Expiry Date..."), QString(),
nullptr, nullptr, nullptr, QString()
},
{
"certificates_change_owner_trust", i18n("Change Certification Trust..."), QString(),
nullptr, nullptr, nullptr, QString()
},
{
"certificates_change_passphrase", i18n("Change Passphrase..."), QString(),
nullptr, nullptr, nullptr, QString()
},
{
"certificates_add_userid", i18n("Add User ID..."), QString(),
nullptr, nullptr, nullptr, QString()
},
// Tools menu
{
"tools_refresh_openpgp_certificates", i18n("Refresh OpenPGP Certificates"), QString(),
"view-refresh", nullptr, nullptr, QString()
},
// Window menu
// (come from TabWidget)
// Help menu
// (come from MainWindow)
};
static const std::vector<action_data> cms_action_data = {
// Certificate menu
{
"certificates_trust_root", i18n("Trust Root Certificate"), QString(),
nullptr, nullptr, nullptr, QString()
},
{
"certificates_distrust_root", i18n("Distrust Root Certificate"), QString(),
nullptr, nullptr, nullptr, QString()
},
{
"certificates_dump_certificate", i18n("Technical Details"), QString(),
nullptr, nullptr, nullptr, QString()
},
// Tools menu
{
"tools_refresh_x509_certificates", i18n("Refresh S/MIME Certificates"), QString(),
"view-refresh", nullptr, nullptr, QString()
},
{
"crl_clear_crl_cache", i18n("Clear CRL Cache"), QString(),
nullptr, nullptr, nullptr, QString()
},
{
"crl_dump_crl_cache", i18n("Dump CRL Cache"), QString(),
nullptr, nullptr, nullptr, QString()
},
{
"crl_import_crl", i18n("Import CRL From File..."), QString(),
nullptr, nullptr, nullptr, QString()
},
};
std::vector<action_data> action_data = common_and_openpgp_action_data;
if (Settings{}.cmsEnabled()) {
action_data.reserve(action_data.size() + cms_action_data.size());
std::copy(std::begin(cms_action_data), std::end(cms_action_data),
std::back_inserter(action_data));
}
make_actions_from_data(action_data, coll);
if (QAction *action = coll->action(QStringLiteral("view_stop_operations"))) {
connect(this, &KeyListController::commandsExecuting, action, &QAction::setEnabled);
}
// ### somehow make this better...
registerActionForCommand<NewCertificateCommand>(coll->action(QStringLiteral("file_new_certificate")));
//---
registerActionForCommand<LookupCertificatesCommand>(coll->action(QStringLiteral("file_lookup_certificates")));
registerActionForCommand<ImportCertificateFromFileCommand>(coll->action(QStringLiteral("file_import_certificates")));
//---
registerActionForCommand<ExportCertificateCommand>(coll->action(QStringLiteral("file_export_certificates")));
registerActionForCommand<ExportSecretKeyCommand>(coll->action(QStringLiteral("file_export_secret_keys")));
registerActionForCommand<ExportPaperKeyCommand>(coll->action(QStringLiteral("file_export_paper_key")));
registerActionForCommand<ExportOpenPGPCertsToServerCommand>(coll->action(QStringLiteral("file_export_certificates_to_server")));
#ifdef MAILAKONADI_ENABLED
registerActionForCommand<ExportOpenPGPCertToProviderCommand>(coll->action(QStringLiteral("file_export_certificate_to_provider")));
#endif // MAILAKONADI_ENABLED
//---
registerActionForCommand<DecryptVerifyFilesCommand>(coll->action(QStringLiteral("file_decrypt_verify_files")));
registerActionForCommand<SignEncryptFilesCommand>(coll->action(QStringLiteral("file_sign_encrypt_files")));
registerActionForCommand<SignEncryptFolderCommand>(coll->action(QStringLiteral("file_sign_encrypt_folder")));
//---
registerActionForCommand<ChecksumCreateFilesCommand>(coll->action(QStringLiteral("file_checksum_create_files")));
registerActionForCommand<ChecksumVerifyFilesCommand>(coll->action(QStringLiteral("file_checksum_verify_files")));
registerActionForCommand<ReloadKeysCommand>(coll->action(QStringLiteral("view_redisplay")));
//coll->action( "view_stop_operations" ) <-- already dealt with in make_actions_from_data()
registerActionForCommand<DetailsCommand>(coll->action(QStringLiteral("view_certificate_details")));
registerActionForCommand<ChangeOwnerTrustCommand>(coll->action(QStringLiteral("certificates_change_owner_trust")));
registerActionForCommand<TrustRootCommand>(coll->action(QStringLiteral("certificates_trust_root")));
registerActionForCommand<DistrustRootCommand>(coll->action(QStringLiteral("certificates_distrust_root")));
//---
registerActionForCommand<CertifyCertificateCommand>(coll->action(QStringLiteral("certificates_certify_certificate")));
if (RevokeCertificationCommand::isSupported()) {
registerActionForCommand<RevokeCertificationCommand>(coll->action(QStringLiteral("certificates_revoke_certification")));
}
//---
registerActionForCommand<ChangeExpiryCommand>(coll->action(QStringLiteral("certificates_change_expiry")));
registerActionForCommand<ChangePassphraseCommand>(coll->action(QStringLiteral("certificates_change_passphrase")));
registerActionForCommand<AddUserIDCommand>(coll->action(QStringLiteral("certificates_add_userid")));
//---
#ifdef QGPGME_SUPPORTS_KEY_REVOCATION
registerActionForCommand<RevokeKeyCommand>(coll->action(QStringLiteral("certificates_revoke")));
#endif
registerActionForCommand<DeleteCertificatesCommand>(coll->action(QStringLiteral("certificates_delete")));
//---
registerActionForCommand<DumpCertificateCommand>(coll->action(QStringLiteral("certificates_dump_certificate")));
registerActionForCommand<RefreshX509CertsCommand>(coll->action(QStringLiteral("tools_refresh_x509_certificates")));
registerActionForCommand<RefreshOpenPGPCertsCommand>(coll->action(QStringLiteral("tools_refresh_openpgp_certificates")));
//---
registerActionForCommand<ImportCrlCommand>(coll->action(QStringLiteral("crl_import_crl")));
//---
registerActionForCommand<ClearCrlCacheCommand>(coll->action(QStringLiteral("crl_clear_crl_cache")));
registerActionForCommand<DumpCrlCacheCommand>(coll->action(QStringLiteral("crl_dump_crl_cache")));
enableDisableActions(nullptr);
}
void KeyListController::registerAction(QAction *action, Command::Restrictions restrictions, Command * (*create)(QAbstractItemView *, KeyListController *))
{
if (!action) {
return;
}
Q_ASSERT(!action->isCheckable()); // can be added later, for now, disallow
const Private::action_item ai = {
action, restrictions, create
};
- connect(action, SIGNAL(triggered()), this, SLOT(slotActionTriggered()));
+ connect(action, &QAction::triggered, this, [this, action]() { d->slotActionTriggered(action); });
d->actions.push_back(ai);
}
void KeyListController::registerCommand(Command *cmd)
{
if (!cmd || std::binary_search(d->commands.cbegin(), d->commands.cend(), cmd)) {
return;
}
d->addCommand(cmd);
qCDebug(KLEOPATRA_LOG) << (void *)cmd;
if (d->commands.size() == 1) {
Q_EMIT commandsExecuting(true);
}
}
bool KeyListController::hasRunningCommands() const
{
return !d->commands.empty();
}
bool KeyListController::shutdownWarningRequired() const
{
return std::any_of(d->commands.cbegin(), d->commands.cend(), std::mem_fn(&Command::warnWhenRunningAtShutdown));
}
// slot
void KeyListController::cancelCommands()
{
std::for_each(d->commands.begin(), d->commands.end(), std::mem_fn(&Command::cancel));
}
void KeyListController::Private::connectView(QAbstractItemView *view)
{
connect(view, &QObject::destroyed, q, [this](QObject *obj) { slotDestroyed(obj); });
connect(view, &QAbstractItemView::doubleClicked, q, [this](const QModelIndex &index) { slotDoubleClicked(index); });
connect(view, &QAbstractItemView::activated, q, [this](const QModelIndex &index) { slotActivated(index); });
connect(view->selectionModel(), &QItemSelectionModel::selectionChanged,
q, [this](const QItemSelection &oldSel, const QItemSelection &newSel) {
slotSelectionChanged(oldSel, newSel);
});
view->setContextMenuPolicy(Qt::CustomContextMenu);
connect(view, &QWidget::customContextMenuRequested, q, [this](const QPoint &pos) { slotContextMenu(pos); });
}
void KeyListController::Private::connectCommand(Command *cmd)
{
if (!cmd) {
return;
}
connect(cmd, &QObject::destroyed, q, [this](QObject *obj) { slotDestroyed(obj); });
connect(cmd, &Command::finished, q, [this] { slotCommandFinished(); });
//connect( cmd, SIGNAL(canceled()), q, SLOT(slotCommandCanceled()) );
connect(cmd, &Command::info, q, &KeyListController::message);
connect(cmd, &Command::progress, q, [this](const QString &message, int current, int total) { slotProgress(message, current, total); });
}
void KeyListController::Private::slotDoubleClicked(const QModelIndex &idx)
{
QAbstractItemView *const view = qobject_cast<QAbstractItemView *>(q->sender());
if (!view || !std::binary_search(views.cbegin(), views.cend(), view)) {
return;
}
DetailsCommand *const c = new DetailsCommand(view, q);
if (parentWidget) {
c->setParentWidget(parentWidget);
}
c->setIndex(idx);
c->start();
}
void KeyListController::Private::slotActivated(const QModelIndex &idx)
{
Q_UNUSED(idx)
QAbstractItemView *const view = qobject_cast<QAbstractItemView *>(q->sender());
if (!view || !std::binary_search(views.cbegin(), views.cend(), view)) {
return;
}
}
void KeyListController::Private::slotSelectionChanged(const QItemSelection &old, const QItemSelection &new_)
{
Q_UNUSED(old)
Q_UNUSED(new_)
const QItemSelectionModel *const sm = qobject_cast<QItemSelectionModel *>(q->sender());
if (!sm) {
return;
}
q->enableDisableActions(sm);
}
void KeyListController::Private::slotContextMenu(const QPoint &p)
{
QAbstractItemView *const view = qobject_cast<QAbstractItemView *>(q->sender());
if (view && std::binary_search(views.cbegin(), views.cend(), view)) {
Q_EMIT q->contextMenuRequested(view, view->viewport()->mapToGlobal(p));
} else {
qCDebug(KLEOPATRA_LOG) << "sender is not a QAbstractItemView*!";
}
}
void KeyListController::Private::slotCommandFinished()
{
Command *const cmd = qobject_cast<Command *>(q->sender());
if (!cmd || !std::binary_search(commands.cbegin(), commands.cend(), cmd)) {
return;
}
qCDebug(KLEOPATRA_LOG) << (void *)cmd;
if (commands.size() == 1) {
Q_EMIT q->commandsExecuting(false);
}
}
void KeyListController::enableDisableActions(const QItemSelectionModel *sm) const
{
const Command::Restrictions restrictionsMask = d->calculateRestrictionsMask(sm);
for (const Private::action_item &ai : std::as_const(d->actions))
if (ai.action) {
ai.action->setEnabled(ai.restrictions == (ai.restrictions & restrictionsMask));
}
}
static bool all_secret_are_not_owner_trust_ultimate(const std::vector<Key> &keys)
{
for (const Key &key : keys)
if (key.hasSecret() && key.ownerTrust() == Key::Ultimate) {
return false;
}
return true;
}
Command::Restrictions find_root_restrictions(const std::vector<Key> &keys)
{
bool trusted = false, untrusted = false;
for (const Key &key : keys)
if (key.isRoot())
if (key.userID(0).validity() == UserID::Ultimate) {
trusted = true;
} else {
untrusted = true;
}
else {
return Command::NoRestriction;
}
if (trusted)
if (untrusted) {
return Command::NoRestriction;
} else {
return Command::MustBeTrustedRoot;
}
else if (untrusted) {
return Command::MustBeUntrustedRoot;
} else {
return Command::NoRestriction;
}
}
Command::Restrictions KeyListController::Private::calculateRestrictionsMask(const QItemSelectionModel *sm)
{
if (!sm) {
return Command::NoRestriction;
}
const KeyListModelInterface *const m = dynamic_cast<const KeyListModelInterface *>(sm->model());
if (!m) {
return Command::NoRestriction;
}
const std::vector<Key> keys = m->keys(sm->selectedRows());
if (keys.empty()) {
return Command::NoRestriction;
}
Command::Restrictions result = Command::NeedSelection;
if (keys.size() == 1) {
result |= Command::OnlyOneKey;
}
#if GPGME_VERSION_NUMBER >= 0x011102 // 1.17.2
// we need to check the primary subkey because Key::hasSecret() is also true if just the secret key stub of an offline key is available
const auto primaryKeyCanBeUsedForSecretKeyOperations = [](const auto &k) { return k.subkey(0).isSecret(); };
#else
// older versions of GpgME did not always set the secret flag for card keys
const auto primaryKeyCanBeUsedForSecretKeyOperations = [](const auto &k) { return k.subkey(0).isSecret() || k.subkey(0).isCardKey(); };
#endif
if (std::all_of(keys.cbegin(), keys.cend(), primaryKeyCanBeUsedForSecretKeyOperations)) {
result |= Command::NeedSecretKey;
}
if (std::all_of(std::begin(keys), std::end(keys), [](const auto &k) { return k.subkey(0).isSecret() && !k.subkey(0).isCardKey(); })) {
result |= Command::NeedSecretKeyData;
}
if (std::all_of(keys.cbegin(), keys.cend(), [](const Key &key) { return key.protocol() == OpenPGP; })) {
result |= Command::MustBeOpenPGP;
} else if (std::all_of(keys.cbegin(), keys.cend(), [](const Key &key) { return key.protocol() == CMS; })) {
result |= Command::MustBeCMS;
}
if (all_secret_are_not_owner_trust_ultimate(keys)) {
result |= Command::MayOnlyBeSecretKeyIfOwnerTrustIsNotYetUltimate;
}
result |= find_root_restrictions(keys);
if (const ReaderStatus *rs = ReaderStatus::instance()) {
if (!rs->firstCardWithNullPin().empty()) {
result |= Command::AnyCardHasNullPin;
}
if (rs->anyCardCanLearnKeys()) {
result |= Command::AnyCardCanLearnKeys;
}
}
return result;
}
-void KeyListController::Private::slotActionTriggered()
-{
- if (const QObject *const s = q->sender()) {
- const auto it = std::find_if(actions.cbegin(), actions.cend(),
- [this](const action_item &item) { return item.action == q->sender(); });
- if (it != actions.end())
- if (Command *const c = it->createCommand(this->currentView, q)) {
- if (parentWidget) {
- c->setParentWidget(parentWidget);
- }
- c->start();
- } else
- qCDebug(KLEOPATRA_LOG) << "createCommand() == NULL for action(?) \""
- << qPrintable(s->objectName()) << "\"";
- else {
- qCDebug(KLEOPATRA_LOG) << "I don't know anything about action(?) \"%s\"", qPrintable(s->objectName());
- }
- } else {
- qCDebug(KLEOPATRA_LOG) << "not called through a signal/slot connection (sender() == NULL)";
+void KeyListController::Private::slotActionTriggered(QAction *sender)
+{
+ const auto it = std::find_if(actions.cbegin(), actions.cend(),
+ [sender](const action_item &item) { return item.action == sender; });
+ if (it != actions.end())
+ if (Command *const c = it->createCommand(this->currentView, q)) {
+ if (parentWidget) {
+ c->setParentWidget(parentWidget);
+ }
+ c->start();
+ } else
+ qCDebug(KLEOPATRA_LOG) << "createCommand() == NULL for action(?) \""
+ << qPrintable(sender->objectName()) << "\"";
+ else {
+ qCDebug(KLEOPATRA_LOG) << "I don't know anything about action(?) \"%s\"", qPrintable(sender->objectName());
}
}
int KeyListController::Private::toolTipOptions() const
{
using namespace Kleo::Formatting;
static const int validityFlags = Validity | Issuer | ExpiryDates | CertificateUsage;
static const int ownerFlags = Subject | UserIDs | OwnerTrust;
static const int detailsFlags = StorageLocation | CertificateType | SerialNumber | Fingerprint;
const TooltipPreferences prefs;
int flags = KeyID;
flags |= prefs.showValidity() ? validityFlags : 0;
flags |= prefs.showOwnerInformation() ? ownerFlags : 0;
flags |= prefs.showCertificateDetails() ? detailsFlags : 0;
return flags;
}
void KeyListController::updateConfig()
{
const int opts = d->toolTipOptions();
if (d->flatModel) {
d->flatModel->setToolTipOptions(opts);
}
if (d->hierarchicalModel) {
d->hierarchicalModel->setToolTipOptions(opts);
}
}
#include "moc_keylistcontroller.cpp"
diff --git a/src/view/keylistcontroller.h b/src/view/keylistcontroller.h
index e9bbbcd89..50b3c2e26 100644
--- a/src/view/keylistcontroller.h
+++ b/src/view/keylistcontroller.h
@@ -1,104 +1,101 @@
/* -*- mode: c++; c-basic-offset:4 -*-
controllers/keylistcontroller.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QObject>
#include <commands/command.h>
#include <utils/pimpl_ptr.h>
#include <vector>
class QAbstractItemView;
class QAction;
class QPoint;
class QItemSelectionModel;
class KActionCollection;
namespace Kleo
{
class AbstractKeyListModel;
class Command;
class TabWidget;
class KeyListController : public QObject
{
Q_OBJECT
public:
explicit KeyListController(QObject *parent = nullptr);
~KeyListController() override;
std::vector<QAbstractItemView *> views() const;
void setFlatModel(AbstractKeyListModel *model);
AbstractKeyListModel *flatModel() const;
void setHierarchicalModel(AbstractKeyListModel *model);
AbstractKeyListModel *hierarchicalModel() const;
void setParentWidget(QWidget *parent);
QWidget *parentWidget() const;
QAbstractItemView *currentView() const;
void setTabWidget(TabWidget *tabs);
TabWidget *tabWidget() const;
void registerCommand(Command *cmd);
void createActions(KActionCollection *collection);
template <typename T_Command>
void registerActionForCommand(QAction *action)
{
this->registerAction(action, T_Command::restrictions(), &KeyListController::template create<T_Command>);
}
void enableDisableActions(const QItemSelectionModel *sm) const;
bool hasRunningCommands() const;
bool shutdownWarningRequired() const;
private:
void registerAction(QAction *action, Command::Restrictions restrictions, Command * (*create)(QAbstractItemView *, KeyListController *));
template <typename T_Command>
static Command *create(QAbstractItemView *v, KeyListController *c)
{
return new T_Command(v, c);
}
public Q_SLOTS:
void addView(QAbstractItemView *view);
void removeView(QAbstractItemView *view);
void setCurrentView(QAbstractItemView *view);
void cancelCommands();
void updateConfig();
Q_SIGNALS:
void progress(int current, int total);
void message(const QString &msg, int timeout = 0);
void commandsExecuting(bool);
void contextMenuRequested(QAbstractItemView *view, const QPoint &p);
private:
class Private;
kdtools::pimpl_ptr<Private> d;
-
- Q_PRIVATE_SLOT(d, void slotActionTriggered())
- Q_PRIVATE_SLOT(d, void slotCurrentViewChanged(QAbstractItemView *))
};
}
diff --git a/src/view/keytreeview.cpp b/src/view/keytreeview.cpp
index e982d2c03..fd7989de8 100644
--- a/src/view/keytreeview.cpp
+++ b/src/view/keytreeview.cpp
@@ -1,809 +1,798 @@
/* -*- 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 "searchbar.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 <QScrollBar>
#include <KSharedConfig>
#include <KLocalizedString>
#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) {
auto 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);
}
auto tv = qobject_cast<KeyTreeView *> (parent());
if (tv) {
tv->resizeColumns();
}
});
}
for (QAction *action : std::as_const(mColumnActions)) {
const int column = action->data().toInt();
action->setChecked(!isColumnHidden(column));
}
mHeaderPopup->popup(mapToGlobal(e->pos()));
return true;
}
return false;
}
void keyPressEvent(QKeyEvent *event) override;
QModelIndex moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers) override;
private:
bool mMoveCursorUpdatedView = false;
QMenu *mHeaderPopup = nullptr;
QList<QAction *> mColumnActions;
};
void TreeView::keyPressEvent(QKeyEvent *event)
{
mMoveCursorUpdatedView = false;
QTreeView::keyPressEvent(event);
if (mMoveCursorUpdatedView) {
event->accept();
}
}
QModelIndex TreeView::moveCursor(CursorAction cursorAction, Qt::KeyboardModifiers modifiers)
{
// this code is based heavily on QTreeView::moveCursor()
QModelIndex current = currentIndex();
if (!current.isValid()) {
// let QTreeView handle invalid current index
return QTreeView::moveCursor(cursorAction, modifiers);
}
if (isRightToLeft()) {
if (cursorAction == MoveRight) {
cursorAction = MoveLeft;
} else if (cursorAction == MoveLeft) {
cursorAction = MoveRight;
}
}
switch (cursorAction) {
case MoveLeft: {
// HACK: call QTreeView::moveCursor with invalid cursor action to make it call the private executePostedLayout()
(void) QTreeView::moveCursor(static_cast<CursorAction>(-1), modifiers);
int visualColumn = header()->visualIndex(current.column()) - 1;
while (visualColumn >= 0 && isColumnHidden(header()->logicalIndex(visualColumn))) {
visualColumn--;
}
int newColumn = header()->logicalIndex(visualColumn);
QModelIndex next = current.sibling(current.row(), newColumn);
if (next.isValid()) {
return next;
}
//last restort: we change the scrollbar value
QScrollBar *sb = horizontalScrollBar();
int oldValue = sb->value();
sb->setValue(sb->value() - sb->singleStep());
if (oldValue != sb->value()) {
mMoveCursorUpdatedView = true;
}
updateGeometries();
viewport()->update();
break;
}
case MoveRight: {
// HACK: call QTreeView::moveCursor with invalid cursor action to make it call the private executePostedLayout()
(void) QTreeView::moveCursor(static_cast<CursorAction>(-1), modifiers);
int visualColumn = header()->visualIndex(current.column()) + 1;
while (visualColumn < model()->columnCount(current.parent()) && isColumnHidden(header()->logicalIndex(visualColumn))) {
visualColumn++;
}
const int newColumn = header()->logicalIndex(visualColumn);
const QModelIndex next = current.sibling(current.row(), newColumn);
if (next.isValid()) {
return next;
}
//last restort: we change the scrollbar value
QScrollBar *sb = horizontalScrollBar();
int oldValue = sb->value();
sb->setValue(sb->value() + sb->singleStep());
if (oldValue != sb->value()) {
mMoveCursorUpdatedView = true;
}
updateGeometries();
viewport()->update();
break;
}
default:
return QTreeView::moveCursor(cursorAction, modifiers);
}
return current;
}
const KeyListModelInterface * keyListModel(const QTreeView &view)
{
const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(view.model());
Q_ASSERT(klmi);
return klmi;
}
} // 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 (auto 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 (auto 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()) {
// 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);
auto 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->setAllColumnsShowFocus(false);
m_view->setSortingEnabled(true);
m_view->setAccessibleName(i18n("Certificates"));
m_view->setAccessibleDescription(m_isHierarchical ? i18n("Hierarchical list of certificates") : i18n("List of certificates"));
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);
auto rearangingModel = new KeyRearrangeColumnsProxyModel(this);
rearangingModel->setSourceModel(m_proxy);
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
// If a column is added before this TAGS_COLUMN define has to be updated accordingly
<< KeyList::Remarks
);
m_view->setModel(rearangingModel);
/* Handle expansion state */
if (m_group.isValid()) {
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(KeyList::KeyRole).value<GpgME::Key>();
if (key.isNull()) {
return;
}
const auto fpr = QString::fromLatin1(key.primaryFingerprint());
if (m_expandedKeys.contains(fpr)) {
return;
}
m_expandedKeys << fpr;
if (m_group.isValid()) {
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(KeyList::KeyRole).value<GpgME::Key>();
if (key.isNull()) {
return;
}
m_expandedKeys.removeAll(QString::fromLatin1(key.primaryFingerprint()));
if (m_group.isValid()) {
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();
if (m_group.isValid()) {
restoreLayout(m_group);
}
}
void KeyTreeView::restoreExpandState()
{
if (!KeyCache::instance()->initialized()) {
qCWarning(KLEOPATRA_LOG) << "Restore expand state before keycache available. Aborting.";
return;
}
for (const auto &fpr: std::as_const(m_expandedKeys)) {
const KeyListModelInterface *const km = keyListModel(*m_view);
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()
{
const auto tagKeys = Tags::tagKeys();
if (m_hierarchicalModel) {
m_hierarchicalModel->setRemarkKeys(tagKeys);
}
if (m_flatModel) {
m_flatModel->setRemarkKeys(tagKeys);
}
}
void KeyTreeView::saveLayout(KConfigGroup &group)
{
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));
}
group.writeEntry("ColumnVisibility", columnVisibility);
group.writeEntry("ColumnOrder", columnOrder);
group.writeEntry("ColumnWidths", columnWidths);
group.writeEntry("SortAscending", (int)header->sortIndicatorOrder());
if (header->isSortIndicatorShown()) {
group.writeEntry("SortColumn", header->sortIndicatorSection());
} else {
group.writeEntry("SortColumn", -1);
}
}
void KeyTreeView::restoreLayout(const KConfigGroup &group)
{
QHeaderView *header = m_view->header();
QVariantList columnVisibility = group.readEntry("ColumnVisibility", QVariantList());
QVariantList columnOrder = group.readEntry("ColumnOrder", QVariantList());
QVariantList columnWidths = 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 = group.readEntry("SortAscending", (int)Qt::AscendingOrder);
int sortColumn = group.readEntry("SortColumn", 0);
if (sortColumn >= 0) {
m_view->sortByColumn(sortColumn, (Qt::SortOrder)sortOrder);
}
}
KeyTreeView::~KeyTreeView()
{
if (m_group.isValid()) {
saveLayout(m_group);
}
}
static QAbstractProxyModel *find_last_proxy(QAbstractProxyModel *pm)
{
Q_ASSERT(pm);
while (auto 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);
}
namespace
{
QItemSelection itemSelectionFromKeys(const std::vector<Key> &keys, const QTreeView &view)
{
const QModelIndexList indexes = keyListModel(view)->indexes(keys);
return std::accumulate(
indexes.cbegin(), indexes.cend(),
QItemSelection(),
[] (QItemSelection &selection, const QModelIndex &index) {
if (index.isValid()) {
selection.merge(QItemSelection(index, index), QItemSelectionModel::Select);
}
return selection;
});
}
}
void KeyTreeView::selectKeys(const std::vector<Key> &keys)
{
m_view->selectionModel()->select(itemSelectionFromKeys(keys, *m_view),
QItemSelectionModel::ClearAndSelect | QItemSelectionModel::Rows);
}
std::vector<Key> KeyTreeView::selectedKeys() const
{
return keyListModel(*m_view)->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 = this->selectedKeys();
const Key currentKey = keyListModel(*m_view)->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 = keyListModel(*m_view)->index(currentKey);
if (currentIndex.isValid()) {
m_view->selectionModel()->setCurrentIndex(currentIndex, QItemSelectionModel::NoUpdate);
m_view->scrollTo(currentIndex);
}
}
m_view->setAccessibleDescription(m_isHierarchical ? i18n("Hierarchical list of certificates") : i18n("List of certificates"));
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)
+void KeyTreeView::disconnectSearchBar()
{
- 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);
+ for (const auto &connection : m_connections) {
+ disconnect(connection);
}
+ m_connections.clear();
}
-bool KeyTreeView::connectSearchBar(const QObject *bar)
+bool KeyTreeView::connectSearchBar(const SearchBar *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;
+ m_connections.reserve(4);
+ m_connections.push_back(connect(this, &KeyTreeView::stringFilterChanged, bar, &SearchBar::setStringFilter));
+ m_connections.push_back(connect(bar, &SearchBar::stringFilterChanged, this, &KeyTreeView::setStringFilter));
+ m_connections.push_back(connect(this, &KeyTreeView::keyFilterChanged, bar, &SearchBar::setKeyFilter));
+ m_connections.push_back(connect(bar, &SearchBar::keyFilterChanged, this, &KeyTreeView::setKeyFilter));
+
+ return std::all_of(m_connections.cbegin(), m_connections.cend(), [](const QMetaObject::Connection &conn) {
+ return conn;
+ });
}
void KeyTreeView::resizeColumns()
{
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);
}
}
diff --git a/src/view/keytreeview.h b/src/view/keytreeview.h
index f632cf6f0..fe356e93d 100644
--- a/src/view/keytreeview.h
+++ b/src/view/keytreeview.h
@@ -1,158 +1,161 @@
/* -*- mode: c++; c-basic-offset:4 -*-
view/keytreeview.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#pragma once
#include <QWidget>
#include <QString>
#include <QStringList>
#include <gpgme++/key.h>
#include <memory>
#include <vector>
#include <KConfigGroup>
class QTreeView;
namespace Kleo
{
class KeyFilter;
class AbstractKeyListModel;
class AbstractKeyListSortFilterProxyModel;
class KeyListSortFilterProxyModel;
+class SearchBar;
class KeyTreeView : public QWidget
{
Q_OBJECT
public:
explicit KeyTreeView(QWidget *parent = nullptr);
KeyTreeView(const QString &stringFilter, const std::shared_ptr<KeyFilter> &keyFilter,
AbstractKeyListSortFilterProxyModel *additionalProxy, QWidget *parent,
const KConfigGroup &group);
~KeyTreeView() override;
QTreeView *view() const
{
return m_view;
}
AbstractKeyListModel *model() const
{
return m_isHierarchical ? hierarchicalModel() : flatModel();
}
AbstractKeyListModel *flatModel() const
{
return m_flatModel;
}
AbstractKeyListModel *hierarchicalModel() const
{
return m_hierarchicalModel;
}
void setFlatModel(AbstractKeyListModel *model);
void setHierarchicalModel(AbstractKeyListModel *model);
void setKeys(const std::vector<GpgME::Key> &keys);
const std::vector<GpgME::Key> &keys() const
{
return m_keys;
}
void selectKeys(const std::vector<GpgME::Key> &keys);
std::vector<GpgME::Key> selectedKeys() const;
void addKeysUnselected(const std::vector<GpgME::Key> &keys);
void addKeysSelected(const std::vector<GpgME::Key> &keys);
void removeKeys(const std::vector<GpgME::Key> &keys);
#if 0
void setToolTipOptions(int options);
int toolTipOptions() const;
#endif
QString stringFilter() const
{
return m_stringFilter;
}
const std::shared_ptr<KeyFilter> &keyFilter() const
{
return m_keyFilter;
}
bool isHierarchicalView() const
{
return m_isHierarchical;
}
void setColumnSizes(const std::vector<int> &sizes);
std::vector<int> columnSizes() const;
void setSortColumn(int sortColumn, Qt::SortOrder sortOrder);
int sortColumn() const;
Qt::SortOrder sortOrder() const;
virtual KeyTreeView *clone() const
{
return new KeyTreeView(*this);
}
- void disconnectSearchBar(const QObject *bar);
- bool connectSearchBar(const QObject *bar);
+ void disconnectSearchBar();
+ bool connectSearchBar(const SearchBar *bar);
void resizeColumns();
void saveLayout(KConfigGroup &group);
void restoreLayout(const KConfigGroup &group);
public Q_SLOTS:
virtual void setStringFilter(const QString &text);
virtual void setKeyFilter(const std::shared_ptr<Kleo::KeyFilter> &filter);
virtual void setHierarchicalView(bool on);
Q_SIGNALS:
void stringFilterChanged(const QString &filter);
void keyFilterChanged(const std::shared_ptr<Kleo::KeyFilter> &filter);
void hierarchicalChanged(bool on);
protected:
KeyTreeView(const KeyTreeView &);
private:
void init();
void addKeysImpl(const std::vector<GpgME::Key> &, bool);
void restoreExpandState();
void setUpTagKeys();
private:
std::vector<GpgME::Key> m_keys;
KeyListSortFilterProxyModel *m_proxy;
AbstractKeyListSortFilterProxyModel *m_additionalProxy;
QTreeView *m_view;
AbstractKeyListModel *m_flatModel;
AbstractKeyListModel *m_hierarchicalModel;
QString m_stringFilter;
std::shared_ptr<KeyFilter> m_keyFilter;
QStringList m_expandedKeys;
+ std::vector<QMetaObject::Connection> m_connections;
+
KConfigGroup m_group;
bool m_isHierarchical : 1;
bool m_onceResized : 1;
};
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Jan 27, 8:52 PM (1 d, 7 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
14/bf/f241fbcc3d0afa316979fdf3ec73

Event Timeline