diff --git a/src/kleo/keyfilter.h b/src/kleo/keyfilter.h index edb0b596d..9a6dc9e56 100644 --- a/src/kleo/keyfilter.h +++ b/src/kleo/keyfilter.h @@ -1,96 +1,99 @@ /* keyfilter.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include #include #include namespace GpgME { class Key; } class QFont; class QColor; class QString; namespace Kleo { /** @short An abstract base class key filters */ class KLEO_EXPORT KeyFilter { public: virtual ~KeyFilter() {} enum MatchContext { NoMatchContext = 0x0, Appearance = 0x1, Filtering = 0x2, AnyMatchContext = Appearance | Filtering }; Q_DECLARE_FLAGS(MatchContexts, MatchContext) virtual bool matches(const GpgME::Key &key, MatchContexts ctx) const = 0; virtual unsigned int specificity() const = 0; virtual QString id() const = 0; virtual MatchContexts availableMatchContexts() const = 0; // not sure if we want these here, but for the time being, it's // the easiest way: virtual QColor fgColor() const = 0; virtual QColor bgColor() const = 0; virtual QString name() const = 0; virtual QString icon() const = 0; class FontDescription { public: FontDescription(); FontDescription(const FontDescription &other); FontDescription &operator=(const FontDescription &other) { FontDescription copy(other); swap(copy); return *this; } ~FontDescription(); static FontDescription create(bool bold, bool italic, bool strikeOut); static FontDescription create(const QFont &font, bool bold, bool italic, bool strikeOut); QFont font(const QFont &base) const; FontDescription resolve(const FontDescription &other) const; void swap(FontDescription &other) { std::swap(this->d, other.d); } struct Private; private: Private *d; }; virtual FontDescription fontDescription() const = 0; }; Q_DECLARE_OPERATORS_FOR_FLAGS(KeyFilter::MatchContexts) } +#include + +Q_DECLARE_METATYPE(Kleo::KeyFilter::MatchContexts) diff --git a/src/kleo/keyfiltermanager.cpp b/src/kleo/keyfiltermanager.cpp index 9e0e769c2..5ae9dbf22 100644 --- a/src/kleo/keyfiltermanager.cpp +++ b/src/kleo/keyfiltermanager.cpp @@ -1,427 +1,430 @@ /* keyfiltermanager.cpp This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include "keyfiltermanager.h" #include "kconfigbasedkeyfilter.h" #include "defaultkeyfilter.h" #include "stl_util.h" #include "libkleo_debug.h" #include "utils/formatting.h" #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace GpgME; namespace { class Model : public QAbstractListModel { KeyFilterManager::Private *m_keyFilterManagerPrivate; public: explicit Model(KeyFilterManager::Private *p) : QAbstractListModel(nullptr), m_keyFilterManagerPrivate(p) {} int rowCount(const QModelIndex &) const override; QVariant data(const QModelIndex &idx, int role) const override; /* upgrade to public */ using QAbstractListModel::beginResetModel; /* upgrade to public */ using QAbstractListModel::endResetModel; }; class AllCertificatesKeyFilter : public DefaultKeyFilter { public: AllCertificatesKeyFilter() : DefaultKeyFilter() { setSpecificity(UINT_MAX); // overly high for ordering setName(i18n("All Certificates")); setId(QStringLiteral("all-certificates")); setMatchContexts(Filtering); } }; class MyCertificatesKeyFilter : public DefaultKeyFilter { public: MyCertificatesKeyFilter() : DefaultKeyFilter() { setHasSecret(Set); setSpecificity(UINT_MAX - 1); // overly high for ordering setName(i18n("My Certificates")); setId(QStringLiteral("my-certificates")); setMatchContexts(AnyMatchContext); setBold(true); } }; class TrustedCertificatesKeyFilter : public DefaultKeyFilter { public: TrustedCertificatesKeyFilter() : DefaultKeyFilter() { setRevoked(NotSet); setValidity(IsAtLeast); setValidityReferenceLevel(UserID::Marginal); setSpecificity(UINT_MAX - 2); // overly high for ordering setName(i18n("Trusted Certificates")); setId(QStringLiteral("trusted-certificates")); setMatchContexts(Filtering); } }; class FullCertificatesKeyFilter : public DefaultKeyFilter { public: FullCertificatesKeyFilter() : DefaultKeyFilter() { setRevoked(NotSet); setValidity(IsAtLeast); setValidityReferenceLevel(UserID::Full); setSpecificity(UINT_MAX - 3); setName(i18n("Fully Trusted Certificates")); setId(QStringLiteral("full-certificates")); setMatchContexts(Filtering); } }; class OtherCertificatesKeyFilter : public DefaultKeyFilter { public: OtherCertificatesKeyFilter() : DefaultKeyFilter() { setHasSecret(NotSet); setValidity(IsAtMost); setValidityReferenceLevel(UserID::Never); setSpecificity(UINT_MAX - 4); // overly high for ordering setName(i18n("Other Certificates")); setId(QStringLiteral("other-certificates")); setMatchContexts(Filtering); } }; /* This filter selects only invalid keys (i.e. those where not all * UIDs are at least fully valid). */ class KeyNotValidFilter : public DefaultKeyFilter { public: KeyNotValidFilter() : DefaultKeyFilter() { setName(i18n("Not validated Certificates")); setId(QStringLiteral("not-validated-certificates")); setSpecificity(UINT_MAX - 6); // overly high for ordering } bool matches (const Key &key, MatchContexts contexts) const override { return (contexts & Filtering) && !Formatting::uidsHaveFullValidity(key); } }; } static std::vector> defaultFilters() { std::vector > result; result.reserve(6); result.push_back(std::shared_ptr(new MyCertificatesKeyFilter)); result.push_back(std::shared_ptr(new TrustedCertificatesKeyFilter)); result.push_back(std::shared_ptr(new FullCertificatesKeyFilter)); result.push_back(std::shared_ptr(new OtherCertificatesKeyFilter)); result.push_back(std::shared_ptr(new AllCertificatesKeyFilter)); result.push_back(std::shared_ptr(new KeyNotValidFilter)); return result; } class KeyFilterManager::Private { public: Private() : filters(), appearanceFilters(), model(this) {} void clear() { model.beginResetModel(); filters.clear(); appearanceFilters.clear(); model.endResetModel(); } std::vector> filters; std::vector> appearanceFilters; Model model; }; KeyFilterManager *KeyFilterManager::mSelf = nullptr; KeyFilterManager::KeyFilterManager(QObject *parent) : QObject(parent), d(new Private) { mSelf = this; // ### DF: doesn't a KStaticDeleter work more reliably? if (QCoreApplication *app = QCoreApplication::instance()) { connect(app, &QCoreApplication::aboutToQuit, this, &QObject::deleteLater); } reload(); } KeyFilterManager::~KeyFilterManager() { mSelf = nullptr; if (d) { d->clear(); } delete d; d = nullptr; } KeyFilterManager *KeyFilterManager::instance() { if (!mSelf) { mSelf = new KeyFilterManager(); } return mSelf; } const std::shared_ptr &KeyFilterManager::filterMatching(const Key &key, KeyFilter::MatchContexts contexts) const { const auto it = std::find_if(d->filters.cbegin(), d->filters.cend(), [&key, contexts](const std::shared_ptr &filter) { return filter->matches(key, contexts); }); if (it != d->filters.cend()) { return *it; } static const std::shared_ptr null; return null; } std::vector> KeyFilterManager::filtersMatching(const Key &key, KeyFilter::MatchContexts contexts) const { std::vector> result; result.reserve(d->filters.size()); std::remove_copy_if(d->filters.begin(), d->filters.end(), std::back_inserter(result), [&key, contexts](const std::shared_ptr &filter) { return !filter->matches(key, contexts); }); return result; } namespace { struct ByDecreasingSpecificity : std::binary_function, std::shared_ptr, bool> { bool operator()(const std::shared_ptr &lhs, const std::shared_ptr &rhs) const { return lhs->specificity() > rhs->specificity(); } }; } void KeyFilterManager::reload() { d->clear(); d->filters = defaultFilters(); KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc")); const QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$"))); bool ignoreDeVs = Formatting::complianceMode() != QLatin1String("de-vs"); for (QStringList::const_iterator it = groups.begin(); it != groups.end(); ++it) { const KConfigGroup cfg(config, *it); if (cfg.hasKey("is-de-vs") && ignoreDeVs) { /* Don't show de-vs filters in other compliance modes */ continue; } d->filters.push_back(std::shared_ptr(new KConfigBasedKeyFilter(cfg))); } std::stable_sort(d->filters.begin(), d->filters.end(), ByDecreasingSpecificity()); qCDebug(LIBKLEO_LOG) << "final filter count is" << d->filters.size(); } QAbstractItemModel *KeyFilterManager::model() const { return &d->model; } const std::shared_ptr &KeyFilterManager::keyFilterByID(const QString &id) const { const auto it = std::find_if(d->filters.begin(), d->filters.end(), [id](const std::shared_ptr &filter) { return filter->id() == id; }); if (it != d->filters.end()) { return *it; } static const std::shared_ptr null; return null; } const std::shared_ptr &KeyFilterManager::fromModelIndex(const QModelIndex &idx) const { if (!idx.isValid() || idx.model() != &d->model || idx.row() < 0 || static_cast(idx.row()) >= d->filters.size()) { static const std::shared_ptr null; return null; } return d->filters[idx.row()]; } QModelIndex KeyFilterManager::toModelIndex(const std::shared_ptr &kf) const { if (!kf) { return {}; } const auto pair = std::equal_range(d->filters.cbegin(), d->filters.cend(), kf, ByDecreasingSpecificity()); const auto it = std::find(pair.first, pair.second, kf); if (it != pair.second) { return d->model.index(it - d->filters.begin()); } else { return QModelIndex(); } } int Model::rowCount(const QModelIndex &) const { return m_keyFilterManagerPrivate->filters.size(); } QVariant Model::data(const QModelIndex &idx, int role) const { if (!idx.isValid() || idx.model() != this || idx.row() < 0 || static_cast(idx.row()) > m_keyFilterManagerPrivate->filters.size()) { return QVariant(); } const auto filter = m_keyFilterManagerPrivate->filters[idx.row()]; switch (role) { case Qt::DecorationRole: return filter->icon(); case Qt::DisplayRole: case Qt::EditRole: case Qt::ToolTipRole: /* Most useless tooltip ever. */ return filter->name(); - case Qt::UserRole: + case KeyFilterManager::FilterIdRole: return filter->id(); + case KeyFilterManager::FilterMatchContextsRole: + return QVariant::fromValue(filter->availableMatchContexts()); + default: return QVariant(); } } static KeyFilter::FontDescription get_fontdescription(const std::vector> &filters, const Key &key, const KeyFilter::FontDescription &initial) { return kdtools::accumulate_if(filters.begin(), filters.end(), [&key](const std::shared_ptr &filter) { return filter->matches(key, KeyFilter::Appearance); }, initial, [](const KeyFilter::FontDescription &lhs, const std::shared_ptr &rhs) { return lhs.resolve(rhs->fontDescription()); }); } QFont KeyFilterManager::font(const Key &key, const QFont &baseFont) const { KeyFilter::FontDescription fd; fd = get_fontdescription(d->appearanceFilters, key, KeyFilter::FontDescription()); fd = get_fontdescription(d->filters, key, fd); return fd.font(baseFont); } static QColor get_color(const std::vector> &filters, const Key &key, QColor(KeyFilter::*fun)() const) { const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr &filter) { return filter->matches(key, KeyFilter::Appearance) && (filter.get()->*fun)().isValid(); }); if (it == filters.cend()) { return {}; } else { return (it->get()->*fun)(); } } static QString get_string(const std::vector> &filters, const Key &key, QString(KeyFilter::*fun)() const) { const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr &filter) { return filter->matches(key, KeyFilter::Appearance) && !(filter.get()->*fun)().isEmpty(); }); if (it == filters.cend()) { return QString(); } else { return (*it)->icon(); } } QColor KeyFilterManager::bgColor(const Key &key) const { QColor color; color = get_color(d->appearanceFilters, key, &KeyFilter::bgColor); if (!color.isValid()) { color = get_color(d->filters, key, &KeyFilter::bgColor); } return color; } QColor KeyFilterManager::fgColor(const Key &key) const { QColor color; color = get_color(d->appearanceFilters, key, &KeyFilter::fgColor); if (!color.isValid()) { color = get_color(d->filters, key, &KeyFilter::fgColor); } return color; } QIcon KeyFilterManager::icon(const Key &key) const { QString icon; icon = get_string(d->appearanceFilters, key, &KeyFilter::icon); if (icon.isEmpty()) { icon = get_string(d->filters, key, &KeyFilter::icon); } return icon.isEmpty() ? QIcon() : QIcon::fromTheme(icon); } diff --git a/src/kleo/keyfiltermanager.h b/src/kleo/keyfiltermanager.h index 52738232d..0c5ad1c44 100644 --- a/src/kleo/keyfiltermanager.h +++ b/src/kleo/keyfiltermanager.h @@ -1,67 +1,73 @@ /* keyfiltermanager.h This file is part of libkleopatra, the KDE keymanagement library SPDX-FileCopyrightText: 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once #include "kleo_export.h" #include #include #include #include namespace GpgME { class Key; } class QAbstractItemModel; class QModelIndex; class QFont; class QColor; class QIcon; namespace Kleo { class KLEO_EXPORT KeyFilterManager : public QObject { Q_OBJECT +public: + enum ModelRoles { + FilterIdRole = Qt::UserRole, + FilterMatchContextsRole, + }; + protected: explicit KeyFilterManager(QObject *parent = nullptr); ~KeyFilterManager(); public: static KeyFilterManager *instance(); const std::shared_ptr &filterMatching(const GpgME::Key &key, KeyFilter::MatchContexts contexts) const; std::vector> filtersMatching(const GpgME::Key &key, KeyFilter::MatchContexts contexts) const; QAbstractItemModel *model() const; const std::shared_ptr &keyFilterByID(const QString &id) const; const std::shared_ptr &fromModelIndex(const QModelIndex &mi) const; QModelIndex toModelIndex(const std::shared_ptr &kf) const; void reload(); QFont font(const GpgME::Key &key, const QFont &baseFont) const; QColor bgColor(const GpgME::Key &key) const; QColor fgColor(const GpgME::Key &key) const; QIcon icon(const GpgME::Key &key) const; class Private; private: Private *d; static KeyFilterManager *mSelf; }; }