12#include <config-libkleo.h>
14#include "keylistmodel.h"
18#include <libkleo/algorithm.h>
19#include <libkleo/formatting.h>
20#include <libkleo/keyfilter.h>
21#include <libkleo/keyfiltermanager.h>
22#include <libkleo/predicates.h>
23#include <libkleo/systeminfo.h>
25#include <KLocalizedString>
28#include <QAbstractItemModelTester>
37#include <gpgme++/key.h>
40#include <boost/graph/adjacency_list.hpp>
41#include <boost/graph/topological_sort.hpp>
51using namespace Kleo::KeyList;
54Q_DECLARE_METATYPE(GpgME::Key)
55Q_DECLARE_METATYPE(KeyGroup)
58class AbstractKeyListModel::Private
60 AbstractKeyListModel *
const q;
63 explicit Private(AbstractKeyListModel *qq);
65 void updateFromKeyCache();
67 QString getEMail(
const Key &key)
const;
70 int m_toolTipOptions = Formatting::Validity;
73 bool m_useKeyCache =
false;
74 bool m_modelResetInProgress =
false;
75 KeyList::Options m_keyListOptions = AllKeys;
76 std::vector<GpgME::Key> m_remarkKeys;
77 std::shared_ptr<DragHandler> m_dragHandler;
78 std::vector<Key::Origin> extraOrigins;
81AbstractKeyListModel::Private::Private(Kleo::AbstractKeyListModel *qq)
86void AbstractKeyListModel::Private::updateFromKeyCache()
89 const bool inReset = q->modelResetInProgress();
93 q->setKeys(m_keyListOptions == SecretKeysOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys());
94 if (m_keyListOptions == IncludeGroups) {
95 q->setGroups(KeyCache::instance()->groups());
103QString AbstractKeyListModel::Private::getEMail(
const Key &key)
const
106 if (
const auto fpr = key.primaryFingerprint()) {
107 const auto it = prettyEMailCache.constFind(fpr);
108 if (it != prettyEMailCache.constEnd()) {
111 email = Formatting::prettyEMail(key);
112 prettyEMailCache[fpr] = email;
118AbstractKeyListModel::AbstractKeyListModel(
QObject *p)
120 , KeyListModelInterface()
121 , d(new Private(this))
124 d->m_modelResetInProgress =
true;
127 d->m_modelResetInProgress =
false;
131AbstractKeyListModel::~AbstractKeyListModel()
135void AbstractKeyListModel::setToolTipOptions(
int opts)
137 d->m_toolTipOptions = opts;
140int AbstractKeyListModel::toolTipOptions()
const
142 return d->m_toolTipOptions;
145void AbstractKeyListModel::setRemarkKeys(
const std::vector<GpgME::Key> &keys)
147 d->m_remarkKeys = keys;
150std::vector<GpgME::Key> AbstractKeyListModel::remarkKeys()
const
152 return d->m_remarkKeys;
155Key AbstractKeyListModel::key(
const QModelIndex &idx)
const
159 key = doMapToKey(idx);
166 std::vector<Key> result;
167 result.reserve(indexes.
size());
168 std::transform(indexes.
begin(),
170 std::back_inserter(result),
172 return this->key(idx);
174 result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&GpgME::Key::isNull)), result.end());
175 _detail::remove_duplicates_by_fpr(result);
179KeyGroup AbstractKeyListModel::group(
const QModelIndex &idx)
const
182 return doMapToGroup(idx);
188QModelIndex AbstractKeyListModel::index(
const Key &key)
const
190 return index(key, 0);
193QModelIndex AbstractKeyListModel::index(
const Key &key,
int col)
const
195 if (key.isNull() || col < 0 || col >= NumColumns) {
198 return doMapFromKey(key, col);
206 std::transform(keys.begin(),
208 std::back_inserter(result),
209 [
this](
const Key &key) {
210 return this->index(key);
215QModelIndex AbstractKeyListModel::index(
const KeyGroup &group)
const
217 return index(group, 0);
220QModelIndex AbstractKeyListModel::index(
const KeyGroup &group,
int col)
const
222 if (group.isNull() || col < 0 || col >= NumColumns) {
225 return doMapFromGroup(group, col);
229void AbstractKeyListModel::setKeys(
const std::vector<Key> &keys,
const std::vector<Key::Origin> &extraOrigins)
231 const bool inReset = modelResetInProgress();
237 d->extraOrigins = extraOrigins;
243QModelIndex AbstractKeyListModel::addKey(
const Key &key)
245 const std::vector<Key> vec(1, key);
250void AbstractKeyListModel::removeKey(
const Key &key)
256 d->prettyEMailCache.remove(key.primaryFingerprint());
257 d->remarksCache.remove(key.primaryFingerprint());
262 std::vector<Key> sorted;
264 std::remove_copy_if(keys.begin(), keys.end(), std::back_inserter(sorted), std::mem_fn(&Key::isNull));
265 std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
266 return doAddKeys(sorted);
269void AbstractKeyListModel::setGroups(
const std::vector<KeyGroup> &groups)
271 const bool inReset = modelResetInProgress();
282QModelIndex AbstractKeyListModel::addGroup(
const KeyGroup &group)
284 if (group.isNull()) {
287 return doAddGroup(group);
290bool AbstractKeyListModel::removeGroup(
const KeyGroup &group)
292 if (group.isNull()) {
295 return doRemoveGroup(group);
298void AbstractKeyListModel::clear(ItemTypes types)
300 const bool inReset = modelResetInProgress();
306 d->prettyEMailCache.clear();
307 d->remarksCache.clear();
314int AbstractKeyListModel::columnCount(
const QModelIndex &)
const
325 return i18nc(
"@title:column",
"Name");
327 return i18nc(
"@title:column",
"E-Mail");
329 return i18nc(
"@title:column",
"Status");
331 return i18nc(
"@title:column",
"Valid From");
333 return i18nc(
"@title:column",
"Valid Until");
334 case TechnicalDetails:
335 return i18nc(
"@title:column",
"Protocol");
337 return i18nc(
"@title:column",
"Key ID");
339 return i18nc(
"@title:column",
"Key ID");
341 return i18nc(
"@title:column",
"Fingerprint");
343 return i18nc(
"@title:column",
"Issuer");
345 return i18nc(
"@title:column",
"Serial Number");
347 return i18nc(
"@title:column",
"Origin");
349 return i18nc(
"@title:column",
"Last Update");
351 return i18nc(
"@title:column",
"Certification Trust");
353 return i18nc(
"@title:column",
"Tags");
355 return i18nc(
"@title:column",
"Algorithm");
357 return i18nc(
"@title:column",
"Keygrip");
385 const Key key = this->key(index);
387 return data(key, index.
row(), index.
column(), role);
390 const KeyGroup group = this->group(index);
391 if (!group.isNull()) {
392 return data(group, index.
column(), role);
398QVariant AbstractKeyListModel::data(
const Key &key,
int row,
int column,
int role)
const
403 const auto name = Formatting::prettyName(key);
405 return name.isEmpty() ?
i18nc(
"text for screen readers for an empty name",
"no name") : name;
410 const auto email = d->getEMail(key);
412 return email.isEmpty() ?
i18nc(
"text for screen readers for an empty email address",
"no email") : email;
417 return Formatting::complianceStringShort(key);
420 return Formatting::creationDate(key);
422 return Formatting::accessibleCreationDate(key);
424 return Formatting::creationDateString(key);
428 return Formatting::expirationDate(key);
430 return Formatting::accessibleExpirationDate(key);
432 return Formatting::expirationDateString(key);
434 case TechnicalDetails:
435 return Formatting::type(key);
438 return Formatting::accessibleHexID(key.shortKeyID());
439 }
else if (role == ClipboardRole) {
442 return Formatting::prettyID(key.shortKeyID());
446 return Formatting::accessibleHexID(key.keyID());
447 }
else if (role == ClipboardRole) {
450 return Formatting::prettyID(key.keyID());
453 return Formatting::summaryLine(key);
456 return Formatting::accessibleHexID(key.primaryFingerprint());
457 }
else if (role == ClipboardRole) {
460 return Formatting::prettyID(key.primaryFingerprint());
465 if (key.origin() == Key::OriginUnknown && (
int)d->extraOrigins.size() > row) {
466 return Formatting::origin(d->extraOrigins[row]);
468 return Formatting::origin(key.origin());
471 return Formatting::accessibleDate(key.lastUpdate());
473 return Formatting::dateString(key.lastUpdate());
478 return Formatting::ownerTrustShort(key.ownerTrust());
480 const char *
const fpr = key.primaryFingerprint();
481 if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() && d->m_remarkKeys.size()) {
482 if (!(key.keyListMode() & GpgME::SignatureNotations)) {
483 return i18n(
"Loading...");
486 if (it != d->remarksCache.constEnd()) {
490 const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err);
491 if (remarks.size() == 1) {
493 return d->remarksCache[fpr] = remark;
496 remarkList.
reserve(remarks.size());
497 for (
const auto &rem : remarks) {
500 const auto remark = remarkList.
join(QStringLiteral(
"; "));
501 return d->remarksCache[fpr] = remark;
510 return Formatting::prettyAlgorithmName(key.subkey(0).algoName());
513 return Formatting::accessibleHexID(key.subkey(0).keyGrip());
514 }
else if (role == ClipboardRole) {
517 return Formatting::prettyID(key.subkey(0).keyGrip());
523 return Formatting::toolTip(key, toolTipOptions());
525 return KeyFilterManager::instance()->font(key,
526 (column == ShortKeyID || column == KeyID || column == Fingerprint) ?
QFont(QStringLiteral(
"monospace"))
529 return column ==
Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) :
QVariant();
531 if (!SystemInfo::isHighContrastModeActive()) {
532 return returnIfValid(KeyFilterManager::instance()->bgColor(key));
535 if (!SystemInfo::isHighContrastModeActive()) {
536 return returnIfValid(KeyFilterManager::instance()->fgColor(key));
538 }
else if (role == FingerprintRole) {
540 }
else if (role == KeyRole) {
546QVariant AbstractKeyListModel::data(
const KeyGroup &group,
int column,
int role)
const
553 return Formatting::complianceStringShort(group);
554 case TechnicalDetails:
555 return Formatting::type(group);
557 return Formatting::summaryLine(group);
571 return i18nc(
"text for screen readers",
"not applicable");
578 return Formatting::toolTip(group, toolTipOptions());
582 if (column !=
Icon && column != Summary) {
585 return Kleo::all_of(group.keys(),
586 [](
const auto &key) {
587 return key.hasEncrypt();
590 :
QIcon::fromTheme(QStringLiteral(
"emblem-warning"));
593 }
else if (role == GroupRole) {
599bool AbstractKeyListModel::setData(
const QModelIndex &index,
const QVariant &value,
int role)
604 const KeyGroup group = value.
value<KeyGroup>();
605 return doSetGroupData(index, group);
611bool AbstractKeyListModel::modelResetInProgress()
613 return d->m_modelResetInProgress;
618template<
typename Base>
619class TableModelMixin :
public Base
622 explicit TableModelMixin(
QObject *p =
nullptr)
626 ~TableModelMixin()
override
633 return this->hasIndex(row, column, pidx) ? this->createIndex(row, column,
nullptr) :
QModelIndex();
641 bool hasChildren(
const QModelIndex &pidx)
const override
643 return (pidx.
model() ==
this || !pidx.
isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0;
647class FlatKeyListModel
649 :
public TableModelMixin<AbstractKeyListModel>
651 :
public AbstractKeyListModel
656 explicit FlatKeyListModel(
QObject *parent =
nullptr);
657 ~FlatKeyListModel()
override;
659 int rowCount(
const QModelIndex &pidx)
const override
661 return pidx.
isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size();
665 Key doMapToKey(
const QModelIndex &index)
const override;
666 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
668 void doRemoveKey(
const Key &key)
override;
670 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
671 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
672 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
673 QModelIndex doAddGroup(
const KeyGroup &group)
override;
674 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
675 bool doRemoveGroup(
const KeyGroup &group)
override;
677 void doClear(ItemTypes types)
override
680 mKeysByFingerprint.clear();
682 if (types & Groups) {
687 int firstGroupRow()
const
689 return mKeysByFingerprint.size();
692 int lastGroupRow()
const
694 return mKeysByFingerprint.size() + mGroups.size() - 1;
699 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
702 return index.
row() - firstGroupRow();
706 std::vector<Key> mKeysByFingerprint;
707 std::vector<KeyGroup> mGroups;
710class HierarchicalKeyListModel :
public AbstractKeyListModel
714 explicit HierarchicalKeyListModel(
QObject *parent =
nullptr);
715 ~HierarchicalKeyListModel()
override;
717 int rowCount(
const QModelIndex &pidx)
const override;
718 using AbstractKeyListModel::index;
722 bool hasChildren(
const QModelIndex &pidx)
const override
724 return rowCount(pidx) > 0;
728 Key doMapToKey(
const QModelIndex &index)
const override;
729 QModelIndex doMapFromKey(
const Key &key,
int col)
const override;
731 void doRemoveKey(
const Key &key)
override;
733 KeyGroup doMapToGroup(
const QModelIndex &index)
const override;
734 QModelIndex doMapFromGroup(
const KeyGroup &group,
int column)
const override;
735 void doSetGroups(
const std::vector<KeyGroup> &groups)
override;
736 QModelIndex doAddGroup(
const KeyGroup &group)
override;
737 bool doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
override;
738 bool doRemoveGroup(
const KeyGroup &group)
override;
740 void doClear(ItemTypes types)
override;
742 int firstGroupRow()
const
744 return mTopLevels.size();
747 int lastGroupRow()
const
749 return mTopLevels.size() + mGroups.size() - 1;
754 if (!index.
isValid() || index.
row() < firstGroupRow() || index.
row() > lastGroupRow() || index.
column() >= NumColumns) {
757 return index.
row() - firstGroupRow();
761 void addTopLevelKey(
const Key &key);
762 void addKeyWithParent(
const char *issuer_fpr,
const Key &key);
763 void addKeyWithoutParent(
const char *issuer_fpr,
const Key &key);
766 typedef std::map<std::string, std::vector<Key>>
Map;
767 std::vector<Key> mKeysByFingerprint;
768 Map mKeysByExistingParent, mKeysByNonExistingParent;
769 std::vector<Key> mTopLevels;
770 std::vector<KeyGroup> mGroups;
780 static Issuers *instance()
782 static auto self = std::unique_ptr<Issuers>{
new Issuers{}};
786 const char *cleanChainID(
const Key &key)
const
788 const char *chainID =
"";
790 const char *
const chid = key.chainID();
791 if (chid && mKeysWithMaskedIssuer.find(key) == std::end(mKeysWithMaskedIssuer)) {
798 void maskIssuerOfKey(
const Key &key)
800 mKeysWithMaskedIssuer.insert(key);
805 mKeysWithMaskedIssuer.clear();
809 std::set<Key, _detail::ByFingerprint<std::less>> mKeysWithMaskedIssuer;
812static const char *cleanChainID(
const Key &key)
814 return Issuers::instance()->cleanChainID(key);
819FlatKeyListModel::FlatKeyListModel(
QObject *p)
820 : TableModelMixin<AbstractKeyListModel>(p)
824FlatKeyListModel::~FlatKeyListModel()
828Key FlatKeyListModel::doMapToKey(
const QModelIndex &idx)
const
831 if (
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() && idx.
column() < NumColumns) {
832 return mKeysByFingerprint[idx.
row()];
838QModelIndex FlatKeyListModel::doMapFromKey(
const Key &key,
int col)
const
840 Q_ASSERT(!key.isNull());
841 const std::vector<Key>::const_iterator it =
842 std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
843 if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
846 return createIndex(it - mKeysByFingerprint.begin(), col);
852 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
858 for (
auto it = keys.begin(), end = keys.end(); it !=
end; ++it) {
860 const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
861 const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
863 if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
865 mKeysByFingerprint[idx - 1] = *it;
866 if (!modelResetInProgress()) {
867 Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
871 if (!modelResetInProgress()) {
874 mKeysByFingerprint.insert(pos, *it);
875 if (!modelResetInProgress()) {
881 return indexes(keys);
884void FlatKeyListModel::doRemoveKey(
const Key &key)
886 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
887 if (it == mKeysByFingerprint.end()) {
891 const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
892 if (!modelResetInProgress()) {
895 mKeysByFingerprint.erase(it);
896 if (!modelResetInProgress()) {
901KeyGroup FlatKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
904 if (
static_cast<unsigned>(idx.
row()) >= mKeysByFingerprint.size() &&
static_cast<unsigned>(idx.
row()) < mKeysByFingerprint.size() + mGroups.size()
905 && idx.
column() < NumColumns) {
906 return mGroups[idx.
row() - mKeysByFingerprint.size()];
912QModelIndex FlatKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
914 Q_ASSERT(!group.isNull());
915 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
916 return g.source() == group.source() && g.id() == group.id();
918 if (it == mGroups.cend()) {
921 return createIndex(it - mGroups.cbegin() + mKeysByFingerprint.size(), column);
925void FlatKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
927 Q_ASSERT(mGroups.empty());
928 const int first = mKeysByFingerprint.size();
929 const int last = first + groups.size() - 1;
930 if (!modelResetInProgress()) {
934 if (!modelResetInProgress()) {
939QModelIndex FlatKeyListModel::doAddGroup(
const KeyGroup &group)
941 const int newRow = lastGroupRow() + 1;
942 if (!modelResetInProgress()) {
945 mGroups.push_back(group);
946 if (!modelResetInProgress()) {
949 return createIndex(newRow, 0);
952bool FlatKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
954 if (group.isNull()) {
957 const int groupIndex = this->groupIndex(index);
958 if (groupIndex == -1) {
961 mGroups[groupIndex] = group;
962 if (!modelResetInProgress()) {
963 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
968bool FlatKeyListModel::doRemoveGroup(
const KeyGroup &group)
970 const QModelIndex modelIndex = doMapFromGroup(group, 0);
974 const int groupIndex = this->groupIndex(modelIndex);
975 Q_ASSERT(groupIndex != -1);
976 if (groupIndex == -1) {
979 if (!modelResetInProgress()) {
982 mGroups.erase(mGroups.begin() + groupIndex);
983 if (!modelResetInProgress()) {
989HierarchicalKeyListModel::HierarchicalKeyListModel(
QObject *p)
990 : AbstractKeyListModel(p)
991 , mKeysByFingerprint()
992 , mKeysByExistingParent()
993 , mKeysByNonExistingParent()
998HierarchicalKeyListModel::~HierarchicalKeyListModel()
1002int HierarchicalKeyListModel::rowCount(
const QModelIndex &pidx)
const
1006 return mTopLevels.size() + mGroups.size();
1009 if (pidx.
column() != 0) {
1014 const Key issuer = this->key(pidx);
1015 const char *
const fpr = issuer.primaryFingerprint();
1016 if (!fpr || !*fpr) {
1019 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1020 if (it == mKeysByExistingParent.end()) {
1023 return it->second.size();
1028 if (row < 0 || col < 0 || col >= NumColumns) {
1034 if (
static_cast<unsigned>(row) < mTopLevels.size()) {
1035 return index(mTopLevels[row], col);
1036 }
else if (
static_cast<unsigned>(row) < mTopLevels.size() + mGroups.size()) {
1037 return index(mGroups[row - mTopLevels.size()], col);
1044 const Key issuer = this->key(pidx);
1045 const char *
const fpr = issuer.primaryFingerprint();
1046 if (!fpr || !*fpr) {
1049 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
1050 if (it == mKeysByExistingParent.end() ||
static_cast<unsigned>(row) >= it->second.size()) {
1053 return index(it->second[row], col);
1058 const Key key = this->key(idx);
1059 if (key.isNull() || key.isRoot()) {
1062 const std::vector<Key>::const_iterator it =
1063 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), cleanChainID(key), _detail::ByFingerprint<std::less>());
1064 return it != mKeysByFingerprint.end() ? index(*it) :
QModelIndex();
1067Key HierarchicalKeyListModel::doMapToKey(
const QModelIndex &idx)
const
1069 Key key = Key::null;
1072 const char *
const issuer_fpr =
static_cast<const char *
>(idx.
internalPointer());
1073 if (!issuer_fpr || !*issuer_fpr) {
1075 if (
static_cast<unsigned>(idx.
row()) < mTopLevels.size()) {
1076 key = mTopLevels[idx.
row()];
1080 const Map::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1081 if (it != mKeysByExistingParent.end() &&
static_cast<unsigned>(idx.
row()) < it->second.size()) {
1082 key = it->second[idx.
row()];
1090QModelIndex HierarchicalKeyListModel::doMapFromKey(
const Key &key,
int col)
const
1096 const char *issuer_fpr = cleanChainID(key);
1099 const std::vector<Key> *v = &mTopLevels;
1100 if (issuer_fpr && *issuer_fpr) {
1101 const std::map<std::string, std::vector<Key>>::const_iterator it = mKeysByExistingParent.find(issuer_fpr);
1103 if (it != mKeysByExistingParent.end()) {
1106 issuer_fpr =
nullptr;
1110 const std::vector<Key>::const_iterator it = std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
1111 if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
1115 const unsigned int row = std::distance(v->begin(), it);
1116 return createIndex(row, col,
const_cast<char *
>(issuer_fpr));
1119void HierarchicalKeyListModel::addKeyWithParent(
const char *issuer_fpr,
const Key &key)
1121 Q_ASSERT(issuer_fpr);
1122 Q_ASSERT(*issuer_fpr);
1123 Q_ASSERT(!key.isNull());
1125 std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
1128 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1129 const int row = std::distance(subjects.begin(), it);
1131 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1134 if (!modelResetInProgress()) {
1135 Q_EMIT dataChanged(createIndex(row, 0,
const_cast<char *
>(issuer_fpr)), createIndex(row, NumColumns - 1,
const_cast<char *
>(issuer_fpr)));
1139 const std::vector<Key>::const_iterator pos =
1140 Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1141 Q_ASSERT(pos != mKeysByFingerprint.end());
1142 if (!modelResetInProgress()) {
1143 beginInsertRows(index(*pos), row, row);
1145 subjects.insert(it, key);
1146 if (!modelResetInProgress()) {
1152void HierarchicalKeyListModel::addKeyWithoutParent(
const char *issuer_fpr,
const Key &key)
1154 Q_ASSERT(issuer_fpr);
1155 Q_ASSERT(*issuer_fpr);
1156 Q_ASSERT(!key.isNull());
1158 std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
1161 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1163 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1168 subjects.insert(it, key);
1171 addTopLevelKey(key);
1174void HierarchicalKeyListModel::addTopLevelKey(
const Key &key)
1177 const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1178 const int row = std::distance(mTopLevels.begin(), it);
1180 if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1183 if (!modelResetInProgress()) {
1184 Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
1188 if (!modelResetInProgress()) {
1191 mTopLevels.insert(it, key);
1192 if (!modelResetInProgress()) {
1202struct cycle_detector :
public boost::dfs_visitor<> {
1203 cycle_detector(
bool &has_cycle)
1204 : _has_cycle{has_cycle}
1208 template<
class Edge,
class Graph>
1209 void back_edge(Edge, Graph &)
1218static bool graph_has_cycle(
const boost::adjacency_list<> &graph)
1220 bool cycle_found =
false;
1221 cycle_detector vis{cycle_found};
1222 boost::depth_first_search(graph, visitor(vis));
1226static void find_keys_causing_cycles_and_mask_their_issuers(
const std::vector<Key> &keys)
1228 boost::adjacency_list<> graph{keys.size()};
1230 for (
unsigned int i = 0, end = keys.size(); i !=
end; ++i) {
1231 const auto &key = keys[i];
1232 const char *
const issuer_fpr = cleanChainID(key);
1233 if (!issuer_fpr || !*issuer_fpr) {
1236 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1237 if (it == keys.end()) {
1240 const auto j = std::distance(keys.begin(), it);
1241 const auto edge = boost::add_edge(i, j, graph).first;
1242 if (graph_has_cycle(graph)) {
1243 Issuers::instance()->maskIssuerOfKey(key);
1244 boost::remove_edge(edge, graph);
1249static auto build_key_graph(
const std::vector<Key> &keys)
1251 boost::adjacency_list<> graph(keys.size());
1254 for (
unsigned int i = 0, end = keys.size(); i !=
end; ++i) {
1255 const char *
const issuer_fpr = cleanChainID(keys[i]);
1256 if (!issuer_fpr || !*issuer_fpr) {
1259 const std::vector<Key>::const_iterator it = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1260 if (it == keys.end()) {
1263 const auto j = std::distance(keys.begin(), it);
1264 add_edge(i, j, graph);
1271static std::vector<Key> topological_sort(
const std::vector<Key> &keys)
1273 const auto graph = build_key_graph(keys);
1275 std::vector<int> order;
1276 order.reserve(keys.size());
1277 topological_sort(graph, std::back_inserter(order));
1279 Q_ASSERT(order.size() == keys.size());
1281 std::vector<Key> result;
1283 for (
int i : std::as_const(order)) {
1284 result.push_back(keys[i]);
1293 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
1299 const std::vector<Key> oldKeys = mKeysByFingerprint;
1301 std::vector<Key> merged;
1302 merged.reserve(keys.size() + mKeysByFingerprint.size());
1303 std::set_union(keys.begin(),
1305 mKeysByFingerprint.begin(),
1306 mKeysByFingerprint.end(),
1307 std::back_inserter(merged),
1308 _detail::ByFingerprint<std::less>());
1310 mKeysByFingerprint = merged;
1312 if (graph_has_cycle(build_key_graph(mKeysByFingerprint))) {
1313 find_keys_causing_cycles_and_mask_their_issuers(mKeysByFingerprint);
1316 std::set<Key, _detail::ByFingerprint<std::less>> changedParents;
1318 const auto topologicalSortedList = topological_sort(keys);
1319 for (
const Key &key : topologicalSortedList) {
1321 const char *
const fpr = key.primaryFingerprint();
1322 if (!fpr || !*fpr) {
1326 const bool keyAlreadyExisted = std::binary_search(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>());
1328 const Map::iterator it = mKeysByNonExistingParent.find(fpr);
1329 const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
1330 if (it != mKeysByNonExistingParent.end()) {
1331 mKeysByNonExistingParent.erase(it);
1336 if (!keyAlreadyExisted) {
1337 auto last = mTopLevels.begin();
1338 auto lastFP = mKeysByFingerprint.begin();
1340 for (
const Key &k : children) {
1341 last = Kleo::binary_find(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
1342 Q_ASSERT(last != mTopLevels.end());
1343 const int row = std::distance(mTopLevels.begin(), last);
1345 lastFP = Kleo::binary_find(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
1346 Q_ASSERT(lastFP != mKeysByFingerprint.end());
1349 if (!modelResetInProgress()) {
1352 last = mTopLevels.erase(last);
1353 lastFP = mKeysByFingerprint.erase(lastFP);
1354 if (!modelResetInProgress()) {
1361 const char *
const issuer_fpr = cleanChainID(key);
1362 if (!issuer_fpr || !*issuer_fpr) {
1364 addTopLevelKey(key);
1365 }
else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>())) {
1367 addKeyWithParent(issuer_fpr, key);
1370 addKeyWithoutParent(issuer_fpr, key);
1375 while (key_parent.
isValid()) {
1376 changedParents.insert(doMapToKey(key_parent));
1377 key_parent = key_parent.
parent();
1382 if (!keyAlreadyExisted && !children.empty()) {
1387 for (
int i = children.size() - 1; i >= 0; --i) {
1388 Q_EMIT rowMoved(new_parent, i);
1394 if (!modelResetInProgress()) {
1395 for (
const Key &i : std::as_const(changedParents)) {
1402 return indexes(keys);
1405void HierarchicalKeyListModel::doRemoveKey(
const Key &key)
1412 const char *
const fpr = key.primaryFingerprint();
1413 if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
1415 std::vector<Key> keys = mKeysByFingerprint;
1416 const std::vector<Key>::iterator it = Kleo::binary_find(keys.begin(), keys.end(), key, _detail::ByFingerprint<std::less>());
1417 if (it == keys.end()) {
1430 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), key, _detail::ByFingerprint<std::less>());
1432 Q_ASSERT(it != mKeysByFingerprint.end());
1433 Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
1434 Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
1436 if (!modelResetInProgress()) {
1437 beginRemoveRows(parent(idx), idx.
row(), idx.
row());
1439 mKeysByFingerprint.erase(it);
1441 const char *
const issuer_fpr = cleanChainID(key);
1443 const std::vector<Key>::iterator tlIt = Kleo::binary_find(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1444 if (tlIt != mTopLevels.end()) {
1445 mTopLevels.erase(tlIt);
1448 if (issuer_fpr && *issuer_fpr) {
1449 const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
1450 if (nexIt != mKeysByNonExistingParent.end()) {
1451 const std::vector<Key>::iterator eit = Kleo::binary_find(nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>());
1452 if (eit != nexIt->second.end()) {
1453 nexIt->second.erase(eit);
1455 if (nexIt->second.empty()) {
1456 mKeysByNonExistingParent.erase(nexIt);
1460 const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
1461 if (exIt != mKeysByExistingParent.end()) {
1462 const std::vector<Key>::iterator eit = Kleo::binary_find(exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>());
1463 if (eit != exIt->second.end()) {
1464 exIt->second.erase(eit);
1466 if (exIt->second.empty()) {
1467 mKeysByExistingParent.erase(exIt);
1471 if (!modelResetInProgress()) {
1476KeyGroup HierarchicalKeyListModel::doMapToGroup(
const QModelIndex &idx)
const
1484 if (
static_cast<unsigned>(idx.
row()) >= mTopLevels.size() &&
static_cast<unsigned>(idx.
row()) < mTopLevels.size() + mGroups.size()
1485 && idx.
column() < NumColumns) {
1486 return mGroups[idx.
row() - mTopLevels.size()];
1492QModelIndex HierarchicalKeyListModel::doMapFromGroup(
const KeyGroup &group,
int column)
const
1494 Q_ASSERT(!group.isNull());
1495 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(), [group](
const KeyGroup &g) {
1496 return g.source() == group.source() && g.id() == group.id();
1498 if (it == mGroups.cend()) {
1501 return createIndex(it - mGroups.cbegin() + mTopLevels.size(), column);
1505void HierarchicalKeyListModel::doSetGroups(
const std::vector<KeyGroup> &groups)
1507 Q_ASSERT(mGroups.empty());
1508 const int first = mTopLevels.size();
1509 const int last = first + groups.size() - 1;
1510 if (!modelResetInProgress()) {
1514 if (!modelResetInProgress()) {
1519QModelIndex HierarchicalKeyListModel::doAddGroup(
const KeyGroup &group)
1521 const int newRow = lastGroupRow() + 1;
1522 if (!modelResetInProgress()) {
1525 mGroups.push_back(group);
1526 if (!modelResetInProgress()) {
1529 return createIndex(newRow, 0);
1532bool HierarchicalKeyListModel::doSetGroupData(
const QModelIndex &index,
const KeyGroup &group)
1534 if (group.isNull()) {
1537 const int groupIndex = this->groupIndex(index);
1538 if (groupIndex == -1) {
1541 mGroups[groupIndex] = group;
1542 if (!modelResetInProgress()) {
1543 Q_EMIT dataChanged(createIndex(index.
row(), 0), createIndex(index.
row(), NumColumns - 1));
1548bool HierarchicalKeyListModel::doRemoveGroup(
const KeyGroup &group)
1550 const QModelIndex modelIndex = doMapFromGroup(group, 0);
1554 const int groupIndex = this->groupIndex(modelIndex);
1555 Q_ASSERT(groupIndex != -1);
1556 if (groupIndex == -1) {
1559 if (!modelResetInProgress()) {
1562 mGroups.erase(mGroups.begin() + groupIndex);
1563 if (!modelResetInProgress()) {
1569void HierarchicalKeyListModel::doClear(ItemTypes types)
1573 mKeysByFingerprint.clear();
1574 mKeysByExistingParent.clear();
1575 mKeysByNonExistingParent.clear();
1576 Issuers::instance()->clear();
1578 if (types & Groups) {
1583void AbstractKeyListModel::useKeyCache(
bool value, KeyList::Options options)
1585 d->m_keyListOptions = options;
1586 d->m_useKeyCache = value;
1587 if (!d->m_useKeyCache) {
1590 d->updateFromKeyCache();
1592 connect(KeyCache::instance().
get(), &KeyCache::keysMayHaveChanged,
this, [
this] {
1593 d->updateFromKeyCache();
1598AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(
QObject *p)
1600 AbstractKeyListModel *
const m =
new FlatKeyListModel(p);
1601#ifdef KLEO_MODEL_TEST
1608AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(
QObject *p)
1610 AbstractKeyListModel *
const m =
new HierarchicalKeyListModel(p);
1611#ifdef KLEO_MODEL_TEST
1617QMimeData *AbstractKeyListModel::mimeData(
const QModelIndexList &indexes)
const
1619 if (d->m_dragHandler) {
1620 return d->m_dragHandler->mimeData(indexes);
1628 if (d->m_dragHandler) {
1629 return d->m_dragHandler->flags(index);
1635QStringList AbstractKeyListModel::mimeTypes()
const
1637 if (d->m_dragHandler) {
1638 return d->m_dragHandler->mimeTypes();
1644void AbstractKeyListModel::setDragHandler(
const std::shared_ptr<DragHandler> &dragHandler)
1646 d->m_dragHandler = dragHandler;
1649#include "keylistmodel.moc"
1688#include "moc_keylistmodel.cpp"
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QAction * end(const QObject *recvr, const char *slot, QObject *parent)
KIOCORE_EXPORT TransferJob * get(const QUrl &url, LoadType reload=NoReload, JobFlags flags=DefaultFlags)
virtual Qt::ItemFlags flags(const QModelIndex &index) const const
virtual QMimeData * mimeData(const QModelIndexList &indexes) const const
virtual QStringList mimeTypes() const const
void modelAboutToBeReset()
bool isValid() const const
QIcon fromTheme(const QString &name)
bool isNull() const const
void reserve(qsizetype size)
qsizetype size() const const
void * internalPointer() const const
bool isValid() const const
const QAbstractItemModel * model() const const
QModelIndex parent() const const
QModelIndex sibling(int row, int column) const const
QString fromLatin1(QByteArrayView str)
QString fromStdString(const std::string &str)
QString fromUtf8(QByteArrayView str)
QString join(QChar separator) const const
QTestData & newRow(const char *dataTag)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool canConvert() const const
QVariant fromValue(T &&value)