Page MenuHome GnuPG

No OneTemporary

diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index e4530722..aa48ed40 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -1,228 +1,230 @@
# target_include_directories does not handle empty include paths
include_directories(${GPGME_INCLUDES})
add_definitions(-DTRANSLATION_DOMAIN=\"libkleopatra\")
#add_definitions( -DQT_NO_CAST_FROM_ASCII )
#add_definitions( -DQT_NO_CAST_TO_ASCII )
kde_enable_exceptions()
add_definitions( -DGPGMEPP_ERR_SOURCE_DEFAULT=13 ) # 13 is GPG_ERR_SOURCE_KLEO, even if gpg-error's too old to know about
add_subdirectory( pics )
if (BUILD_TESTING)
add_subdirectory( tests )
endif()
########### next target ###############
set(libkleo_core_SRCS
kleo/checksumdefinition.cpp
kleo/defaultkeyfilter.cpp
kleo/defaultkeygenerationjob.cpp
kleo/dn.cpp
kleo/enum.cpp
kleo/kconfigbasedkeyfilter.cpp
kleo/keyfiltermanager.cpp
+ kleo/keygroup.cpp
kleo/keyresolver.cpp
kleo/kleoexception.cpp
kleo/oidmap.cpp
models/keycache.cpp
models/keylistmodel.cpp
models/keylistmodelinterface.cpp
models/keylistsortfilterproxymodel.cpp
models/keyrearrangecolumnsproxymodel.cpp
models/subkeylistmodel.cpp
models/useridlistmodel.cpp
utils/filesystemwatcher.cpp
utils/formatting.cpp
utils/classify.cpp
utils/gnupg.cpp
utils/gnupg-registry.c
utils/hex.cpp
)
ecm_qt_declare_logging_category(libkleo_core_SRCS HEADER libkleo_debug.h IDENTIFIER LIBKLEO_LOG CATEGORY_NAME org.kde.pim.libkleo
DESCRIPTION "libkleo (kleo_core)"
EXPORT LIBKLEO
)
set(libkleo_ui_common_SRCS
ui/dnattributeorderconfigwidget.cpp
ui/kdhorizontalline.cpp
ui/filenamerequester.cpp
ui/messagebox.cpp
ui/cryptoconfigmodule.cpp
ui/cryptoconfigdialog.cpp
ui/directoryserviceswidget.cpp
ui/progressbar.cpp
ui/progressdialog.cpp
ui/auditlogviewer.cpp
)
ecm_qt_declare_logging_category(libkleo_ui_common_SRCS HEADER kleo_ui_debug.h IDENTIFIER KLEO_UI_LOG CATEGORY_NAME org.kde.pim.kleo_ui
DESCRIPTION "libkleo (kleo_ui)"
OLD_CATEGORY_NAMES log_kleo_ui
EXPORT LIBKLEO
)
set(libkleo_ui_SRCS # make this a separate lib.
ui/keylistview.cpp
ui/keyselectiondialog.cpp
ui/keyrequester.cpp
ui/keyapprovaldialog.cpp
ui/newkeyapprovaldialog.cpp
ui/keyselectioncombo.cpp
)
ki18n_wrap_ui(libkleo_ui_common_SRCS
ui/directoryserviceswidget.ui
)
set(kleo_LIB_SRCS ${libkleo_core_SRCS} ${libkleo_ui_SRCS}
${libkleo_ui_common_SRCS})
set(kleo_LIB_LIBS PUBLIC QGpgme Gpgmepp PRIVATE Qt5::Widgets
KF5::I18n
KF5::Completion
KF5::ConfigCore
KF5::CoreAddons
KF5::WidgetsAddons
KF5::ItemModels
KF5::Codecs)
if (KF5PimTextEdit_FOUND)
add_definitions(-DHAVE_PIMTEXTEDIT)
set(kleo_LIB_LIBS ${kleo_LIB_LIBS} PRIVATE KF5::PimTextEdit)
endif()
add_library(KF5Libkleo ${kleo_LIB_SRCS})
if (COMPILE_WITH_UNITY_CMAKE_SUPPORT)
set_target_properties(KF5Libkleo PROPERTIES UNITY_BUILD ON)
endif()
generate_export_header(KF5Libkleo BASE_NAME kleo)
add_library(KF5::Libkleo ALIAS KF5Libkleo)
if(WIN32)
target_link_libraries(KF5Libkleo ${kleo_LIB_LIBS} ${GPGME_VANILLA_LIBRARIES} )
else()
target_link_libraries(KF5Libkleo ${kleo_LIB_LIBS} )
endif()
set_target_properties(KF5Libkleo PROPERTIES
VERSION ${LIBKLEO_VERSION_STRING}
SOVERSION ${LIBKLEO_SOVERSION}
EXPORT_NAME Libkleo
)
install(TARGETS
KF5Libkleo
EXPORT KF5LibkleoTargets ${KF5_INSTALL_TARGETS_DEFAULT_ARGS}
)
target_include_directories(KF5Libkleo PUBLIC "$<BUILD_INTERFACE:${libkleo_SOURCE_DIR}/src;${libkleo_BINARY_DIR}/src>")
target_include_directories(KF5Libkleo INTERFACE "$<INSTALL_INTERFACE:${KDE_INSTALL_INCLUDEDIR_KF5}/Libkleo/;${KDE_INSTALL_INCLUDEDIR_KF5}/libkleo>")
ecm_generate_headers(libkleo_CamelCase_HEADERS
HEADER_NAMES
ChecksumDefinition
DefaultKeyFilter
DefaultKeyGenerationJob
Dn
Enum
KConfigBasedKeyFilter
KeyFilter
KeyFilterManager
+ KeyGroup
KeyResolver
KleoException
OidMap
Predicates
Stl_Util
REQUIRED_HEADERS libkleo_HEADERS
PREFIX Libkleo
RELATIVE kleo
)
ecm_generate_headers(libkleo_CamelCase_models_HEADERS
HEADER_NAMES
KeyCache
KeyList
KeyListModel
KeyListModelInterface
KeyListSortFilterProxyModel
KeyRearrangeColumnsProxyModel
SubkeyListModel
UserIDListModel
REQUIRED_HEADERS libkleo_models_HEADERS
PREFIX Libkleo
RELATIVE models
)
ecm_generate_headers(libkleo_CamelCase_utils_HEADERS
HEADER_NAMES
Classify
FileSystemWatcher
Formatting
GnuPG
REQUIRED_HEADERS libkleo_utils_HEADERS
PREFIX Libkleo
RELATIVE utils
)
ecm_generate_headers(libkleo_CamelCase_ui_HEADERS
HEADER_NAMES
CryptoConfigDialog
CryptoConfigModule
DNAttributeOrderConfigWidget
DirectoryServicesWidget
FileNameRequester
KDHorizontalLine
KeyApprovalDialog
NewKeyApprovalDialog
KeyRequester
KeySelectionCombo
KeySelectionDialog
MessageBox
ProgressDialog
REQUIRED_HEADERS libkleo_ui_HEADERS
PREFIX Libkleo
RELATIVE ui
)
ecm_generate_pri_file(BASE_NAME Libkleo
LIB_NAME KF5Libkleo
DEPS "QGpgme" FILENAME_VAR PRI_FILENAME INCLUDE_INSTALL_DIR ${KDE_INSTALL_INCLUDEDIR_KF5}/Libkleo
)
install(FILES
${libkleo_CamelCase_HEADERS}
${libkleo_CamelCase_models_HEADERS}
${libkleo_CamelCase_ui_HEADERS}
${libkleo_CamelCase_utils_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/Libkleo
COMPONENT Devel
)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/kleo_export.h
${libkleo_HEADERS}
${libkleo_models_HEADERS}
${libkleo_ui_HEADERS}
${libkleo_utils_HEADERS}
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5}/libkleo
COMPONENT Devel
)
install(FILES
${PRI_FILENAME}
DESTINATION ${ECM_MKSPECS_INSTALL_DIR})
if ( WIN32 )
install ( FILES libkleopatrarc-win32.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc )
else ()
install ( FILES libkleopatrarc.desktop DESTINATION ${KDE_INSTALL_CONFDIR} RENAME libkleopatrarc )
endif ()
diff --git a/src/kleo/keygroup.cpp b/src/kleo/keygroup.cpp
new file mode 100644
index 00000000..69240a4f
--- /dev/null
+++ b/src/kleo/keygroup.cpp
@@ -0,0 +1,75 @@
+/*
+ kleo/keygroup.cpp
+
+ This file is part of libkleopatra, the KDE keymanagement library
+ SPDX-FileCopyrightText: 2021 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#include "keygroup.h"
+
+#include <QString>
+
+#include <gpgme++/key.h>
+
+using namespace Kleo;
+using namespace GpgME;
+
+class KeyGroup::Private
+{
+public:
+ explicit Private(const QString &name, const std::vector<GpgME::Key> &keys);
+
+ QString name;
+ std::vector<Key> keys;
+};
+
+KeyGroup::Private::Private(const QString &name_, const std::vector<GpgME::Key> &keys_)
+ : name(name_)
+ , keys(keys_)
+{
+}
+
+KeyGroup::KeyGroup()
+ : KeyGroup(QString(), {})
+{
+}
+
+KeyGroup::~KeyGroup() = default;
+
+KeyGroup::KeyGroup(const QString &name, const std::vector<GpgME::Key> &keys)
+ : d(new Private(name, keys))
+{
+}
+
+KeyGroup::KeyGroup(const KeyGroup &other)
+ : d(new Private(*other.d))
+{
+}
+
+KeyGroup &KeyGroup::operator=(const KeyGroup &other)
+{
+ *d = *other.d;
+ return *this;
+}
+
+KeyGroup::KeyGroup(KeyGroup &&other) = default;
+
+KeyGroup &KeyGroup::operator=(KeyGroup &&other) = default;
+
+bool KeyGroup::isNull() const
+{
+ return !d || d->name.isEmpty();
+}
+
+QString KeyGroup::name() const
+{
+ return d ? d->name : QString();
+}
+
+std::vector<Key> Kleo::KeyGroup::keys() const
+{
+ return d ? d->keys : std::vector<Key>();
+}
diff --git a/src/kleo/keygroup.h b/src/kleo/keygroup.h
new file mode 100644
index 00000000..7a2bd7f7
--- /dev/null
+++ b/src/kleo/keygroup.h
@@ -0,0 +1,56 @@
+/*
+ kleo/keygroup.h
+
+ This file is part of libkleopatra, the KDE keymanagement library
+ SPDX-FileCopyrightText: 2021 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
+
+ SPDX-License-Identifier: GPL-2.0-or-later
+*/
+
+#ifndef LIBKLEO_KEYGROUP_H
+#define LIBKLEO_KEYGROUP_H
+
+#include "kleo_export.h"
+
+#include <memory>
+#include <vector>
+
+class QString;
+
+namespace GpgME
+{
+class Key;
+}
+
+namespace Kleo
+{
+
+class KLEO_EXPORT KeyGroup
+{
+public:
+ KeyGroup();
+ ~KeyGroup();
+
+ explicit KeyGroup(const QString &name, const std::vector<GpgME::Key> &keys);
+
+ KeyGroup(const KeyGroup &other);
+ KeyGroup &operator=(const KeyGroup &other);
+
+ KeyGroup(KeyGroup &&other);
+ KeyGroup &operator=(KeyGroup &&other);
+
+ bool isNull() const;
+
+ QString name() const;
+
+ std::vector<GpgME::Key> keys() const;
+
+private:
+ class Private;
+ std::unique_ptr<Private> d;
+};
+
+}
+
+#endif // LIBKLEO_KEYGROUP_H
diff --git a/src/models/keylist.h b/src/models/keylist.h
index 74bd2239..97e1ad70 100644
--- a/src/models/keylist.h
+++ b/src/models/keylist.h
@@ -1,43 +1,46 @@
/*
models/keylist.h
This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
+ SPDX-FileCopyrightText: 2021 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef LIBKLEO_KEYLIST_H
#define LIBKLEO_KEYLIST_H
namespace Kleo
{
namespace KeyList
{
static const int FingerprintRole = 0xF1;
static const int KeyRole = 0xF2;
+ static const int GroupRole = 0xF3;
enum Columns {
PrettyName,
PrettyEMail,
ValidFrom,
ValidUntil,
TechnicalDetails,
ShortKeyID,
KeyID,
Fingerprint,
Issuer,
SerialNumber,
OwnerTrust,
Origin,
LastUpdate,
Validity,
Summary, // Short summary line
Remarks, // Additional remark notations
NumColumns,
Icon = PrettyName // which column shall the icon be displayed in?
};
}
}
#endif /* LIBKLEO_KEYLIST_H */
diff --git a/src/models/keylistmodel.cpp b/src/models/keylistmodel.cpp
index 8418f639..ec4558ec 100644
--- a/src/models/keylistmodel.cpp
+++ b/src/models/keylistmodel.cpp
@@ -1,1012 +1,1188 @@
/* -*- mode: c++; c-basic-offset:4 -*-
models/keylistmodel.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
+ SPDX-FileCopyrightText: 2021 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "keylistmodel.h"
#include "keycache.h"
#include "keylist.h"
+#include "kleo/keygroup.h"
#include "kleo/predicates.h"
#include "kleo/keyfiltermanager.h"
#include "kleo/keyfilter.h"
#include "utils/formatting.h"
#ifdef KLEO_MODEL_TEST
# include <QAbstractItemModelTester>
#endif
#include <Libkleo/KeyFilterManager>
#include <Libkleo/KeyFilter>
#include <KLocalizedString>
#include <QFont>
#include <QColor>
#include <QHash>
#include <QIcon>
#include <QDate>
#include <gpgme++/key.h>
#ifndef Q_MOC_RUN // QTBUG-22829
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/adjacency_list.hpp>
#endif
#include <algorithm>
#include <map>
#include <set>
#include <iterator>
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
# define GPGME_HAS_REMARKS
#endif
using namespace GpgME;
using namespace Kleo;
using namespace Kleo::KeyList;
Q_DECLARE_METATYPE(GpgME::Key)
+Q_DECLARE_METATYPE(KeyGroup)
class AbstractKeyListModel::Private
{
public:
int m_toolTipOptions = Formatting::Validity;
mutable QHash<const char *, QVariant> prettyEMailCache;
mutable QHash<const char *, QVariant> remarksCache;
bool m_useKeyCache = false;
bool m_secretOnly = false;
std::vector<GpgME::Key> m_remarkKeys;
};
AbstractKeyListModel::AbstractKeyListModel(QObject *p)
: QAbstractItemModel(p), KeyListModelInterface(), d(new Private)
{
-
}
AbstractKeyListModel::~AbstractKeyListModel() {}
void AbstractKeyListModel::setToolTipOptions(int opts)
{
d->m_toolTipOptions = opts;
}
int AbstractKeyListModel::toolTipOptions() const
{
return d->m_toolTipOptions;
}
void AbstractKeyListModel::setRemarkKeys(const std::vector<GpgME::Key> &keys)
{
d->m_remarkKeys = keys;
}
std::vector<GpgME::Key> AbstractKeyListModel::remarkKeys() const
{
return d->m_remarkKeys;
}
Key AbstractKeyListModel::key(const QModelIndex &idx) const
{
if (idx.isValid()) {
return doMapToKey(idx);
} else {
return Key::null;
}
}
std::vector<Key> AbstractKeyListModel::keys(const QList<QModelIndex> &indexes) const
{
std::vector<Key> result;
result.reserve(indexes.size());
std::transform(indexes.begin(), indexes.end(),
std::back_inserter(result),
[this](const QModelIndex &idx) {
return this->key(idx);
});
result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
return result;
}
+KeyGroup AbstractKeyListModel::group(const QModelIndex &idx) const
+{
+ if (idx.isValid()) {
+ return doMapToGroup(idx);
+ } else {
+ return KeyGroup();
+ }
+}
+
QModelIndex AbstractKeyListModel::index(const Key &key) const
{
return index(key, 0);
}
QModelIndex AbstractKeyListModel::index(const Key &key, int col) const
{
if (key.isNull() || col < 0 || col >= NumColumns) {
return {};
} else {
return doMapFromKey(key, col);
}
}
QList<QModelIndex> AbstractKeyListModel::indexes(const std::vector<Key> &keys) const
{
QList<QModelIndex> result;
result.reserve(keys.size());
std::transform(keys.begin(), keys.end(),
std::back_inserter(result),
[this](const Key &key) {
return this->index(key);
});
return result;
}
+QModelIndex AbstractKeyListModel::index(const KeyGroup &group) const
+{
+ return index(group, 0);
+}
+
+QModelIndex AbstractKeyListModel::index(const KeyGroup &group, int col) const
+{
+ if (group.isNull() || col < 0 || col >= NumColumns) {
+ return {};
+ } else {
+ return doMapFromGroup(group, col);
+ }
+}
+
void AbstractKeyListModel::setKeys(const std::vector<Key> &keys)
{
- clear();
+ clear(Keys);
addKeys(keys);
}
QModelIndex AbstractKeyListModel::addKey(const Key &key)
{
const std::vector<Key> vec(1, key);
const QList<QModelIndex> l = doAddKeys(vec);
return l.empty() ? QModelIndex() : l.front();
}
void AbstractKeyListModel::removeKey(const Key &key)
{
if (key.isNull()) {
return;
}
doRemoveKey(key);
d->prettyEMailCache.remove(key.primaryFingerprint());
d->remarksCache.remove(key.primaryFingerprint());
}
QList<QModelIndex> AbstractKeyListModel::addKeys(const std::vector<Key> &keys)
{
std::vector<Key> sorted;
sorted.reserve(keys.size());
std::remove_copy_if(keys.begin(), keys.end(),
std::back_inserter(sorted),
std::mem_fn(&Key::isNull));
std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
return doAddKeys(sorted);
}
-void AbstractKeyListModel::clear()
+QModelIndex AbstractKeyListModel::addGroup(const KeyGroup &group)
+{
+ return doAddGroup(group);
+}
+
+void AbstractKeyListModel::clear(ItemTypes types)
{
beginResetModel();
- doClear();
- d->prettyEMailCache.clear();
- d->remarksCache.clear();
+ doClear(types);
+ if (types & Keys) {
+ d->prettyEMailCache.clear();
+ d->remarksCache.clear();
+ }
endResetModel();
}
int AbstractKeyListModel::columnCount(const QModelIndex &) const
{
return NumColumns;
}
QVariant AbstractKeyListModel::headerData(int section, Qt::Orientation o, int role) const
{
if (o == Qt::Horizontal)
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole)
switch (section) {
case PrettyName: return i18n("Name");
case PrettyEMail: return i18n("E-Mail");
case Validity: return i18n("User-IDs");
case ValidFrom: return i18n("Valid From");
case ValidUntil: return i18n("Valid Until");
case TechnicalDetails: return i18n("Protocol");
case ShortKeyID: return i18n("Key-ID");
case KeyID: return i18n("Key-ID");
case Fingerprint: return i18n("Fingerprint");
case Issuer: return i18n("Issuer");
case SerialNumber: return i18n("Serial Number");
case Origin: return i18n("Origin");
case LastUpdate: return i18n("Last Update");
case OwnerTrust: return i18n("Certification Trust");
case Remarks: return i18n("Tags");
case NumColumns:;
}
return QVariant();
}
static QVariant returnIfValid(const QColor &t)
{
if (t.isValid()) {
return t;
} else {
return QVariant();
}
}
static QVariant returnIfValid(const QIcon &t)
{
if (!t.isNull()) {
return t;
} else {
return QVariant();
}
}
QVariant AbstractKeyListModel::data(const QModelIndex &index, int role) const
{
const Key key = this->key(index);
- if (key.isNull()) {
- return QVariant();
+ if (!key.isNull()) {
+ return data(key, index.column(), role);
}
- const int column = index.column();
+ const KeyGroup group = this->group(index);
+ if (!group.isNull()) {
+ return data(group, index.column(), role);
+ }
- if (role == Qt::DisplayRole || role == Qt::EditRole) {
+ return QVariant();
+}
+
+QVariant AbstractKeyListModel::data(const Key &key, int column, int role) const
+{
+ if (role == Qt::DisplayRole || role == Qt::EditRole) {
switch (column) {
case PrettyName:
return Formatting::prettyName(key);
case PrettyEMail:
if (const char *const fpr = key.primaryFingerprint()) {
const QHash<const char *, QVariant>::const_iterator it = d->prettyEMailCache.constFind(fpr);
if (it != d->prettyEMailCache.constEnd()) {
return *it;
} else {
return d->prettyEMailCache[fpr] = Formatting::prettyEMail(key);
}
} else {
return QVariant();
}
case Validity:
return Formatting::complianceStringShort(key);
case ValidFrom:
if (role == Qt::EditRole) {
return Formatting::creationDate(key);
} else {
return Formatting::creationDateString(key);
}
case ValidUntil:
if (role == Qt::EditRole) {
return Formatting::expirationDate(key);
} else {
return Formatting::expirationDateString(key);
}
case TechnicalDetails:
return Formatting::type(key);
case ShortKeyID:
return QString::fromLatin1(key.shortKeyID());
case KeyID:
return Formatting::prettyID(key.keyID());
case Summary:
return Formatting::summaryLine(key);
case Fingerprint:
return Formatting::prettyID(key.primaryFingerprint());
case Issuer:
return QString::fromUtf8(key.issuerName());
case Origin:
return Formatting::origin(key.origin());
case LastUpdate:
return Formatting::dateString(key.lastUpdate());
case SerialNumber:
return QString::fromUtf8(key.issuerSerial());
case OwnerTrust:
return Formatting::ownerTrustShort(key.ownerTrust());
case Remarks:
#ifdef GPGME_HAS_REMARKS
{
const char *const fpr = key.primaryFingerprint();
if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() &&
d->m_remarkKeys.size()) {
if (!(key.keyListMode() & GpgME::SignatureNotations)) {
return i18n("Loading...");
}
const QHash<const char *, QVariant>::const_iterator it = d->remarksCache.constFind(fpr);
if (it != d->remarksCache.constEnd()) {
return *it;
} else {
GpgME::Error err;
const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err);
if (remarks.size() == 1) {
const auto remark = QString::fromStdString(remarks[0]);
return d->remarksCache[fpr] = remark;
} else {
QStringList remarkList;
remarkList.reserve(remarks.size());
for (const auto &rem: remarks) {
remarkList << QString::fromStdString(rem);
}
const auto remark = remarkList.join(QStringLiteral("; "));
return d->remarksCache[fpr] = remark;
}
}
} else {
return QVariant();
}
}
#endif
return QVariant();
case NumColumns:
break;
}
} else if (role == Qt::ToolTipRole) {
return Formatting::toolTip(key, toolTipOptions());
} else if (role == Qt::FontRole) {
return KeyFilterManager::instance()->font(key, (column == ShortKeyID || column == KeyID || column == Fingerprint) ? QFont(QStringLiteral("monospace")) : QFont());
} else if (role == Qt::DecorationRole) {
return column == Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) : QVariant();
} else if (role == Qt::BackgroundRole) {
return returnIfValid(KeyFilterManager::instance()->bgColor(key));
} else if (role == Qt::ForegroundRole) {
return returnIfValid(KeyFilterManager::instance()->fgColor(key));
} else if (role == FingerprintRole) {
return QString::fromLatin1(key.primaryFingerprint());
} else if (role == KeyRole) {
return QVariant::fromValue(key);
}
return QVariant();
}
+QVariant AbstractKeyListModel::data(const KeyGroup &group, int column, int role) const
+{
+ if (role == Qt::DisplayRole || role == Qt::EditRole) {
+ switch (column) {
+ case PrettyName:
+ return group.name();
+ case PrettyEMail:
+ return QVariant();
+ case Validity:
+ return QString();
+ case ValidFrom:
+ return QString();
+ case ValidUntil:
+ return QString();
+ case TechnicalDetails:
+ return i18nc("a group of keys/certificates", "Group");
+ case ShortKeyID:
+ return QString();
+ case KeyID:
+ return QString();
+ case Summary:
+ return group.name(); // used for filtering
+ case Fingerprint:
+ return QString();
+ case Issuer:
+ return QString();
+ case Origin:
+ return QString();
+ case LastUpdate:
+ return QString();
+ case SerialNumber:
+ return QString();
+ case OwnerTrust:
+ return QString();
+ case Remarks:
+ return QVariant();
+ case NumColumns:
+ break;
+ }
+ } else if (role == Qt::ToolTipRole) {
+ return QString();
+ } else if (role == Qt::FontRole) {
+ return QFont();
+ } else if (role == Qt::DecorationRole) {
+ return column == Icon ? QIcon::fromTheme("group") : QVariant();
+ } else if (role == Qt::BackgroundRole) {
+ } else if (role == Qt::ForegroundRole) {
+ } else if (role == GroupRole) {
+ return QVariant::fromValue(group);
+ }
+ return QVariant();
+}
+
namespace
{
template <typename Base>
class TableModelMixin : public Base
{
public:
explicit TableModelMixin(QObject *p = nullptr) : Base(p) {}
~TableModelMixin() {}
using Base::index;
QModelIndex index(int row, int column, const QModelIndex &pidx = QModelIndex()) const override
{
return this->hasIndex(row, column, pidx) ? this->createIndex(row, column, nullptr) : QModelIndex();
}
private:
QModelIndex parent(const QModelIndex &) const override
{
return QModelIndex();
}
bool hasChildren(const QModelIndex &pidx) const override
{
return (pidx.model() == this || !pidx.isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0;
}
};
class FlatKeyListModel
#ifndef Q_MOC_RUN
: public TableModelMixin<AbstractKeyListModel>
#else
: public AbstractKeyListModel
#endif
{
Q_OBJECT
public:
explicit FlatKeyListModel(QObject *parent = nullptr);
~FlatKeyListModel() override;
int rowCount(const QModelIndex &pidx) const override
{
- return pidx.isValid() ? 0 : mKeysByFingerprint.size();
+ return pidx.isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size();
}
private:
Key doMapToKey(const QModelIndex &index) const override;
QModelIndex doMapFromKey(const Key &key, int col) const override;
QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) override;
void doRemoveKey(const Key &key) override;
- void doClear() override {
- mKeysByFingerprint.clear();
+
+ KeyGroup doMapToGroup(const QModelIndex &index) const override;
+ QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override;
+ QModelIndex doAddGroup(const KeyGroup &group) override;
+
+ void doClear(ItemTypes types) override
+ {
+ if (types & Keys) {
+ mKeysByFingerprint.clear();
+ }
+ if (types & Groups) {
+ mGroups.clear();
+ }
}
private:
std::vector<Key> mKeysByFingerprint;
+ std::vector<KeyGroup> mGroups;
};
class HierarchicalKeyListModel : public AbstractKeyListModel
{
Q_OBJECT
public:
explicit HierarchicalKeyListModel(QObject *parent = nullptr);
~HierarchicalKeyListModel() override;
int rowCount(const QModelIndex &pidx) const override;
using AbstractKeyListModel::index;
QModelIndex index(int row, int col, const QModelIndex &pidx) const override;
QModelIndex parent(const QModelIndex &idx) const override;
bool hasChildren(const QModelIndex &pidx) const override
{
return rowCount(pidx) > 0;
}
private:
Key doMapToKey(const QModelIndex &index) const override;
QModelIndex doMapFromKey(const Key &key, int col) const override;
QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) override;
void doRemoveKey(const Key &key) override;
- void doClear() override {
+
+ KeyGroup doMapToGroup(const QModelIndex &index) const override;
+ QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override;
+ QModelIndex doAddGroup(const KeyGroup &group) override;
+
+ void doClear(ItemTypes types) override
+ {
mTopLevels.clear();
mKeysByFingerprint.clear();
mKeysByExistingParent.clear();
mKeysByNonExistingParent.clear();
}
private:
void addTopLevelKey(const Key &key);
void addKeyWithParent(const char *issuer_fpr, const Key &key);
void addKeyWithoutParent(const char *issuer_fpr, const Key &key);
private:
typedef std::map< std::string, std::vector<Key> > Map;
std::vector<Key> mKeysByFingerprint; // all keys
Map mKeysByExistingParent, mKeysByNonExistingParent; // parent->child map
std::vector<Key> mTopLevels; // all roots + parent-less
};
static const char *cleanChainID(const Key &key)
{
if (key.isRoot()) {
return "";
}
if (const char *chid = key.chainID()) {
return chid;
}
return "";
}
}
FlatKeyListModel::FlatKeyListModel(QObject *p)
- : TableModelMixin<AbstractKeyListModel>(p),
- mKeysByFingerprint()
+ : TableModelMixin<AbstractKeyListModel>(p)
{
-
}
FlatKeyListModel::~FlatKeyListModel() {}
Key FlatKeyListModel::doMapToKey(const QModelIndex &idx) const
{
Q_ASSERT(idx.isValid());
if (static_cast<unsigned>(idx.row()) < mKeysByFingerprint.size() && idx.column() < NumColumns) {
return mKeysByFingerprint[ idx.row() ];
} else {
return Key::null;
}
}
QModelIndex FlatKeyListModel::doMapFromKey(const Key &key, int col) const
{
Q_ASSERT(!key.isNull());
const std::vector<Key>::const_iterator it
= std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
key, _detail::ByFingerprint<std::less>());
if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
return {};
} else {
return createIndex(it - mKeysByFingerprint.begin(), col);
}
}
QList<QModelIndex> FlatKeyListModel::doAddKeys(const std::vector<Key> &keys)
{
Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
if (keys.empty()) {
return QList<QModelIndex>();
}
for (auto it = keys.begin(), end = keys.end(); it != end; ++it) {
// find an insertion point:
const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
// key existed before - replace with new one:
mKeysByFingerprint[idx - 1] = *it;
Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
} else {
// new key - insert:
beginInsertRows(QModelIndex(), idx, idx);
mKeysByFingerprint.insert(pos, *it);
endInsertRows();
}
}
return indexes(keys);
}
void FlatKeyListModel::doRemoveKey(const Key &key)
{
const std::vector<Key>::iterator it
= qBinaryFind(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
key, _detail::ByFingerprint<std::less>());
if (it == mKeysByFingerprint.end()) {
return;
}
const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
beginRemoveRows(QModelIndex(), row, row);
mKeysByFingerprint.erase(it);
endRemoveRows();
}
+KeyGroup FlatKeyListModel::doMapToGroup(const QModelIndex &idx) const
+{
+ Q_ASSERT(idx.isValid());
+ if (static_cast<unsigned>(idx.row()) >= mKeysByFingerprint.size()
+ && static_cast<unsigned>(idx.row()) < mKeysByFingerprint.size() + mGroups.size()
+ && idx.column() < NumColumns) {
+ return mGroups[ idx.row() - mKeysByFingerprint.size() ];
+ } else {
+ return KeyGroup();
+ }
+}
+
+QModelIndex FlatKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
+{
+ Q_ASSERT(!group.isNull());
+ const QString name = group.name();
+ const auto it = std::find_if(mGroups.begin(), mGroups.end(), [name](const KeyGroup &g) { return g.name() == name; });
+ if (it == mGroups.end()) {
+ return QModelIndex();
+ } else {
+ return createIndex(it - mGroups.begin() + mKeysByFingerprint.size(), column);
+ }
+}
+
+QModelIndex FlatKeyListModel::doAddGroup(const KeyGroup &group)
+{
+ if (group.isNull()) {
+ return QModelIndex();
+ }
+
+ // look for existing group
+ const QModelIndex index = doMapFromGroup(group, 0);
+ if (index.isValid()) {
+ // group existed before - replace with new one
+ const int indexInGroups = index.row() - mKeysByFingerprint.size();
+ mGroups[indexInGroups] = group;
+ Q_EMIT dataChanged(createIndex(index.row(), 0), createIndex(index.row(), NumColumns - 1));
+ return index;
+ } else {
+ // new group - append
+ const int newRow = mKeysByFingerprint.size() + mGroups.size();
+ beginInsertRows(QModelIndex(), newRow, newRow);
+ mGroups.push_back(group);
+ endInsertRows();
+ return createIndex(newRow, 0);
+ }
+}
+
HierarchicalKeyListModel::HierarchicalKeyListModel(QObject *p)
: AbstractKeyListModel(p),
mKeysByFingerprint(),
mKeysByExistingParent(),
mKeysByNonExistingParent(),
mTopLevels()
{
}
HierarchicalKeyListModel::~HierarchicalKeyListModel() {}
int HierarchicalKeyListModel::rowCount(const QModelIndex &pidx) const
{
// toplevel item:
if (!pidx.isValid()) {
return mTopLevels.size();
}
if (pidx.column() != 0) {
return 0;
}
// non-toplevel item - find the number of subjects for this issuer:
const Key issuer = this->key(pidx);
const char *const fpr = issuer.primaryFingerprint();
if (!fpr || !*fpr) {
return 0;
}
const Map::const_iterator it = mKeysByExistingParent.find(fpr);
if (it == mKeysByExistingParent.end()) {
return 0;
}
return it->second.size();
}
QModelIndex HierarchicalKeyListModel::index(int row, int col, const QModelIndex &pidx) const
{
if (row < 0 || col < 0 || col >= NumColumns) {
return {};
}
// toplevel item:
if (!pidx.isValid()) {
if (static_cast<unsigned>(row) < mTopLevels.size()) {
return index(mTopLevels[row], col);
} else {
return QModelIndex();
}
}
// non-toplevel item - find the row'th subject of this key:
const Key issuer = this->key(pidx);
const char *const fpr = issuer.primaryFingerprint();
if (!fpr || !*fpr) {
return QModelIndex();
}
const Map::const_iterator it = mKeysByExistingParent.find(fpr);
if (it == mKeysByExistingParent.end() || static_cast<unsigned>(row) >= it->second.size()) {
return QModelIndex();
}
return index(it->second[row], col);
}
QModelIndex HierarchicalKeyListModel::parent(const QModelIndex &idx) const
{
const Key key = this->key(idx);
if (key.isNull() || key.isRoot()) {
return {};
}
const std::vector<Key>::const_iterator it
= qBinaryFind(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
cleanChainID(key), _detail::ByFingerprint<std::less>());
return it != mKeysByFingerprint.end() ? index(*it) : QModelIndex();
}
Key HierarchicalKeyListModel::doMapToKey(const QModelIndex &idx) const
{
if (!idx.isValid()) {
return Key::null;
}
const char *const issuer_fpr = static_cast<const char *>(idx.internalPointer());
if (!issuer_fpr || !*issuer_fpr) {
// top-level:
if (static_cast<unsigned>(idx.row()) >= mTopLevels.size()) {
return Key::null;
} else {
return mTopLevels[idx.row()];
}
}
// non-toplevel:
const Map::const_iterator it
= mKeysByExistingParent.find(issuer_fpr);
if (it == mKeysByExistingParent.end() || static_cast<unsigned>(idx.row()) >= it->second.size()) {
return Key::null;
}
return it->second[idx.row()];
}
QModelIndex HierarchicalKeyListModel::doMapFromKey(const Key &key, int col) const
{
if (key.isNull()) {
return {};
}
const char *issuer_fpr = cleanChainID(key);
// we need to look in the toplevels list,...
const std::vector<Key> *v = &mTopLevels;
if (issuer_fpr && *issuer_fpr) {
const std::map< std::string, std::vector<Key> >::const_iterator it
= mKeysByExistingParent.find(issuer_fpr);
// ...unless we find an existing parent:
if (it != mKeysByExistingParent.end()) {
v = &it->second;
} else {
issuer_fpr = nullptr; // force internalPointer to zero for toplevels
}
}
const std::vector<Key>::const_iterator it
= std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
return QModelIndex();
}
const unsigned int row = std::distance(v->begin(), it);
return createIndex(row, col, const_cast<char * /* thanks, Trolls :/ */ >(issuer_fpr));
}
void HierarchicalKeyListModel::addKeyWithParent(const char *issuer_fpr, const Key &key)
{
Q_ASSERT(issuer_fpr); Q_ASSERT(*issuer_fpr); Q_ASSERT(!key.isNull());
std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
// find insertion point:
const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
const int row = std::distance(subjects.begin(), it);
if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
// exists -> replace
*it = key;
Q_EMIT dataChanged(createIndex(row, 0, const_cast<char *>(issuer_fpr)), createIndex(row, NumColumns - 1, const_cast<char *>(issuer_fpr)));
} else {
// doesn't exist -> insert
const std::vector<Key>::const_iterator pos = qBinaryFind(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
Q_ASSERT(pos != mKeysByFingerprint.end());
beginInsertRows(index(*pos), row, row);
subjects.insert(it, key);
endInsertRows();
}
}
void HierarchicalKeyListModel::addKeyWithoutParent(const char *issuer_fpr, const Key &key)
{
Q_ASSERT(issuer_fpr); Q_ASSERT(*issuer_fpr); Q_ASSERT(!key.isNull());
std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
// find insertion point:
const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0)
// exists -> replace
{
*it = key;
} else
// doesn't exist -> insert
{
subjects.insert(it, key);
}
addTopLevelKey(key);
}
void HierarchicalKeyListModel::addTopLevelKey(const Key &key)
{
// find insertion point:
const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
const int row = std::distance(mTopLevels.begin(), it);
if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
// exists -> replace
*it = key;
Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
} else {
// doesn't exist -> insert
beginInsertRows(QModelIndex(), row, row);
mTopLevels.insert(it, key);
endInsertRows();
}
}
// sorts 'keys' such that parent always come before their children:
static std::vector<Key> topological_sort(const std::vector<Key> &keys)
{
boost::adjacency_list<> graph(keys.size());
// add edges from children to parents:
for (unsigned int i = 0, end = keys.size(); i != end; ++i) {
const char *const issuer_fpr = cleanChainID(keys[i]);
if (!issuer_fpr || !*issuer_fpr) {
continue;
}
const std::vector<Key>::const_iterator it
= qBinaryFind(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
if (it == keys.end()) {
continue;
}
add_edge(i, std::distance(keys.begin(), it), graph);
}
std::vector<int> order;
order.reserve(keys.size());
topological_sort(graph, std::back_inserter(order));
Q_ASSERT(order.size() == keys.size());
std::vector<Key> result;
result.reserve(keys.size());
for (int i : qAsConst(order)) {
result.push_back(keys[i]);
}
return result;
}
QList<QModelIndex> HierarchicalKeyListModel::doAddKeys(const std::vector<Key> &keys)
{
Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
if (keys.empty()) {
return QList<QModelIndex>();
}
const std::vector<Key> oldKeys = mKeysByFingerprint;
std::vector<Key> merged;
merged.reserve(keys.size() + mKeysByFingerprint.size());
std::set_union(keys.begin(), keys.end(),
mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
std::back_inserter(merged), _detail::ByFingerprint<std::less>());
mKeysByFingerprint = merged;
std::set<Key, _detail::ByFingerprint<std::less> > changedParents;
Q_FOREACH (const Key &key, topological_sort(keys)) {
// check to see whether this key is a parent for a previously parent-less group:
const char *const fpr = key.primaryFingerprint();
if (!fpr || !*fpr) {
continue;
}
const bool keyAlreadyExisted = qBinaryFind(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>()) != oldKeys.end();
const Map::iterator it = mKeysByNonExistingParent.find(fpr);
const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
if (it != mKeysByNonExistingParent.end()) {
mKeysByNonExistingParent.erase(it);
}
// Step 1: For new keys, remove children from toplevel:
if (!keyAlreadyExisted) {
auto last = mTopLevels.begin();
auto lastFP = mKeysByFingerprint.begin();
for (const Key &k : qAsConst(children)) {
last = qBinaryFind(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
Q_ASSERT(last != mTopLevels.end());
const int row = std::distance(mTopLevels.begin(), last);
lastFP = qBinaryFind(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
Q_ASSERT(lastFP != mKeysByFingerprint.end());
Q_EMIT rowAboutToBeMoved(QModelIndex(), row);
beginRemoveRows(QModelIndex(), row, row);
last = mTopLevels.erase(last);
lastFP = mKeysByFingerprint.erase(lastFP);
endRemoveRows();
}
}
// Step 2: add/update key
const char *const issuer_fpr = cleanChainID(key);
if (!issuer_fpr || !*issuer_fpr)
// root or something...
{
addTopLevelKey(key);
} else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>()))
// parent exists...
{
addKeyWithParent(issuer_fpr, key);
} else
// parent doesn't exist yet...
{
addKeyWithoutParent(issuer_fpr, key);
}
const QModelIndex key_idx = index(key);
QModelIndex key_parent = key_idx.parent();
while (key_parent.isValid()) {
changedParents.insert(doMapToKey(key_parent));
key_parent = key_parent.parent();
}
// Step 3: Add children to new parent ( == key )
if (!keyAlreadyExisted && !children.empty()) {
addKeys(children);
const QModelIndex new_parent = index(key);
// Q_EMIT the rowMoved() signals in reversed direction, so the
// implementation can use a stack for mapping.
for (int i = children.size() - 1; i >= 0; --i) {
Q_EMIT rowMoved(new_parent, i);
}
}
}
//Q_EMIT dataChanged for all parents with new children. This triggers KeyListSortFilterProxyModel to
//show a parent node if it just got children matching the proxy's filter
for (const Key &i : qAsConst(changedParents)) {
const QModelIndex idx = index(i);
if (idx.isValid()) {
Q_EMIT dataChanged(idx.sibling(idx.row(), 0), idx.sibling(idx.row(), NumColumns - 1));
}
}
return indexes(keys);
}
void HierarchicalKeyListModel::doRemoveKey(const Key &key)
{
const QModelIndex idx = index(key);
if (!idx.isValid()) {
return;
}
const char *const fpr = key.primaryFingerprint();
if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
//handle non-leave nodes:
std::vector<Key> keys = mKeysByFingerprint;
const std::vector<Key>::iterator it = qBinaryFind(keys.begin(), keys.end(),
key, _detail::ByFingerprint<std::less>());
if (it == keys.end()) {
return;
}
keys.erase(it);
// FIXME for simplicity, we just clear the model and re-add all keys minus the removed one. This is suboptimal,
// but acceptable given that deletion of non-leave nodes is rather rare.
- clear();
+ clear(Keys);
addKeys(keys);
return;
}
//handle leave nodes:
const std::vector<Key>::iterator it = qBinaryFind(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
key, _detail::ByFingerprint<std::less>());
Q_ASSERT(it != mKeysByFingerprint.end());
Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
beginRemoveRows(parent(idx), idx.row(), idx.row());
mKeysByFingerprint.erase(it);
const char *const issuer_fpr = cleanChainID(key);
const std::vector<Key>::iterator tlIt = qBinaryFind(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
if (tlIt != mTopLevels.end()) {
mTopLevels.erase(tlIt);
}
if (issuer_fpr && *issuer_fpr) {
const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
if (nexIt != mKeysByNonExistingParent.end()) {
const std::vector<Key>::iterator eit = qBinaryFind(nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>());
if (eit != nexIt->second.end()) {
nexIt->second.erase(eit);
}
if (nexIt->second.empty()) {
mKeysByNonExistingParent.erase(nexIt);
}
}
const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
if (exIt != mKeysByExistingParent.end()) {
const std::vector<Key>::iterator eit = qBinaryFind(exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>());
if (eit != exIt->second.end()) {
exIt->second.erase(eit);
}
if (exIt->second.empty()) {
mKeysByExistingParent.erase(exIt);
}
}
}
endRemoveRows();
}
+KeyGroup HierarchicalKeyListModel::doMapToGroup(const QModelIndex &idx) const
+{
+ Q_ASSERT(!"not implemented");
+ return KeyGroup();
+}
+
+QModelIndex HierarchicalKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
+{
+ Q_ASSERT(!"not implemented");
+ return QModelIndex();
+}
+
+QModelIndex HierarchicalKeyListModel::doAddGroup(const KeyGroup &group)
+{
+ Q_ASSERT(!"not implemented");
+ return QModelIndex();
+}
+
void AbstractKeyListModel::useKeyCache(bool value, bool secretOnly)
{
d->m_secretOnly = secretOnly;
d->m_useKeyCache = value;
if (value) {
setKeys(d->m_secretOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys());
} else {
setKeys(std::vector<Key>());
}
connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged, this, [this] { if (d->m_useKeyCache) { setKeys(d->m_secretOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys()); } });
}
// static
AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(QObject *p)
{
AbstractKeyListModel *const m = new FlatKeyListModel(p);
#ifdef KLEO_MODEL_TEST
new QAbstractItemModelTester(m, p);
#endif
return m;
}
// static
AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(QObject *p)
{
AbstractKeyListModel *const m = new HierarchicalKeyListModel(p);
#ifdef KLEO_MODEL_TEST
new QAbstractItemModelTester(m, p);
#endif
return m;
}
#include "keylistmodel.moc"
/*!
\fn AbstractKeyListModel::rowAboutToBeMoved( const QModelIndex & old_parent, int old_row )
Emitted before the removal of a row from that model. It will later
be added to the model again, in response to which rowMoved() will be
emitted. If multiple rows are moved in one go, multiple
rowAboutToBeMoved() signals are emitted before the corresponding
number of rowMoved() signals is emitted - in reverse order.
This works around the absence of move semantics in
QAbstractItemModel. Clients can maintain a stack to perform the
QModelIndex-mapping themselves, or, e.g., to preserve the selection
status of the row:
\code
std::vector<bool> mMovingRowWasSelected; // transient, used when rows are moved
// ...
void slotRowAboutToBeMoved( const QModelIndex & p, int row ) {
mMovingRowWasSelected.push_back( selectionModel()->isSelected( model()->index( row, 0, p ) ) );
}
void slotRowMoved( const QModelIndex & p, int row ) {
const bool wasSelected = mMovingRowWasSelected.back();
mMovingRowWasSelected.pop_back();
if ( wasSelected )
selectionModel()->select( model()->index( row, 0, p ), Select|Rows );
}
\endcode
A similar mechanism could be used to preserve the current item during moves.
*/
/*!
\fn AbstractKeyListModel::rowMoved( const QModelIndex & new_parent, int new_parent )
See rowAboutToBeMoved()
*/
diff --git a/src/models/keylistmodel.h b/src/models/keylistmodel.h
index 1a6044f2..171ac12b 100644
--- a/src/models/keylistmodel.h
+++ b/src/models/keylistmodel.h
@@ -1,96 +1,123 @@
/* -*- mode: c++; c-basic-offset:4 -*-
models/keylistmodel.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
+ SPDX-FileCopyrightText: 2021 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __KLEOPATRA_MODELS_KEYLISTMODEL_H__
#define __KLEOPATRA_MODELS_KEYLISTMODEL_H__
#include <QAbstractItemModel>
#include <kleo_export.h>
#include "keylistmodelinterface.h"
#include <vector>
namespace GpgME
{
class Key;
}
namespace Kleo
{
class KLEO_EXPORT AbstractKeyListModel : public QAbstractItemModel
, public KeyListModelInterface
{
Q_OBJECT
public:
+ enum ItemType {
+ Keys = 0x01,
+ Groups = 0x02,
+ All = Keys | Groups
+ };
+ Q_DECLARE_FLAGS(ItemTypes, ItemType)
+
explicit AbstractKeyListModel(QObject *parent = nullptr);
~AbstractKeyListModel() override;
static AbstractKeyListModel *createFlatKeyListModel(QObject *parent = nullptr);
static AbstractKeyListModel *createHierarchicalKeyListModel(QObject *parent = nullptr);
GpgME::Key key(const QModelIndex &idx) const override;
std::vector<GpgME::Key> keys(const QList<QModelIndex> &indexes) const override;
+ KeyGroup group(const QModelIndex &idx) const override;
+
using QAbstractItemModel::index;
QModelIndex index(const GpgME::Key &key) const override;
QModelIndex index(const GpgME::Key &key, int col) const;
QList<QModelIndex> indexes(const std::vector<GpgME::Key> &keys) const override;
+ QModelIndex index(const KeyGroup &group) const override;
+ QModelIndex index(const KeyGroup &group, int col) const;
+
Q_SIGNALS:
void rowAboutToBeMoved(const QModelIndex &old_parent, int old_row);
void rowMoved(const QModelIndex &new_parent, int new_row);
public Q_SLOTS:
void setKeys(const std::vector<GpgME::Key> &keys);
/* Set this to set all or only secret keys from the keycache. */
void useKeyCache(bool value, bool secretOnly);
QModelIndex addKey(const GpgME::Key &key);
QList<QModelIndex> addKeys(const std::vector<GpgME::Key> &keys);
void removeKey(const GpgME::Key &key);
- void clear();
+
+ QModelIndex addGroup(const KeyGroup &group);
+
+ void clear(ItemTypes types = All);
public:
int columnCount(const QModelIndex &pidx) const override;
QVariant headerData(int section, Qt::Orientation o, int role = Qt::DisplayRole) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
/**
* defines which information is displayed in tooltips
* see Kleo::Formatting::ToolTipOption
*/
int toolTipOptions() const;
void setToolTipOptions(int opts);
/**
* Set the keys to use for KeyListModelInterface::Remark column
* to obtain remarks from this keys signature notations.
* Needs at least GpgME 1.14 to work properly. Remarks are
* joined by a semicolon and a space. */
void setRemarkKeys(const std::vector<GpgME::Key> &remarkKeys);
std::vector<GpgME::Key> remarkKeys() const;
private:
+ QVariant data(const GpgME::Key &key, int column, int role) const;
+ QVariant data(const KeyGroup &group, int column, int role) const;
+
virtual GpgME::Key doMapToKey(const QModelIndex &index) const = 0;
virtual QModelIndex doMapFromKey(const GpgME::Key &key, int column) const = 0;
virtual QList<QModelIndex> doAddKeys(const std::vector<GpgME::Key> &keys) = 0;
virtual void doRemoveKey(const GpgME::Key &key) = 0;
- virtual void doClear() = 0;
+
+ virtual KeyGroup doMapToGroup(const QModelIndex &index) const = 0;
+ virtual QModelIndex doMapFromGroup(const KeyGroup &group, int column) const = 0;
+ virtual QModelIndex doAddGroup(const KeyGroup &group) = 0;
+
+ virtual void doClear(ItemTypes types) = 0;
private:
class Private;
QScopedPointer<Private> const d;
};
}
+Q_DECLARE_OPERATORS_FOR_FLAGS(Kleo::AbstractKeyListModel::ItemTypes)
+
#endif /* __KLEOPATRA_MODELS_KEYLISTMODEL_H__ */
diff --git a/src/models/keylistmodelinterface.h b/src/models/keylistmodelinterface.h
index 5eab35e3..55cd18b5 100644
--- a/src/models/keylistmodelinterface.h
+++ b/src/models/keylistmodelinterface.h
@@ -1,42 +1,48 @@
/* -*- mode: c++; c-basic-offset:4 -*-
models/keylistmodelinterface.h
- This file is part of Kleopatra, the KDE keymanager
+ This file is part of libkleopatra, the KDE keymanagement library
SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB
+ SPDX-FileCopyrightText: 2021 g10 Code GmbH
+ SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef __KLEOPATRA_MODELS_KEYLISTMODELINTERFACE_H__
#define __KLEOPATRA_MODELS_KEYLISTMODELINTERFACE_H__
#include <vector>
#include <kleo_export.h>
namespace GpgME
{
class Key;
}
class QModelIndex;
template <typename T> class QList;
namespace Kleo
{
+class KeyGroup;
class KLEO_EXPORT KeyListModelInterface
{
public:
virtual ~KeyListModelInterface();
virtual GpgME::Key key(const QModelIndex &idx) const = 0;
virtual std::vector<GpgME::Key> keys(const QList<QModelIndex> &idxs) const = 0;
virtual QModelIndex index(const GpgME::Key &key) const = 0;
virtual QList<QModelIndex> indexes(const std::vector<GpgME::Key> &keys) const = 0;
+
+ virtual KeyGroup group(const QModelIndex &idx) const = 0;
+ virtual QModelIndex index(const KeyGroup &group) const = 0;
};
}
#endif /* __KLEOPATRA_MODELS_KEYLISTMODELINTERFACE_H__ */
diff --git a/src/models/keylistsortfilterproxymodel.cpp b/src/models/keylistsortfilterproxymodel.cpp
index 37c1cde6..7975e6c1 100644
--- a/src/models/keylistsortfilterproxymodel.cpp
+++ b/src/models/keylistsortfilterproxymodel.cpp
@@ -1,215 +1,237 @@
/* -*- mode: c++; c-basic-offset:4 -*-
models/keylistsortfilterproxymodel.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "keylistsortfilterproxymodel.h"
#include "keylist.h"
#include "keylistmodel.h"
#include "kleo/keyfilter.h"
+#include "kleo/keygroup.h"
#include "kleo/stl_util.h"
#include <libkleo_debug.h>
#include <gpgme++/key.h>
#include <gpgme++/gpgmepp_version.h>
#if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
# define GPGME_HAS_REMARKS
#endif
using namespace Kleo;
using namespace GpgME;
AbstractKeyListSortFilterProxyModel::AbstractKeyListSortFilterProxyModel(QObject *p)
: QSortFilterProxyModel(p), KeyListModelInterface()
{
init();
}
AbstractKeyListSortFilterProxyModel::AbstractKeyListSortFilterProxyModel(const AbstractKeyListSortFilterProxyModel &other)
: QSortFilterProxyModel(), KeyListModelInterface()
{
Q_UNUSED(other)
init();
}
void AbstractKeyListSortFilterProxyModel::init()
{
setDynamicSortFilter(true);
setSortRole(Qt::EditRole); // EditRole can be expected to be in a less formatted way, better for sorting
setFilterRole(Qt::DisplayRole);
setFilterCaseSensitivity(Qt::CaseInsensitive);
}
AbstractKeyListSortFilterProxyModel::~AbstractKeyListSortFilterProxyModel() {}
Key AbstractKeyListSortFilterProxyModel::key(const QModelIndex &idx) const
{
const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(sourceModel());
if (!klmi) {
static Key null;
return null;
}
return klmi->key(mapToSource(idx));
}
std::vector<Key> AbstractKeyListSortFilterProxyModel::keys(const QList<QModelIndex> &indexes) const
{
const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(sourceModel());
if (!klmi) {
return std::vector<Key>();
}
QList<QModelIndex> mapped;
mapped.reserve(indexes.size());
std::transform(indexes.begin(), indexes.end(),
std::back_inserter(mapped),
[this](const QModelIndex &idx) {
return mapToSource(idx);
});
return klmi->keys(mapped);
}
+KeyGroup AbstractKeyListSortFilterProxyModel::group(const QModelIndex &idx) const
+{
+ if (const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(sourceModel())) {
+ return klmi->group(mapToSource(idx));
+ }
+ return KeyGroup();
+}
+
QModelIndex AbstractKeyListSortFilterProxyModel::index(const Key &key) const
{
if (const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(sourceModel())) {
return mapFromSource(klmi->index(key));
- } else {
- return {};
}
+ return {};
}
QList<QModelIndex> AbstractKeyListSortFilterProxyModel::indexes(const std::vector<Key> &keys) const
{
if (const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(sourceModel())) {
const QList<QModelIndex> source = klmi->indexes(keys);
QList<QModelIndex> mapped;
mapped.reserve(source.size());
std::transform(source.begin(), source.end(),
std::back_inserter(mapped),
[this](const QModelIndex &idx) {
return mapFromSource(idx);
});
return mapped;
- } else {
- return QList<QModelIndex>();
}
+ return QList<QModelIndex>();
+}
+
+QModelIndex AbstractKeyListSortFilterProxyModel::index(const Kleo::KeyGroup& group) const
+{
+ if (const KeyListModelInterface *const klmi = dynamic_cast<KeyListModelInterface *>(sourceModel())) {
+ return mapFromSource(klmi->index(group));
+ }
+ return {};
}
class KeyListSortFilterProxyModel::Private
{
friend class ::Kleo::KeyListSortFilterProxyModel;
public:
explicit Private()
: keyFilter() {}
~Private() {}
private:
std::shared_ptr<const KeyFilter> keyFilter;
};
KeyListSortFilterProxyModel::KeyListSortFilterProxyModel(QObject *p)
: AbstractKeyListSortFilterProxyModel(p), d(new Private)
{
}
KeyListSortFilterProxyModel::KeyListSortFilterProxyModel(const KeyListSortFilterProxyModel &other)
: AbstractKeyListSortFilterProxyModel(other), d(new Private(*other.d))
{
}
KeyListSortFilterProxyModel::~KeyListSortFilterProxyModel() {}
KeyListSortFilterProxyModel *KeyListSortFilterProxyModel::clone() const
{
return new KeyListSortFilterProxyModel(*this);
}
std::shared_ptr<const KeyFilter> KeyListSortFilterProxyModel::keyFilter() const
{
return d->keyFilter;
}
void KeyListSortFilterProxyModel::setKeyFilter(const std::shared_ptr<const KeyFilter> &kf)
{
if (kf == d->keyFilter) {
return;
}
d->keyFilter = kf;
invalidate();
}
bool KeyListSortFilterProxyModel::filterAcceptsRow(int source_row, const QModelIndex &source_parent) const
{
-
//
// 0. Keep parents of matching children:
//
const QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
for (int i = 0, end = sourceModel()->rowCount(index); i != end; ++i)
if (filterAcceptsRow(i, index)) {
return true;
}
//
// 1. Check filterRegExp
//
const int role = filterRole();
const int col = filterKeyColumn();
const QRegExp rx = filterRegExp();
const QModelIndex nameIndex = sourceModel()->index(source_row, KeyList::PrettyName, source_parent);
const KeyListModelInterface *const klm = dynamic_cast<KeyListModelInterface *>(sourceModel());
Q_ASSERT(klm);
const Key key = klm->key(nameIndex);
+ const KeyGroup group = klm->group(nameIndex);
+ Q_ASSERT(!key.isNull() || !group.isNull());
if (col) {
const QModelIndex colIdx = sourceModel()->index(source_row, col, source_parent);
const QString content = colIdx.data(role).toString();
if (!content.contains(rx)) {
return false;
}
- } else {
+ } else if (!key.isNull()) {
// By default match against the full uid data (name / email / comment / dn)
bool match = false;
for (const auto &uid: key.userIDs()) {
const auto id = QString::fromUtf8(uid.id());
if (id.contains(rx)) {
match = true;
break;
}
#ifdef GPGME_HAS_REMARKS
// Also match against remarks (search tags)
const auto alm = dynamic_cast<AbstractKeyListModel *>(sourceModel());
if (alm) {
const auto remarks = alm->data(alm->index(key, KeyList::Remarks));
if (!remarks.isNull() && remarks.toString().contains(rx)) {
match = true;
break;
}
}
#endif
}
if (!match) {
return false;
}
+ } else if (!group.isNull()) {
+ if (!group.name().contains(rx)) {
+ return false;
+ }
+ } else {
+ return false;
}
//
- // 2. Check that key filters match (if any are defined)
+ // 2. For keys check that key filters match (if any are defined)
//
- if (d->keyFilter) { // avoid artifacts when no filters are defined
+ if (d->keyFilter && !key.isNull()) { // avoid artifacts when no filters are defined
return d->keyFilter->matches(key, KeyFilter::Filtering);
}
// 3. match by default:
return true;
}
diff --git a/src/models/keylistsortfilterproxymodel.h b/src/models/keylistsortfilterproxymodel.h
index 495429a1..ae74bbad 100644
--- a/src/models/keylistsortfilterproxymodel.h
+++ b/src/models/keylistsortfilterproxymodel.h
@@ -1,78 +1,82 @@
/* -*- mode: c++; c-basic-offset:4 -*-
models/keylistsortfilterproxymodel.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
*/
#ifndef __KLEOPATRA_MODELS_KEYLISTSORTFILTERPROXYMODEL_H__
#define __KLEOPATRA_MODELS_KEYLISTSORTFILTERPROXYMODEL_H__
#include <QSortFilterProxyModel>
#include "keylistmodelinterface.h"
#include <kleo_export.h>
#include <memory>
namespace GpgME
{
class Key;
}
namespace Kleo
{
class KeyFilter;
class KLEO_EXPORT AbstractKeyListSortFilterProxyModel : public QSortFilterProxyModel
, public KeyListModelInterface
{
Q_OBJECT
protected:
AbstractKeyListSortFilterProxyModel(const AbstractKeyListSortFilterProxyModel &);
public:
explicit AbstractKeyListSortFilterProxyModel(QObject *parent = nullptr);
~AbstractKeyListSortFilterProxyModel() override;
virtual AbstractKeyListSortFilterProxyModel *clone() const = 0;
GpgME::Key key(const QModelIndex &idx) const override;
std::vector<GpgME::Key> keys(const QList<QModelIndex> &indexes) const override;
+ KeyGroup group(const QModelIndex &idx) const override;
+
using QAbstractItemModel::index;
QModelIndex index(const GpgME::Key &key) const override;
QList<QModelIndex> indexes(const std::vector<GpgME::Key> &keys) const override;
+ QModelIndex index(const KeyGroup &group) const override;
+
private:
void init();
};
class KLEO_EXPORT KeyListSortFilterProxyModel : public AbstractKeyListSortFilterProxyModel
{
Q_OBJECT
protected:
KeyListSortFilterProxyModel(const KeyListSortFilterProxyModel &);
public:
explicit KeyListSortFilterProxyModel(QObject *parent = nullptr);
~KeyListSortFilterProxyModel() override;
std::shared_ptr<const KeyFilter> keyFilter() const;
void setKeyFilter(const std::shared_ptr<const KeyFilter> &kf);
KeyListSortFilterProxyModel *clone() const override;
protected:
bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override;
private:
class Private;
QScopedPointer<Private> const d;
};
}
#endif /* __KLEOPATRA_MODELS_KEYLISTMODEL_H__ */
diff --git a/src/models/keyrearrangecolumnsproxymodel.cpp b/src/models/keyrearrangecolumnsproxymodel.cpp
index 1e22c7e5..6426a327 100644
--- a/src/models/keyrearrangecolumnsproxymodel.cpp
+++ b/src/models/keyrearrangecolumnsproxymodel.cpp
@@ -1,73 +1,84 @@
/* models/keyrearangecolumnsproxymodel.cpp
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "keyrearrangecolumnsproxymodel.h"
+#include "kleo/keygroup.h"
+
#include <gpgme++/key.h>
using namespace Kleo;
using namespace GpgME;
KeyRearrangeColumnsProxyModel::KeyRearrangeColumnsProxyModel(QObject *parent) :
KRearrangeColumnsProxyModel(parent),
KeyListModelInterface()
{
}
KeyListModelInterface *KeyRearrangeColumnsProxyModel::klm() const
{
auto *ret = dynamic_cast<KeyListModelInterface *>(sourceModel());
Q_ASSERT(ret);
return ret;
}
Key KeyRearrangeColumnsProxyModel::key(const QModelIndex &idx) const
{
return klm()->key(mapToSource(idx));
}
std::vector<GpgME::Key> KeyRearrangeColumnsProxyModel::keys(const QList<QModelIndex> &idxs) const
{
QList<QModelIndex> srcIdxs;
srcIdxs.reserve(idxs.count());
for (const QModelIndex &idx : idxs) {
srcIdxs << mapToSource(idx);
}
return klm()->keys(srcIdxs);
}
+KeyGroup KeyRearrangeColumnsProxyModel::group(const QModelIndex &idx) const
+{
+ return klm()->group(mapToSource(idx));
+}
QModelIndex KeyRearrangeColumnsProxyModel::index(const GpgME::Key &key) const
{
return mapFromSource(klm()->index(key));
}
QList<QModelIndex> KeyRearrangeColumnsProxyModel::indexes(const std::vector<GpgME::Key> &keys) const
{
QList<QModelIndex> myIdxs;
const QList <QModelIndex> srcIdxs = klm()->indexes(keys);
myIdxs.reserve(srcIdxs.count());
for (const QModelIndex &idx : srcIdxs) {
myIdxs << mapFromSource(idx);
}
return myIdxs;
}
+QModelIndex KeyRearrangeColumnsProxyModel::index(const KeyGroup &group) const
+{
+ return mapFromSource(klm()->index(group));
+}
+
void KeyRearrangeColumnsProxyModel::sort(int column, Qt::SortOrder order)
{
const auto fakeIdx = createIndex(0, column);
if (!fakeIdx.isValid()) {
// Empty model?
KRearrangeColumnsProxyModel::sort(column, order);
return;
}
const auto remappedIdx = mapToSource(fakeIdx);
KRearrangeColumnsProxyModel::sort(remappedIdx.column(), order);
}
diff --git a/src/models/keyrearrangecolumnsproxymodel.h b/src/models/keyrearrangecolumnsproxymodel.h
index 0d03028f..273e0e26 100644
--- a/src/models/keyrearrangecolumnsproxymodel.h
+++ b/src/models/keyrearrangecolumnsproxymodel.h
@@ -1,41 +1,45 @@
/* models/keyrearangecolumnsproxymodel.h
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
SPDX-FileContributor: Intevation GmbH
SPDX-License-Identifier: GPL-2.0-or-later
*/
#ifndef KEYREARRANGECOLUMNSPROXYMODEL_H
#define KEYREARRANGECOLUMNSPROXYMODEL_H
#include "keylistmodelinterface.h"
#include <kleo_export.h>
#include <KRearrangeColumnsProxyModel>
namespace Kleo
{
/** KRearrangeColumnsProxymodel that implements the KeyListModelInterface. */
class KLEO_EXPORT KeyRearrangeColumnsProxyModel: public KRearrangeColumnsProxyModel,
public KeyListModelInterface
{
public:
explicit KeyRearrangeColumnsProxyModel(QObject *parent = nullptr);
GpgME::Key key(const QModelIndex &idx) const override;
std::vector<GpgME::Key> keys(const QList<QModelIndex> &idxs) const override;
+ KeyGroup group(const QModelIndex &idx) const override;
+
using KRearrangeColumnsProxyModel::index;
QModelIndex index(const GpgME::Key &key) const override;
QList<QModelIndex> indexes(const std::vector<GpgME::Key> &keys) const override;
+ QModelIndex index(const KeyGroup &group) const override;
+
void sort(int column, Qt::SortOrder order = Qt::AscendingOrder) override;
private:
KeyListModelInterface *klm() const;
};
} // namespace Kleo
#endif // KEYREARRANGECOLUMNSPROXYMODEL_H

File Metadata

Mime Type
text/x-diff
Expires
Mon, Dec 23, 4:05 PM (14 h, 27 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
94/73/65feef6a5b9aa67f06d59f8feb18

Event Timeline