Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34329093
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
23 KB
Subscribers
None
View Options
diff --git a/src/kleo/keyfiltermanager.cpp b/src/kleo/keyfiltermanager.cpp
index e7689a69..daa25bae 100644
--- a/src/kleo/keyfiltermanager.cpp
+++ b/src/kleo/keyfiltermanager.cpp
@@ -1,489 +1,625 @@
/*
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 <config-libkleo.h>
#include "keyfiltermanager.h"
#include "defaultkeyfilter.h"
#include "kconfigbasedkeyfilter.h"
#include "stl_util.h"
#include <libkleo/algorithm.h>
#include <libkleo/compliance.h>
#include <libkleo/gnupg.h>
#include <libkleo/keyhelpers.h>
#include <libkleo_debug.h>
#include <KConfig>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSharedConfig>
#include <QAbstractListModel>
#include <QCoreApplication>
#include <QIcon>
#include <QModelIndex>
#include <QRegularExpression>
#include <QStringList>
#include <algorithm>
#include <climits>
#include <functional>
using namespace Kleo;
using namespace GpgME;
namespace
{
+void adjustFilters(std::vector<std::shared_ptr<KeyFilter>> &filters, Protocol protocol)
+{
+ if (protocol != GpgME::UnknownProtocol) {
+ // remove filters with conflicting isOpenPGP rule
+ const auto conflictingValue = (protocol == GpgME::OpenPGP) ? DefaultKeyFilter::NotSet : DefaultKeyFilter::Set;
+ Kleo::erase_if(filters, [conflictingValue](const auto &f) {
+ const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
+ Q_ASSERT(filter);
+ return filter->isOpenPGP() == conflictingValue;
+ });
+ // add isOpenPGP rule to all filters
+ const auto isOpenPGPValue = (protocol == GpgME::OpenPGP) ? DefaultKeyFilter::Set : DefaultKeyFilter::NotSet;
+ std::for_each(std::begin(filters), std::end(filters), [isOpenPGPValue](auto &f) {
+ const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
+ Q_ASSERT(filter);
+ return filter->setIsOpenPGP(isOpenPGPValue);
+ });
+ }
+}
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 - 2); // 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 - 3); // 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 - 4);
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 - 5); // overly high for ordering
setName(i18n("Other Certificates"));
setId(QStringLiteral("other-certificates"));
setMatchContexts(Filtering);
}
};
/* This filter selects uncertified OpenPGP keys, i.e. "good" OpenPGP keys with
* unrevoked user IDs that are not fully valid. */
class UncertifiedOpenPGPKeysFilter : public DefaultKeyFilter
{
public:
UncertifiedOpenPGPKeysFilter()
: DefaultKeyFilter()
{
setSpecificity(UINT_MAX - 6); // overly high for ordering
setName(i18n("Not Certified Certificates"));
setId(QStringLiteral("not-certified-certificates"));
setMatchContexts(Filtering);
setIsOpenPGP(Set);
setIsBad(NotSet);
}
bool matches(const Key &key, MatchContexts contexts) const override
{
return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
}
bool matches(const UserID &userID, MatchContexts contexts) const override
{
return DefaultKeyFilter::matches(userID.parent(), contexts) && userID.validity() < UserID::Full;
}
};
/* 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()
{
setSpecificity(UINT_MAX - 7); // overly high for ordering
setName(i18n("Not Validated Certificates"));
setId(QStringLiteral("not-validated-certificates"));
setMatchContexts(Filtering);
}
bool matches(const Key &key, MatchContexts contexts) const override
{
return DefaultKeyFilter::matches(key, contexts) && !Kleo::allUserIDsHaveFullValidity(key);
}
bool matches(const UserID &userID, MatchContexts contexts) const override
{
return DefaultKeyFilter::matches(userID.parent(), contexts) && userID.validity() < UserID::Full;
}
};
}
static std::vector<std::shared_ptr<KeyFilter>> defaultFilters()
{
std::vector<std::shared_ptr<KeyFilter>> result;
result.reserve(6);
result.push_back(std::shared_ptr<KeyFilter>(new MyCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new TrustedCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new FullCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new OtherCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new AllCertificatesKeyFilter));
result.push_back(std::shared_ptr<KeyFilter>(new UncertifiedOpenPGPKeysFilter));
result.push_back(std::shared_ptr<KeyFilter>(new KeyNotValidFilter));
return result;
}
class KeyFilterManager::Private
{
public:
Private()
: filters()
, model(this)
{
}
void clear()
{
model.beginResetModel();
filters.clear();
model.endResetModel();
}
std::vector<std::shared_ptr<KeyFilter>> filters;
Model model;
GpgME::Protocol protocol = GpgME::UnknownProtocol;
};
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();
}
}
KeyFilterManager *KeyFilterManager::instance()
{
if (!mSelf) {
mSelf = new KeyFilterManager();
}
return mSelf;
}
void KeyFilterManager::alwaysFilterByProtocol(GpgME::Protocol protocol)
{
if (protocol != d->protocol) {
d->protocol = protocol;
reload();
+ Q_EMIT alwaysFilterByProtocolChanged(protocol);
}
}
const std::shared_ptr<KeyFilter> &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<KeyFilter> &filter) {
return filter->matches(key, contexts);
});
if (it != d->filters.cend()) {
return *it;
}
static const std::shared_ptr<KeyFilter> null;
return null;
}
std::vector<std::shared_ptr<KeyFilter>> KeyFilterManager::filtersMatching(const Key &key, KeyFilter::MatchContexts contexts) const
{
std::vector<std::shared_ptr<KeyFilter>> 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<KeyFilter> &filter) {
return !filter->matches(key, contexts);
});
return result;
}
namespace
{
static const auto byDecreasingSpecificity = [](const std::shared_ptr<KeyFilter> &lhs, const std::shared_ptr<KeyFilter> &rhs) {
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+$")));
const bool ignoreDeVs = !DeVSCompliance::isCompliant();
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<KeyFilter>(new KConfigBasedKeyFilter(cfg)));
}
std::stable_sort(d->filters.begin(), d->filters.end(), byDecreasingSpecificity);
- if (d->protocol != GpgME::UnknownProtocol) {
- // remove filters with conflicting isOpenPGP rule
- const auto conflictingValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::NotSet : DefaultKeyFilter::Set;
- Kleo::erase_if(d->filters, [conflictingValue](const auto &f) {
- const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
- Q_ASSERT(filter);
- return filter->isOpenPGP() == conflictingValue;
- });
- // add isOpenPGP rule to all filters
- const auto isOpenPGPValue = (d->protocol == GpgME::OpenPGP) ? DefaultKeyFilter::Set : DefaultKeyFilter::NotSet;
- std::for_each(std::begin(d->filters), std::end(d->filters), [isOpenPGPValue](auto &f) {
- const auto filter = std::dynamic_pointer_cast<DefaultKeyFilter>(f);
- Q_ASSERT(filter);
- return filter->setIsOpenPGP(isOpenPGPValue);
- });
- }
+ adjustFilters(d->filters, d->protocol);
qCDebug(LIBKLEO_LOG) << "KeyFilterManager::" << __func__ << "final filter count is" << d->filters.size();
}
QAbstractItemModel *KeyFilterManager::model() const
{
return &d->model;
}
const std::shared_ptr<KeyFilter> &KeyFilterManager::keyFilterByID(const QString &id) const
{
const auto it = std::find_if(d->filters.begin(), d->filters.end(), [id](const std::shared_ptr<KeyFilter> &filter) {
return filter->id() == id;
});
if (it != d->filters.end()) {
return *it;
}
static const std::shared_ptr<KeyFilter> null;
return null;
}
const std::shared_ptr<KeyFilter> &KeyFilterManager::fromModelIndex(const QModelIndex &idx) const
{
if (!idx.isValid() || idx.model() != &d->model || idx.row() < 0 || static_cast<unsigned>(idx.row()) >= d->filters.size()) {
static const std::shared_ptr<KeyFilter> null;
return null;
}
return d->filters[idx.row()];
}
QModelIndex KeyFilterManager::toModelIndex(const std::shared_ptr<KeyFilter> &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<unsigned>(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 KeyFilterManager::FilterIdRole:
return filter->id();
case KeyFilterManager::FilterMatchContextsRole:
return QVariant::fromValue(filter->availableMatchContexts());
+ case KeyFilterManager::FilterRole:
+ return QVariant::fromValue(filter);
+
default:
return QVariant();
}
}
static KeyFilter::FontDescription
get_fontdescription(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, const KeyFilter::FontDescription &initial)
{
return kdtools::accumulate_if(
filters.begin(),
filters.end(),
[&key](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, KeyFilter::Appearance);
},
initial,
[](const KeyFilter::FontDescription &lhs, const std::shared_ptr<KeyFilter> &rhs) {
return lhs.resolve(rhs->fontDescription());
});
}
QFont KeyFilterManager::font(const Key &key, const QFont &baseFont) const
{
const KeyFilter::FontDescription fd = get_fontdescription(d->filters, key, KeyFilter::FontDescription());
return fd.font(baseFont);
}
static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QColor (KeyFilter::*fun)() const)
{
const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(key, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
});
if (it == filters.cend()) {
return {};
} else {
return (it->get()->*fun)();
}
}
static QColor get_color(const std::vector<std::shared_ptr<KeyFilter>> &filters, const UserID &userID, QColor (KeyFilter::*fun)() const)
{
const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &userID](const std::shared_ptr<KeyFilter> &filter) {
return filter->matches(userID, KeyFilter::Appearance) && (filter.get()->*fun)().isValid();
});
if (it == filters.cend()) {
return {};
} else {
return (it->get()->*fun)();
}
}
static QString get_string(const std::vector<std::shared_ptr<KeyFilter>> &filters, const Key &key, QString (KeyFilter::*fun)() const)
{
const auto it = std::find_if(filters.cbegin(), filters.cend(), [&fun, &key](const std::shared_ptr<KeyFilter> &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
{
return get_color(d->filters, key, &KeyFilter::bgColor);
}
QColor KeyFilterManager::fgColor(const Key &key) const
{
return get_color(d->filters, key, &KeyFilter::fgColor);
}
QColor KeyFilterManager::bgColor(const UserID &userID) const
{
return get_color(d->filters, userID, &KeyFilter::bgColor);
}
QColor KeyFilterManager::fgColor(const UserID &userID) const
{
return get_color(d->filters, userID, &KeyFilter::fgColor);
}
QIcon KeyFilterManager::icon(const Key &key) const
{
const QString icon = get_string(d->filters, key, &KeyFilter::icon);
return icon.isEmpty() ? QIcon() : QIcon::fromTheme(icon);
}
+
+Protocol KeyFilterManager::protocol() const
+{
+ return d->protocol;
+}
+
+class KeyFilterModel::Private
+{
+ friend class KeyFilterModel;
+ std::vector<std::shared_ptr<KeyFilter>> customFilters;
+};
+
+KeyFilterModel::KeyFilterModel(QObject *parent)
+ : QSortFilterProxyModel(parent)
+ , d(new Private)
+{
+ setSourceModel(KeyFilterManager::instance()->model());
+ connect(KeyFilterManager::instance(), &KeyFilterManager::alwaysFilterByProtocolChanged, this, [this](auto protocol) {
+ beginResetModel();
+ adjustFilters(d->customFilters, protocol);
+ endResetModel();
+ });
+}
+
+void KeyFilterModel::prependCustomFilter(const std::shared_ptr<KeyFilter> &filter)
+{
+ beginResetModel();
+ d->customFilters.insert(d->customFilters.begin(), filter);
+ adjustFilters(d->customFilters, KeyFilterManager::instance()->protocol());
+ endResetModel();
+}
+
+bool KeyFilterModel::isCustomFilter(int row) const
+{
+ return row < d->customFilters.size();
+}
+
+int KeyFilterModel::rowCount(const QModelIndex &parent) const
+{
+ return d->customFilters.size() + QSortFilterProxyModel::rowCount(parent);
+}
+
+int KeyFilterModel::columnCount(const QModelIndex &parent) const
+{
+ Q_UNUSED(parent)
+ // pretend that there is only one column to workaround a bug in
+ // QAccessibleTable which provides the accessibility interface for the
+ // pop-up of the combo box
+ return 1;
+}
+
+QModelIndex KeyFilterModel::mapToSource(const QModelIndex &index) const
+{
+ if (!index.isValid()) {
+ return {};
+ }
+ if (!isCustomFilter(index.row())) {
+ const int sourceRow = index.row() - d->customFilters.size();
+ return QSortFilterProxyModel::mapToSource(createIndex(sourceRow, index.column(), index.internalPointer()));
+ }
+ return {};
+}
+
+QModelIndex KeyFilterModel::mapFromSource(const QModelIndex &source_index) const
+{
+ const QModelIndex idx = QSortFilterProxyModel::mapFromSource(source_index);
+ return createIndex(d->customFilters.size() + idx.row(), idx.column(), idx.internalPointer());
+}
+
+QModelIndex KeyFilterModel::index(int row, int column, const QModelIndex &parent) const
+{
+ if (row < 0 || row >= rowCount()) {
+ return {};
+ }
+ if (row < d->customFilters.size()) {
+ return createIndex(row, column, nullptr);
+ } else {
+ const QModelIndex mi = QSortFilterProxyModel::index(row - d->customFilters.size(), column, parent);
+ return createIndex(row, column, mi.internalPointer());
+ }
+}
+
+Qt::ItemFlags KeyFilterModel::flags(const QModelIndex &index) const
+{
+ Q_UNUSED(index)
+ return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
+}
+
+QModelIndex KeyFilterModel::parent(const QModelIndex &) const
+{
+ // Flat list
+ return {};
+}
+
+QVariant KeyFilterModel::data(const QModelIndex &index, int role) const
+{
+ if (!index.isValid()) {
+ return QVariant();
+ }
+
+ if (isCustomFilter(index.row())) {
+ const auto filter = d->customFilters[index.row()];
+ switch (role) {
+ case Qt::DecorationRole:
+ return filter->icon();
+
+ case Qt::DisplayRole:
+ case Qt::EditRole:
+ return filter->name();
+ case Qt::ToolTipRole:
+ return filter->description();
+
+ case KeyFilterManager::FilterIdRole:
+ return filter->id();
+
+ case KeyFilterManager::FilterMatchContextsRole:
+ return QVariant::fromValue(filter->availableMatchContexts());
+
+ case KeyFilterManager::FilterRole:
+ return QVariant::fromValue(filter);
+
+ default:
+ return QVariant();
+ }
+ }
+
+ return QSortFilterProxyModel::data(index, role);
+}
diff --git a/src/kleo/keyfiltermanager.h b/src/kleo/keyfiltermanager.h
index 9892cf50..71628d5d 100644
--- a/src/kleo/keyfiltermanager.h
+++ b/src/kleo/keyfiltermanager.h
@@ -1,84 +1,113 @@
/*
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 <Libkleo/KeyFilter>
#include <QObject>
+#include <QSortFilterProxyModel>
#include <gpgme++/global.h>
#include <memory>
#include <vector>
namespace GpgME
{
class Key;
class UserID;
}
-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,
+ FilterRole,
};
protected:
explicit KeyFilterManager(QObject *parent = nullptr);
~KeyFilterManager() override;
public:
static KeyFilterManager *instance();
/**
* Adds the rule that keys must match @p protocol to all filters.
*/
void alwaysFilterByProtocol(GpgME::Protocol protocol);
+ GpgME::Protocol protocol() const;
const std::shared_ptr<KeyFilter> &filterMatching(const GpgME::Key &key, KeyFilter::MatchContexts contexts) const;
std::vector<std::shared_ptr<KeyFilter>> filtersMatching(const GpgME::Key &key, KeyFilter::MatchContexts contexts) const;
QAbstractItemModel *model() const;
const std::shared_ptr<KeyFilter> &keyFilterByID(const QString &id) const;
const std::shared_ptr<KeyFilter> &fromModelIndex(const QModelIndex &mi) const;
QModelIndex toModelIndex(const std::shared_ptr<KeyFilter> &kf) const;
void reload();
QFont font(const GpgME::Key &key, const QFont &baseFont) const;
QColor bgColor(const GpgME::Key &key) const;
QColor bgColor(const GpgME::UserID &userID) const;
QColor fgColor(const GpgME::Key &key) const;
QColor fgColor(const GpgME::UserID &userID) const;
QIcon icon(const GpgME::Key &key) const;
class Private;
+Q_SIGNALS:
+ void alwaysFilterByProtocolChanged(GpgME::Protocol protocol);
+
private:
std::unique_ptr<Private> d;
static KeyFilterManager *mSelf;
};
+class KLEO_EXPORT KeyFilterModel : public QSortFilterProxyModel
+{
+ Q_OBJECT
+
+public:
+ KeyFilterModel(QObject *parent = nullptr);
+ bool isCustomFilter(int row) const;
+ void prependCustomFilter(const std::shared_ptr<KeyFilter> &filter);
+
+ int rowCount(const QModelIndex &parent = QModelIndex()) const override;
+ int columnCount(const QModelIndex &parent = QModelIndex()) const override;
+
+ QModelIndex mapToSource(const QModelIndex &index) const override;
+ QModelIndex mapFromSource(const QModelIndex &source_index) const override;
+
+ QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override;
+ Qt::ItemFlags flags(const QModelIndex &index) const override;
+ QModelIndex parent(const QModelIndex &) const override;
+ QVariant data(const QModelIndex &index, int role) const override;
+
+private:
+ class Private;
+ const std::unique_ptr<Private> d;
+};
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Dec 30, 5:47 PM (1 d, 9 h)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
73/cf/b367d43f1528ecf99307449282f1
Attached To
rLIBKLEO Libkleo
Event Timeline
Log In to Comment