1 /* -*- mode: c++; c-basic-offset:4 -*-
2 models/keylistmodel.cpp
3
4 This file is part of libkleopatra, the KDE keymanagement library
5 SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
6 SPDX-FileCopyrightText: 2021 g10 Code GmbH
7 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
8
9 SPDX-License-Identifier: GPL-2.0-or-later
10 */
11
12 #include "keylistmodel.h"
13
14 #include "keycache.h"
15 #include "kleo/keygroup.h"
16 #include "kleo/predicates.h"
17 #include "kleo/keyfiltermanager.h"
18 #include "kleo/keyfilter.h"
19 #include "utils/algorithm.h"
20 #include "utils/formatting.h"
21
22 #ifdef KLEO_MODEL_TEST
23 # include <QAbstractItemModelTester>
24 #endif
25
26 #include <Libkleo/KeyFilterManager>
27 #include <Libkleo/KeyFilter>
28
29 #include <KLocalizedString>
30
31 #include <QFont>
32 #include <QColor>
33 #include <QHash>
34 #include <QIcon>
35 #include <QDate>
36 #include <gpgme++/key.h>
37
38 #ifndef Q_MOC_RUN // QTBUG-22829
39 #include <boost/graph/topological_sort.hpp>
40 #include <boost/graph/adjacency_list.hpp>
41 #endif
42
43 #include <algorithm>
44 #include <map>
45 #include <set>
46 #include <iterator>
47
48 #include <gpgme++/gpgmepp_version.h>
49 #if GPGMEPP_VERSION >= 0x10E00 // 1.14.0
50 # define GPGME_HAS_REMARKS
51 #endif
52
53 using namespace GpgME;
54 using namespace Kleo;
55 using namespace Kleo::KeyList;
56
57 Q_DECLARE_METATYPE(GpgME::Key)
58 Q_DECLARE_METATYPE(KeyGroup)
59
60 class AbstractKeyListModel::Private
61 {
62 AbstractKeyListModel *const q;
63 public:
64 explicit Private(AbstractKeyListModel *qq);
65
66 void updateFromKeyCache();
67
68 public:
69 int m_toolTipOptions = Formatting::Validity;
70 mutable QHash<const char *, QVariant> prettyEMailCache;
71 mutable QHash<const char *, QVariant> remarksCache;
72 bool m_useKeyCache = false;
73 bool m_modelResetInProgress = false;
74 KeyList::Options m_keyListOptions = AllKeys;
75 std::vector<GpgME::Key> m_remarkKeys;
76 };
77
Private(Kleo::AbstractKeyListModel * qq)78 AbstractKeyListModel::Private::Private(Kleo::AbstractKeyListModel *qq)
79 : q(qq)
80 {
81 }
82
updateFromKeyCache()83 void AbstractKeyListModel::Private::updateFromKeyCache()
84 {
85 if (m_useKeyCache) {
86 q->setKeys(m_keyListOptions == SecretKeysOnly ? KeyCache::instance()->secretKeys() : KeyCache::instance()->keys());
87 if (m_keyListOptions == IncludeGroups) {
88 q->setGroups(KeyCache::instance()->groups());
89 }
90 }
91 }
92
AbstractKeyListModel(QObject * p)93 AbstractKeyListModel::AbstractKeyListModel(QObject *p)
94 : QAbstractItemModel(p), KeyListModelInterface(), d(new Private(this))
95 {
96 connect(this, &QAbstractItemModel::modelAboutToBeReset, this, [this] () { d->m_modelResetInProgress = true; });
97 connect(this, &QAbstractItemModel::modelReset, this, [this] () { d->m_modelResetInProgress = false; });
98 }
99
~AbstractKeyListModel()100 AbstractKeyListModel::~AbstractKeyListModel() {}
101
setToolTipOptions(int opts)102 void AbstractKeyListModel::setToolTipOptions(int opts)
103 {
104 d->m_toolTipOptions = opts;
105 }
106
toolTipOptions() const107 int AbstractKeyListModel::toolTipOptions() const
108 {
109 return d->m_toolTipOptions;
110 }
111
setRemarkKeys(const std::vector<GpgME::Key> & keys)112 void AbstractKeyListModel::setRemarkKeys(const std::vector<GpgME::Key> &keys)
113 {
114 d->m_remarkKeys = keys;
115 }
116
remarkKeys() const117 std::vector<GpgME::Key> AbstractKeyListModel::remarkKeys() const
118 {
119 return d->m_remarkKeys;
120 }
121
key(const QModelIndex & idx) const122 Key AbstractKeyListModel::key(const QModelIndex &idx) const
123 {
124 if (idx.isValid()) {
125 return doMapToKey(idx);
126 } else {
127 return Key::null;
128 }
129 }
130
keys(const QList<QModelIndex> & indexes) const131 std::vector<Key> AbstractKeyListModel::keys(const QList<QModelIndex> &indexes) const
132 {
133 std::vector<Key> result;
134 result.reserve(indexes.size());
135 std::transform(indexes.begin(), indexes.end(),
136 std::back_inserter(result),
137 [this](const QModelIndex &idx) {
138 return this->key(idx);
139 });
140 result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&GpgME::Key::isNull)), result.end());
141 _detail::remove_duplicates_by_fpr(result);
142 return result;
143 }
144
group(const QModelIndex & idx) const145 KeyGroup AbstractKeyListModel::group(const QModelIndex &idx) const
146 {
147 if (idx.isValid()) {
148 return doMapToGroup(idx);
149 } else {
150 return KeyGroup();
151 }
152 }
153
index(const Key & key) const154 QModelIndex AbstractKeyListModel::index(const Key &key) const
155 {
156 return index(key, 0);
157 }
158
index(const Key & key,int col) const159 QModelIndex AbstractKeyListModel::index(const Key &key, int col) const
160 {
161 if (key.isNull() || col < 0 || col >= NumColumns) {
162 return {};
163 } else {
164 return doMapFromKey(key, col);
165 }
166 }
167
indexes(const std::vector<Key> & keys) const168 QList<QModelIndex> AbstractKeyListModel::indexes(const std::vector<Key> &keys) const
169 {
170 QList<QModelIndex> result;
171 result.reserve(keys.size());
172 std::transform(keys.begin(), keys.end(),
173 std::back_inserter(result),
174 [this](const Key &key) {
175 return this->index(key);
176 });
177 return result;
178 }
179
index(const KeyGroup & group) const180 QModelIndex AbstractKeyListModel::index(const KeyGroup &group) const
181 {
182 return index(group, 0);
183 }
184
index(const KeyGroup & group,int col) const185 QModelIndex AbstractKeyListModel::index(const KeyGroup &group, int col) const
186 {
187 if (group.isNull() || col < 0 || col >= NumColumns) {
188 return {};
189 } else {
190 return doMapFromGroup(group, col);
191 }
192 }
193
setKeys(const std::vector<Key> & keys)194 void AbstractKeyListModel::setKeys(const std::vector<Key> &keys)
195 {
196 beginResetModel();
197 clear(Keys);
198 addKeys(keys);
199 endResetModel();
200 }
201
addKey(const Key & key)202 QModelIndex AbstractKeyListModel::addKey(const Key &key)
203 {
204 const std::vector<Key> vec(1, key);
205 const QList<QModelIndex> l = doAddKeys(vec);
206 return l.empty() ? QModelIndex() : l.front();
207 }
208
removeKey(const Key & key)209 void AbstractKeyListModel::removeKey(const Key &key)
210 {
211 if (key.isNull()) {
212 return;
213 }
214 doRemoveKey(key);
215 d->prettyEMailCache.remove(key.primaryFingerprint());
216 d->remarksCache.remove(key.primaryFingerprint());
217 }
218
addKeys(const std::vector<Key> & keys)219 QList<QModelIndex> AbstractKeyListModel::addKeys(const std::vector<Key> &keys)
220 {
221 std::vector<Key> sorted;
222 sorted.reserve(keys.size());
223 std::remove_copy_if(keys.begin(), keys.end(),
224 std::back_inserter(sorted),
225 std::mem_fn(&Key::isNull));
226 std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
227 return doAddKeys(sorted);
228 }
229
setGroups(const std::vector<KeyGroup> & groups)230 void AbstractKeyListModel::setGroups(const std::vector<KeyGroup> &groups)
231 {
232 beginResetModel();
233 clear(Groups);
234 doSetGroups(groups);
235 endResetModel();
236 }
237
addGroup(const KeyGroup & group)238 QModelIndex AbstractKeyListModel::addGroup(const KeyGroup &group)
239 {
240 if (group.isNull()) {
241 return QModelIndex();
242 }
243 return doAddGroup(group);
244 }
245
removeGroup(const KeyGroup & group)246 bool AbstractKeyListModel::removeGroup(const KeyGroup &group)
247 {
248 if (group.isNull()) {
249 return false;
250 }
251 return doRemoveGroup(group);
252 }
253
clear(ItemTypes types)254 void AbstractKeyListModel::clear(ItemTypes types)
255 {
256 const bool inReset = modelResetInProgress();
257 if (!inReset) {
258 beginResetModel();
259 }
260 doClear(types);
261 if (types & Keys) {
262 d->prettyEMailCache.clear();
263 d->remarksCache.clear();
264 }
265 if (!inReset) {
266 endResetModel();
267 }
268 }
269
columnCount(const QModelIndex &) const270 int AbstractKeyListModel::columnCount(const QModelIndex &) const
271 {
272 return NumColumns;
273 }
274
headerData(int section,Qt::Orientation o,int role) const275 QVariant AbstractKeyListModel::headerData(int section, Qt::Orientation o, int role) const
276 {
277 if (o == Qt::Horizontal) {
278 if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
279 switch (section) {
280 case PrettyName: return i18n("Name");
281 case PrettyEMail: return i18n("E-Mail");
282 case Validity: return i18n("User-IDs");
283 case ValidFrom: return i18n("Valid From");
284 case ValidUntil: return i18n("Valid Until");
285 case TechnicalDetails: return i18n("Protocol");
286 case ShortKeyID: return i18n("Key-ID");
287 case KeyID: return i18n("Key-ID");
288 case Fingerprint: return i18n("Fingerprint");
289 case Issuer: return i18n("Issuer");
290 case SerialNumber: return i18n("Serial Number");
291 case Origin: return i18n("Origin");
292 case LastUpdate: return i18n("Last Update");
293 case OwnerTrust: return i18n("Certification Trust");
294 case Remarks: return i18n("Tags");
295 case NumColumns:;
296 }
297 }
298 }
299 return QVariant();
300 }
301
returnIfValid(const QColor & t)302 static QVariant returnIfValid(const QColor &t)
303 {
304 if (t.isValid()) {
305 return t;
306 } else {
307 return QVariant();
308 }
309 }
310
returnIfValid(const QIcon & t)311 static QVariant returnIfValid(const QIcon &t)
312 {
313 if (!t.isNull()) {
314 return t;
315 } else {
316 return QVariant();
317 }
318 }
319
data(const QModelIndex & index,int role) const320 QVariant AbstractKeyListModel::data(const QModelIndex &index, int role) const
321 {
322 const Key key = this->key(index);
323 if (!key.isNull()) {
324 return data(key, index.column(), role);
325 }
326
327 const KeyGroup group = this->group(index);
328 if (!group.isNull()) {
329 return data(group, index.column(), role);
330 }
331
332 return QVariant();
333 }
334
data(const Key & key,int column,int role) const335 QVariant AbstractKeyListModel::data(const Key &key, int column, int role) const
336 {
337 if (role == Qt::DisplayRole || role == Qt::EditRole) {
338 switch (column) {
339 case PrettyName:
340 return Formatting::prettyName(key);
341 case PrettyEMail:
342 if (const char *const fpr = key.primaryFingerprint()) {
343 const QHash<const char *, QVariant>::const_iterator it = d->prettyEMailCache.constFind(fpr);
344 if (it != d->prettyEMailCache.constEnd()) {
345 return *it;
346 } else {
347 return d->prettyEMailCache[fpr] = Formatting::prettyEMail(key);
348 }
349 } else {
350 return QVariant();
351 }
352 case Validity:
353 return Formatting::complianceStringShort(key);
354 case ValidFrom:
355 if (role == Qt::EditRole) {
356 return Formatting::creationDate(key);
357 } else {
358 return Formatting::creationDateString(key);
359 }
360 case ValidUntil:
361 if (role == Qt::EditRole) {
362 return Formatting::expirationDate(key);
363 } else {
364 return Formatting::expirationDateString(key);
365 }
366 case TechnicalDetails:
367 return Formatting::type(key);
368 case ShortKeyID:
369 return QString::fromLatin1(key.shortKeyID());
370 case KeyID:
371 return Formatting::prettyID(key.keyID());
372 case Summary:
373 return Formatting::summaryLine(key);
374 case Fingerprint:
375 return Formatting::prettyID(key.primaryFingerprint());
376 case Issuer:
377 return QString::fromUtf8(key.issuerName());
378 case Origin:
379 return Formatting::origin(key.origin());
380 case LastUpdate:
381 return Formatting::dateString(key.lastUpdate());
382 case SerialNumber:
383 return QString::fromUtf8(key.issuerSerial());
384 case OwnerTrust:
385 return Formatting::ownerTrustShort(key.ownerTrust());
386 case Remarks:
387 #ifdef GPGME_HAS_REMARKS
388 {
389 const char *const fpr = key.primaryFingerprint();
390 if (fpr && key.protocol() == GpgME::OpenPGP && key.numUserIDs() &&
391 d->m_remarkKeys.size()) {
392 if (!(key.keyListMode() & GpgME::SignatureNotations)) {
393 return i18n("Loading...");
394 }
395 const QHash<const char *, QVariant>::const_iterator it = d->remarksCache.constFind(fpr);
396 if (it != d->remarksCache.constEnd()) {
397 return *it;
398 } else {
399 GpgME::Error err;
400 const auto remarks = key.userID(0).remarks(d->m_remarkKeys, err);
401 if (remarks.size() == 1) {
402 const auto remark = QString::fromStdString(remarks[0]);
403 return d->remarksCache[fpr] = remark;
404 } else {
405 QStringList remarkList;
406 remarkList.reserve(remarks.size());
407 for (const auto &rem: remarks) {
408 remarkList << QString::fromStdString(rem);
409 }
410 const auto remark = remarkList.join(QStringLiteral("; "));
411 return d->remarksCache[fpr] = remark;
412 }
413 }
414 } else {
415 return QVariant();
416 }
417 }
418 #endif
419 return QVariant();
420 case NumColumns:
421 break;
422 }
423 } else if (role == Qt::ToolTipRole) {
424 return Formatting::toolTip(key, toolTipOptions());
425 } else if (role == Qt::FontRole) {
426 return KeyFilterManager::instance()->font(key, (column == ShortKeyID || column == KeyID || column == Fingerprint) ? QFont(QStringLiteral("monospace")) : QFont());
427 } else if (role == Qt::DecorationRole) {
428 return column == Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) : QVariant();
429 } else if (role == Qt::BackgroundRole) {
430 return returnIfValid(KeyFilterManager::instance()->bgColor(key));
431 } else if (role == Qt::ForegroundRole) {
432 return returnIfValid(KeyFilterManager::instance()->fgColor(key));
433 } else if (role == FingerprintRole) {
434 return QString::fromLatin1(key.primaryFingerprint());
435 } else if (role == KeyRole) {
436 return QVariant::fromValue(key);
437 }
438 return QVariant();
439 }
440
data(const KeyGroup & group,int column,int role) const441 QVariant AbstractKeyListModel::data(const KeyGroup &group, int column, int role) const
442 {
443 if (role == Qt::DisplayRole || role == Qt::EditRole) {
444 switch (column) {
445 case PrettyName:
446 return group.name();
447 case PrettyEMail:
448 return QVariant();
449 case Validity:
450 return Formatting::complianceStringShort(group);
451 case ValidFrom:
452 return QString();
453 case ValidUntil:
454 return QString();
455 case TechnicalDetails:
456 return Formatting::type(group);
457 case ShortKeyID:
458 return QString();
459 case KeyID:
460 return QString();
461 case Summary:
462 return Formatting::summaryLine(group); // used for filtering
463 case Fingerprint:
464 return QString();
465 case Issuer:
466 return QString();
467 case Origin:
468 return QString();
469 case LastUpdate:
470 return QString();
471 case SerialNumber:
472 return QString();
473 case OwnerTrust:
474 return QString();
475 case Remarks:
476 return QVariant();
477 case NumColumns:
478 break;
479 }
480 } else if (role == Qt::ToolTipRole) {
481 return Formatting::toolTip(group, toolTipOptions());
482 } else if (role == Qt::FontRole) {
483 return QFont();
484 } else if (role == Qt::DecorationRole) {
485 return column == Icon ? QIcon::fromTheme(QStringLiteral("group")) : QVariant();
486 } else if (role == Qt::BackgroundRole) {
487 } else if (role == Qt::ForegroundRole) {
488 } else if (role == GroupRole) {
489 return QVariant::fromValue(group);
490 }
491 return QVariant();
492 }
493
setData(const QModelIndex & index,const QVariant & value,int role)494 bool AbstractKeyListModel::setData(const QModelIndex &index, const QVariant &value, int role)
495 {
496 Q_UNUSED(role)
497 Q_ASSERT(value.canConvert<KeyGroup>());
498 if (value.canConvert<KeyGroup>()) {
499 const KeyGroup group = value.value<KeyGroup>();
500 return doSetGroupData(index, group);
501 }
502
503 return false;
504 }
505
modelResetInProgress()506 bool AbstractKeyListModel::modelResetInProgress()
507 {
508 return d->m_modelResetInProgress;
509 }
510
511 namespace
512 {
513 template <typename Base>
514 class TableModelMixin : public Base
515 {
516 public:
TableModelMixin(QObject * p=nullptr)517 explicit TableModelMixin(QObject *p = nullptr) : Base(p) {}
~TableModelMixin()518 ~TableModelMixin() override {}
519
520 using Base::index;
index(int row,int column,const QModelIndex & pidx=QModelIndex ()) const521 QModelIndex index(int row, int column, const QModelIndex &pidx = QModelIndex()) const override
522 {
523 return this->hasIndex(row, column, pidx) ? this->createIndex(row, column, nullptr) : QModelIndex();
524 }
525
526 private:
parent(const QModelIndex &) const527 QModelIndex parent(const QModelIndex &) const override
528 {
529 return QModelIndex();
530 }
hasChildren(const QModelIndex & pidx) const531 bool hasChildren(const QModelIndex &pidx) const override
532 {
533 return (pidx.model() == this || !pidx.isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0;
534 }
535 };
536
537 class FlatKeyListModel
538 #ifndef Q_MOC_RUN
539 : public TableModelMixin<AbstractKeyListModel>
540 #else
541 : public AbstractKeyListModel
542 #endif
543 {
544 Q_OBJECT
545 public:
546 explicit FlatKeyListModel(QObject *parent = nullptr);
547 ~FlatKeyListModel() override;
548
rowCount(const QModelIndex & pidx) const549 int rowCount(const QModelIndex &pidx) const override
550 {
551 return pidx.isValid() ? 0 : mKeysByFingerprint.size() + mGroups.size();
552 }
553
554 private:
555 Key doMapToKey(const QModelIndex &index) const override;
556 QModelIndex doMapFromKey(const Key &key, int col) const override;
557 QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) override;
558 void doRemoveKey(const Key &key) override;
559
560 KeyGroup doMapToGroup(const QModelIndex &index) const override;
561 QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override;
562 void doSetGroups(const std::vector<KeyGroup> &groups) override;
563 QModelIndex doAddGroup(const KeyGroup &group) override;
564 bool doSetGroupData(const QModelIndex &index, const KeyGroup &group) override;
565 bool doRemoveGroup(const KeyGroup &group) override;
566
doClear(ItemTypes types)567 void doClear(ItemTypes types) override
568 {
569 if (types & Keys) {
570 mKeysByFingerprint.clear();
571 }
572 if (types & Groups) {
573 mGroups.clear();
574 }
575 }
576
firstGroupRow() const577 int firstGroupRow() const
578 {
579 return mKeysByFingerprint.size();
580 }
581
lastGroupRow() const582 int lastGroupRow() const
583 {
584 return mKeysByFingerprint.size() + mGroups.size() - 1;
585 }
586
groupIndex(const QModelIndex & index) const587 int groupIndex(const QModelIndex &index) const
588 {
589 if (!index.isValid()
590 || index.row() < firstGroupRow() || index.row() > lastGroupRow()
591 || index.column() >= NumColumns) {
592 return -1;
593 }
594 return index.row() - firstGroupRow();
595 }
596
597 private:
598 std::vector<Key> mKeysByFingerprint;
599 std::vector<KeyGroup> mGroups;
600 };
601
602 class HierarchicalKeyListModel : public AbstractKeyListModel
603 {
604 Q_OBJECT
605 public:
606 explicit HierarchicalKeyListModel(QObject *parent = nullptr);
607 ~HierarchicalKeyListModel() override;
608
609 int rowCount(const QModelIndex &pidx) const override;
610 using AbstractKeyListModel::index;
611 QModelIndex index(int row, int col, const QModelIndex &pidx) const override;
612 QModelIndex parent(const QModelIndex &idx) const override;
613
hasChildren(const QModelIndex & pidx) const614 bool hasChildren(const QModelIndex &pidx) const override
615 {
616 return rowCount(pidx) > 0;
617 }
618
619 private:
620 Key doMapToKey(const QModelIndex &index) const override;
621 QModelIndex doMapFromKey(const Key &key, int col) const override;
622 QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) override;
623 void doRemoveKey(const Key &key) override;
624
625 KeyGroup doMapToGroup(const QModelIndex &index) const override;
626 QModelIndex doMapFromGroup(const KeyGroup &group, int column) const override;
627 void doSetGroups(const std::vector<KeyGroup> &groups) override;
628 QModelIndex doAddGroup(const KeyGroup &group) override;
629 bool doSetGroupData(const QModelIndex &index, const KeyGroup &group) override;
630 bool doRemoveGroup(const KeyGroup &group) override;
631
doClear(ItemTypes types)632 void doClear(ItemTypes types) override
633 {
634 if (types & Keys) {
635 mTopLevels.clear();
636 mKeysByFingerprint.clear();
637 mKeysByExistingParent.clear();
638 mKeysByNonExistingParent.clear();
639 }
640 if (types & Groups) {
641 mGroups.clear();
642 }
643 }
644
firstGroupRow() const645 int firstGroupRow() const
646 {
647 return mTopLevels.size();
648 }
649
lastGroupRow() const650 int lastGroupRow() const
651 {
652 return mTopLevels.size() + mGroups.size() - 1;
653 }
654
groupIndex(const QModelIndex & index) const655 int groupIndex(const QModelIndex &index) const
656 {
657 if (!index.isValid()
658 || index.row() < firstGroupRow() || index.row() > lastGroupRow()
659 || index.column() >= NumColumns) {
660 return -1;
661 }
662 return index.row() - firstGroupRow();
663 }
664
665 private:
666 void addTopLevelKey(const Key &key);
667 void addKeyWithParent(const char *issuer_fpr, const Key &key);
668 void addKeyWithoutParent(const char *issuer_fpr, const Key &key);
669
670 private:
671 typedef std::map< std::string, std::vector<Key> > Map;
672 std::vector<Key> mKeysByFingerprint; // all keys
673 Map mKeysByExistingParent, mKeysByNonExistingParent; // parent->child map
674 std::vector<Key> mTopLevels; // all roots + parent-less
675 std::vector<KeyGroup> mGroups;
676 };
677
cleanChainID(const Key & key)678 static const char *cleanChainID(const Key &key)
679 {
680 if (key.isRoot()) {
681 return "";
682 }
683 if (const char *chid = key.chainID()) {
684 return chid;
685 }
686 return "";
687 }
688
689 }
690
FlatKeyListModel(QObject * p)691 FlatKeyListModel::FlatKeyListModel(QObject *p)
692 : TableModelMixin<AbstractKeyListModel>(p)
693 {
694 }
695
~FlatKeyListModel()696 FlatKeyListModel::~FlatKeyListModel() {}
697
doMapToKey(const QModelIndex & idx) const698 Key FlatKeyListModel::doMapToKey(const QModelIndex &idx) const
699 {
700 Q_ASSERT(idx.isValid());
701 if (static_cast<unsigned>(idx.row()) < mKeysByFingerprint.size() && idx.column() < NumColumns) {
702 return mKeysByFingerprint[ idx.row() ];
703 } else {
704 return Key::null;
705 }
706 }
707
doMapFromKey(const Key & key,int col) const708 QModelIndex FlatKeyListModel::doMapFromKey(const Key &key, int col) const
709 {
710 Q_ASSERT(!key.isNull());
711 const std::vector<Key>::const_iterator it
712 = std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
713 key, _detail::ByFingerprint<std::less>());
714 if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
715 return {};
716 } else {
717 return createIndex(it - mKeysByFingerprint.begin(), col);
718 }
719 }
720
doAddKeys(const std::vector<Key> & keys)721 QList<QModelIndex> FlatKeyListModel::doAddKeys(const std::vector<Key> &keys)
722 {
723 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
724
725 if (keys.empty()) {
726 return QList<QModelIndex>();
727 }
728
729 for (auto it = keys.begin(), end = keys.end(); it != end; ++it) {
730
731 // find an insertion point:
732 const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
733 const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
734
735 if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
736 // key existed before - replace with new one:
737 mKeysByFingerprint[idx - 1] = *it;
738 if (!modelResetInProgress()) {
739 Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
740 }
741 } else {
742 // new key - insert:
743 if (!modelResetInProgress()) {
744 beginInsertRows(QModelIndex(), idx, idx);
745 }
746 mKeysByFingerprint.insert(pos, *it);
747 if (!modelResetInProgress()) {
748 endInsertRows();
749 }
750 }
751 }
752
753 return indexes(keys);
754 }
755
doRemoveKey(const Key & key)756 void FlatKeyListModel::doRemoveKey(const Key &key)
757 {
758 const std::vector<Key>::iterator it
759 = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
760 key, _detail::ByFingerprint<std::less>());
761 if (it == mKeysByFingerprint.end()) {
762 return;
763 }
764
765 const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
766 if (!modelResetInProgress()) {
767 beginRemoveRows(QModelIndex(), row, row);
768 }
769 mKeysByFingerprint.erase(it);
770 if (!modelResetInProgress()) {
771 endRemoveRows();
772 }
773 }
774
doMapToGroup(const QModelIndex & idx) const775 KeyGroup FlatKeyListModel::doMapToGroup(const QModelIndex &idx) const
776 {
777 Q_ASSERT(idx.isValid());
778 if (static_cast<unsigned>(idx.row()) >= mKeysByFingerprint.size()
779 && static_cast<unsigned>(idx.row()) < mKeysByFingerprint.size() + mGroups.size()
780 && idx.column() < NumColumns) {
781 return mGroups[ idx.row() - mKeysByFingerprint.size() ];
782 } else {
783 return KeyGroup();
784 }
785 }
786
doMapFromGroup(const KeyGroup & group,int column) const787 QModelIndex FlatKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
788 {
789 Q_ASSERT(!group.isNull());
790 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(),
791 [group](const KeyGroup &g) {
792 return g.source() == group.source() && g.id() == group.id();
793 });
794 if (it == mGroups.cend()) {
795 return QModelIndex();
796 } else {
797 return createIndex(it - mGroups.cbegin() + mKeysByFingerprint.size(), column);
798 }
799 }
800
doSetGroups(const std::vector<KeyGroup> & groups)801 void FlatKeyListModel::doSetGroups(const std::vector<KeyGroup> &groups)
802 {
803 Q_ASSERT(mGroups.empty()); // ensure that groups have been cleared
804 const int first = mKeysByFingerprint.size();
805 const int last = first + groups.size() - 1;
806 if (!modelResetInProgress()) {
807 beginInsertRows(QModelIndex(), first, last);
808 }
809 mGroups = groups;
810 if (!modelResetInProgress()) {
811 endInsertRows();
812 }
813 }
814
doAddGroup(const KeyGroup & group)815 QModelIndex FlatKeyListModel::doAddGroup(const KeyGroup &group)
816 {
817 const int newRow = lastGroupRow() + 1;
818 if (!modelResetInProgress()) {
819 beginInsertRows(QModelIndex(), newRow, newRow);
820 }
821 mGroups.push_back(group);
822 if (!modelResetInProgress()) {
823 endInsertRows();
824 }
825 return createIndex(newRow, 0);
826 }
827
doSetGroupData(const QModelIndex & index,const KeyGroup & group)828 bool FlatKeyListModel::doSetGroupData(const QModelIndex &index, const KeyGroup &group)
829 {
830 if (group.isNull()) {
831 return false;
832 }
833 const int groupIndex = this->groupIndex(index);
834 if (groupIndex == -1) {
835 return false;
836 }
837 mGroups[groupIndex] = group;
838 if (!modelResetInProgress()) {
839 Q_EMIT dataChanged(createIndex(index.row(), 0), createIndex(index.row(), NumColumns - 1));
840 }
841 return true;
842 }
843
doRemoveGroup(const KeyGroup & group)844 bool FlatKeyListModel::doRemoveGroup(const KeyGroup &group)
845 {
846 const QModelIndex modelIndex = doMapFromGroup(group, 0);
847 if (!modelIndex.isValid()) {
848 return false;
849 }
850 const int groupIndex = this->groupIndex(modelIndex);
851 Q_ASSERT(groupIndex != -1);
852 if (groupIndex == -1) {
853 return false;
854 }
855 if (!modelResetInProgress()) {
856 beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row());
857 }
858 mGroups.erase(mGroups.begin() + groupIndex);
859 if (!modelResetInProgress()) {
860 endRemoveRows();
861 }
862 return true;
863 }
864
HierarchicalKeyListModel(QObject * p)865 HierarchicalKeyListModel::HierarchicalKeyListModel(QObject *p)
866 : AbstractKeyListModel(p),
867 mKeysByFingerprint(),
868 mKeysByExistingParent(),
869 mKeysByNonExistingParent(),
870 mTopLevels()
871 {
872
873 }
874
~HierarchicalKeyListModel()875 HierarchicalKeyListModel::~HierarchicalKeyListModel() {}
876
rowCount(const QModelIndex & pidx) const877 int HierarchicalKeyListModel::rowCount(const QModelIndex &pidx) const
878 {
879
880 // toplevel item:
881 if (!pidx.isValid()) {
882 return mTopLevels.size() + mGroups.size();
883 }
884
885 if (pidx.column() != 0) {
886 return 0;
887 }
888
889 // non-toplevel item - find the number of subjects for this issuer:
890 const Key issuer = this->key(pidx);
891 const char *const fpr = issuer.primaryFingerprint();
892 if (!fpr || !*fpr) {
893 return 0;
894 }
895 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
896 if (it == mKeysByExistingParent.end()) {
897 return 0;
898 }
899 return it->second.size();
900 }
901
index(int row,int col,const QModelIndex & pidx) const902 QModelIndex HierarchicalKeyListModel::index(int row, int col, const QModelIndex &pidx) const
903 {
904
905 if (row < 0 || col < 0 || col >= NumColumns) {
906 return {};
907 }
908
909 // toplevel item:
910 if (!pidx.isValid()) {
911 if (static_cast<unsigned>(row) < mTopLevels.size()) {
912 return index(mTopLevels[row], col);
913 } else if (static_cast<unsigned>(row) < mTopLevels.size() + mGroups.size()) {
914 return index(mGroups[row - mTopLevels.size()], col);
915 } else {
916 return QModelIndex();
917 }
918 }
919
920 // non-toplevel item - find the row'th subject of this key:
921 const Key issuer = this->key(pidx);
922 const char *const fpr = issuer.primaryFingerprint();
923 if (!fpr || !*fpr) {
924 return QModelIndex();
925 }
926 const Map::const_iterator it = mKeysByExistingParent.find(fpr);
927 if (it == mKeysByExistingParent.end() || static_cast<unsigned>(row) >= it->second.size()) {
928 return QModelIndex();
929 }
930 return index(it->second[row], col);
931 }
932
parent(const QModelIndex & idx) const933 QModelIndex HierarchicalKeyListModel::parent(const QModelIndex &idx) const
934 {
935 const Key key = this->key(idx);
936 if (key.isNull() || key.isRoot()) {
937 return {};
938 }
939 const std::vector<Key>::const_iterator it
940 = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
941 cleanChainID(key), _detail::ByFingerprint<std::less>());
942 return it != mKeysByFingerprint.end() ? index(*it) : QModelIndex();
943 }
944
doMapToKey(const QModelIndex & idx) const945 Key HierarchicalKeyListModel::doMapToKey(const QModelIndex &idx) const
946 {
947
948 if (!idx.isValid()) {
949 return Key::null;
950 }
951
952 const char *const issuer_fpr = static_cast<const char *>(idx.internalPointer());
953 if (!issuer_fpr || !*issuer_fpr) {
954 // top-level:
955 if (static_cast<unsigned>(idx.row()) >= mTopLevels.size()) {
956 return Key::null;
957 } else {
958 return mTopLevels[idx.row()];
959 }
960 }
961
962 // non-toplevel:
963 const Map::const_iterator it
964 = mKeysByExistingParent.find(issuer_fpr);
965 if (it == mKeysByExistingParent.end() || static_cast<unsigned>(idx.row()) >= it->second.size()) {
966 return Key::null;
967 }
968 return it->second[idx.row()];
969 }
970
doMapFromKey(const Key & key,int col) const971 QModelIndex HierarchicalKeyListModel::doMapFromKey(const Key &key, int col) const
972 {
973
974 if (key.isNull()) {
975 return {};
976 }
977
978 const char *issuer_fpr = cleanChainID(key);
979
980 // we need to look in the toplevels list,...
981 const std::vector<Key> *v = &mTopLevels;
982 if (issuer_fpr && *issuer_fpr) {
983 const std::map< std::string, std::vector<Key> >::const_iterator it
984 = mKeysByExistingParent.find(issuer_fpr);
985 // ...unless we find an existing parent:
986 if (it != mKeysByExistingParent.end()) {
987 v = &it->second;
988 } else {
989 issuer_fpr = nullptr; // force internalPointer to zero for toplevels
990 }
991 }
992
993 const std::vector<Key>::const_iterator it
994 = std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
995 if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
996 return QModelIndex();
997 }
998
999 const unsigned int row = std::distance(v->begin(), it);
1000 return createIndex(row, col, const_cast<char * /* thanks, Trolls :/ */ >(issuer_fpr));
1001 }
1002
addKeyWithParent(const char * issuer_fpr,const Key & key)1003 void HierarchicalKeyListModel::addKeyWithParent(const char *issuer_fpr, const Key &key)
1004 {
1005
1006 Q_ASSERT(issuer_fpr); Q_ASSERT(*issuer_fpr); Q_ASSERT(!key.isNull());
1007
1008 std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
1009
1010 // find insertion point:
1011 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1012 const int row = std::distance(subjects.begin(), it);
1013
1014 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1015 // exists -> replace
1016 *it = key;
1017 if (!modelResetInProgress()) {
1018 Q_EMIT dataChanged(createIndex(row, 0, const_cast<char *>(issuer_fpr)), createIndex(row, NumColumns - 1, const_cast<char *>(issuer_fpr)));
1019 }
1020 } else {
1021 // doesn't exist -> insert
1022 const std::vector<Key>::const_iterator pos = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
1023 issuer_fpr, _detail::ByFingerprint<std::less>());
1024 Q_ASSERT(pos != mKeysByFingerprint.end());
1025 if (!modelResetInProgress()) {
1026 beginInsertRows(index(*pos), row, row);
1027 }
1028 subjects.insert(it, key);
1029 if (!modelResetInProgress()) {
1030 endInsertRows();
1031 }
1032 }
1033 }
1034
addKeyWithoutParent(const char * issuer_fpr,const Key & key)1035 void HierarchicalKeyListModel::addKeyWithoutParent(const char *issuer_fpr, const Key &key)
1036 {
1037
1038 Q_ASSERT(issuer_fpr); Q_ASSERT(*issuer_fpr); Q_ASSERT(!key.isNull());
1039
1040 std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
1041
1042 // find insertion point:
1043 const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
1044
1045 if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0)
1046 // exists -> replace
1047 {
1048 *it = key;
1049 } else
1050 // doesn't exist -> insert
1051 {
1052 subjects.insert(it, key);
1053 }
1054
1055 addTopLevelKey(key);
1056 }
1057
addTopLevelKey(const Key & key)1058 void HierarchicalKeyListModel::addTopLevelKey(const Key &key)
1059 {
1060
1061 // find insertion point:
1062 const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
1063 const int row = std::distance(mTopLevels.begin(), it);
1064
1065 if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
1066 // exists -> replace
1067 *it = key;
1068 if (!modelResetInProgress()) {
1069 Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
1070 }
1071 } else {
1072 // doesn't exist -> insert
1073 if (!modelResetInProgress()) {
1074 beginInsertRows(QModelIndex(), row, row);
1075 }
1076 mTopLevels.insert(it, key);
1077 if (!modelResetInProgress()) {
1078 endInsertRows();
1079 }
1080 }
1081
1082 }
1083
1084 // sorts 'keys' such that parent always come before their children:
topological_sort(const std::vector<Key> & keys)1085 static std::vector<Key> topological_sort(const std::vector<Key> &keys)
1086 {
1087
1088 boost::adjacency_list<> graph(keys.size());
1089
1090 // add edges from children to parents:
1091 for (unsigned int i = 0, end = keys.size(); i != end; ++i) {
1092 const char *const issuer_fpr = cleanChainID(keys[i]);
1093 if (!issuer_fpr || !*issuer_fpr) {
1094 continue;
1095 }
1096 const std::vector<Key>::const_iterator it
1097 = Kleo::binary_find(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
1098 if (it == keys.end()) {
1099 continue;
1100 }
1101 add_edge(i, std::distance(keys.begin(), it), graph);
1102 }
1103
1104 std::vector<int> order;
1105 order.reserve(keys.size());
1106 topological_sort(graph, std::back_inserter(order));
1107
1108 Q_ASSERT(order.size() == keys.size());
1109
1110 std::vector<Key> result;
1111 result.reserve(keys.size());
1112 for (int i : std::as_const(order)) {
1113 result.push_back(keys[i]);
1114 }
1115 return result;
1116 }
1117
doAddKeys(const std::vector<Key> & keys)1118 QList<QModelIndex> HierarchicalKeyListModel::doAddKeys(const std::vector<Key> &keys)
1119 {
1120 Q_ASSERT(std::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
1121
1122 if (keys.empty()) {
1123 return QList<QModelIndex>();
1124 }
1125
1126 const std::vector<Key> oldKeys = mKeysByFingerprint;
1127
1128 std::vector<Key> merged;
1129 merged.reserve(keys.size() + mKeysByFingerprint.size());
1130 std::set_union(keys.begin(), keys.end(),
1131 mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
1132 std::back_inserter(merged), _detail::ByFingerprint<std::less>());
1133
1134 mKeysByFingerprint = merged;
1135
1136 std::set<Key, _detail::ByFingerprint<std::less> > changedParents;
1137
1138 const auto topologicalSortedList = topological_sort(keys);
1139 for (const Key &key : topologicalSortedList) {
1140
1141 // check to see whether this key is a parent for a previously parent-less group:
1142 const char *const fpr = key.primaryFingerprint();
1143 if (!fpr || !*fpr) {
1144 continue;
1145 }
1146
1147 const bool keyAlreadyExisted = std::binary_search(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>());
1148
1149 const Map::iterator it = mKeysByNonExistingParent.find(fpr);
1150 const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
1151 if (it != mKeysByNonExistingParent.end()) {
1152 mKeysByNonExistingParent.erase(it);
1153 }
1154
1155 // Step 1: For new keys, remove children from toplevel:
1156
1157 if (!keyAlreadyExisted) {
1158 auto last = mTopLevels.begin();
1159 auto lastFP = mKeysByFingerprint.begin();
1160
1161 for (const Key &k : std::as_const(children)) {
1162 last = Kleo::binary_find(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
1163 Q_ASSERT(last != mTopLevels.end());
1164 const int row = std::distance(mTopLevels.begin(), last);
1165
1166 lastFP = Kleo::binary_find(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
1167 Q_ASSERT(lastFP != mKeysByFingerprint.end());
1168
1169 Q_EMIT rowAboutToBeMoved(QModelIndex(), row);
1170 if (!modelResetInProgress()) {
1171 beginRemoveRows(QModelIndex(), row, row);
1172 }
1173 last = mTopLevels.erase(last);
1174 lastFP = mKeysByFingerprint.erase(lastFP);
1175 if (!modelResetInProgress()) {
1176 endRemoveRows();
1177 }
1178 }
1179 }
1180 // Step 2: add/update key
1181
1182 const char *const issuer_fpr = cleanChainID(key);
1183 if (!issuer_fpr || !*issuer_fpr)
1184 // root or something...
1185 {
1186 addTopLevelKey(key);
1187 } else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>()))
1188 // parent exists...
1189 {
1190 addKeyWithParent(issuer_fpr, key);
1191 } else
1192 // parent doesn't exist yet...
1193 {
1194 addKeyWithoutParent(issuer_fpr, key);
1195 }
1196
1197 const QModelIndex key_idx = index(key);
1198 QModelIndex key_parent = key_idx.parent();
1199 while (key_parent.isValid()) {
1200 changedParents.insert(doMapToKey(key_parent));
1201 key_parent = key_parent.parent();
1202 }
1203
1204 // Step 3: Add children to new parent ( == key )
1205
1206 if (!keyAlreadyExisted && !children.empty()) {
1207 addKeys(children);
1208 const QModelIndex new_parent = index(key);
1209 // Q_EMIT the rowMoved() signals in reversed direction, so the
1210 // implementation can use a stack for mapping.
1211 for (int i = children.size() - 1; i >= 0; --i) {
1212 Q_EMIT rowMoved(new_parent, i);
1213 }
1214 }
1215 }
1216 //Q_EMIT dataChanged for all parents with new children. This triggers KeyListSortFilterProxyModel to
1217 //show a parent node if it just got children matching the proxy's filter
1218 if (!modelResetInProgress()) {
1219 for (const Key &i : std::as_const(changedParents)) {
1220 const QModelIndex idx = index(i);
1221 if (idx.isValid()) {
1222 Q_EMIT dataChanged(idx.sibling(idx.row(), 0), idx.sibling(idx.row(), NumColumns - 1));
1223 }
1224 }
1225 }
1226 return indexes(keys);
1227 }
1228
doRemoveKey(const Key & key)1229 void HierarchicalKeyListModel::doRemoveKey(const Key &key)
1230 {
1231 const QModelIndex idx = index(key);
1232 if (!idx.isValid()) {
1233 return;
1234 }
1235
1236 const char *const fpr = key.primaryFingerprint();
1237 if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
1238 //handle non-leave nodes:
1239 std::vector<Key> keys = mKeysByFingerprint;
1240 const std::vector<Key>::iterator it = Kleo::binary_find(keys.begin(), keys.end(),
1241 key, _detail::ByFingerprint<std::less>());
1242 if (it == keys.end()) {
1243 return;
1244 }
1245 keys.erase(it);
1246 // FIXME for simplicity, we just clear the model and re-add all keys minus the removed one. This is suboptimal,
1247 // but acceptable given that deletion of non-leave nodes is rather rare.
1248 clear(Keys);
1249 addKeys(keys);
1250 return;
1251 }
1252
1253 //handle leave nodes:
1254
1255 const std::vector<Key>::iterator it = Kleo::binary_find(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
1256 key, _detail::ByFingerprint<std::less>());
1257
1258 Q_ASSERT(it != mKeysByFingerprint.end());
1259 Q_ASSERT(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
1260 Q_ASSERT(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
1261
1262 if (!modelResetInProgress()) {
1263 beginRemoveRows(parent(idx), idx.row(), idx.row());
1264 }
1265 mKeysByFingerprint.erase(it);
1266
1267 const char *const issuer_fpr = cleanChainID(key);
1268
1269 const std::vector<Key>::iterator tlIt = Kleo::binary_find(mTopLevels.begin(), mTopLevels.end(),
1270 key, _detail::ByFingerprint<std::less>());
1271 if (tlIt != mTopLevels.end()) {
1272 mTopLevels.erase(tlIt);
1273 }
1274
1275 if (issuer_fpr && *issuer_fpr) {
1276 const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
1277 if (nexIt != mKeysByNonExistingParent.end()) {
1278 const std::vector<Key>::iterator eit = Kleo::binary_find(nexIt->second.begin(), nexIt->second.end(),
1279 key, _detail::ByFingerprint<std::less>());
1280 if (eit != nexIt->second.end()) {
1281 nexIt->second.erase(eit);
1282 }
1283 if (nexIt->second.empty()) {
1284 mKeysByNonExistingParent.erase(nexIt);
1285 }
1286 }
1287
1288 const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
1289 if (exIt != mKeysByExistingParent.end()) {
1290 const std::vector<Key>::iterator eit = Kleo::binary_find(exIt->second.begin(), exIt->second.end(),
1291 key, _detail::ByFingerprint<std::less>());
1292 if (eit != exIt->second.end()) {
1293 exIt->second.erase(eit);
1294 }
1295 if (exIt->second.empty()) {
1296 mKeysByExistingParent.erase(exIt);
1297 }
1298 }
1299 }
1300 if (!modelResetInProgress()) {
1301 endRemoveRows();
1302 }
1303 }
1304
doMapToGroup(const QModelIndex & idx) const1305 KeyGroup HierarchicalKeyListModel::doMapToGroup(const QModelIndex &idx) const
1306 {
1307 Q_ASSERT(idx.isValid());
1308 if (idx.parent().isValid()) {
1309 // groups are always top-level
1310 return KeyGroup();
1311 }
1312
1313 if (static_cast<unsigned>(idx.row()) >= mTopLevels.size()
1314 && static_cast<unsigned>(idx.row()) < mTopLevels.size() + mGroups.size()
1315 && idx.column() < NumColumns) {
1316 return mGroups[ idx.row() - mTopLevels.size() ];
1317 } else {
1318 return KeyGroup();
1319 }
1320 }
1321
doMapFromGroup(const KeyGroup & group,int column) const1322 QModelIndex HierarchicalKeyListModel::doMapFromGroup(const KeyGroup &group, int column) const
1323 {
1324 Q_ASSERT(!group.isNull());
1325 const auto it = std::find_if(mGroups.cbegin(), mGroups.cend(),
1326 [group](const KeyGroup &g) {
1327 return g.source() == group.source() && g.id() == group.id();
1328 });
1329 if (it == mGroups.cend()) {
1330 return QModelIndex();
1331 } else {
1332 return createIndex(it - mGroups.cbegin() + mTopLevels.size(), column);
1333 }
1334 }
1335
doSetGroups(const std::vector<KeyGroup> & groups)1336 void HierarchicalKeyListModel::doSetGroups(const std::vector<KeyGroup> &groups)
1337 {
1338 Q_ASSERT(mGroups.empty()); // ensure that groups have been cleared
1339 const int first = mTopLevels.size();
1340 const int last = first + groups.size() - 1;
1341 if (!modelResetInProgress()) {
1342 beginInsertRows(QModelIndex(), first, last);
1343 }
1344 mGroups = groups;
1345 if (!modelResetInProgress()) {
1346 endInsertRows();
1347 }
1348 }
1349
doAddGroup(const KeyGroup & group)1350 QModelIndex HierarchicalKeyListModel::doAddGroup(const KeyGroup &group)
1351 {
1352 const int newRow = lastGroupRow() + 1;
1353 if (!modelResetInProgress()) {
1354 beginInsertRows(QModelIndex(), newRow, newRow);
1355 }
1356 mGroups.push_back(group);
1357 if (!modelResetInProgress()) {
1358 endInsertRows();
1359 }
1360 return createIndex(newRow, 0);
1361 }
1362
doSetGroupData(const QModelIndex & index,const KeyGroup & group)1363 bool HierarchicalKeyListModel::doSetGroupData(const QModelIndex &index, const KeyGroup &group)
1364 {
1365 if (group.isNull()) {
1366 return false;
1367 }
1368 const int groupIndex = this->groupIndex(index);
1369 if (groupIndex == -1) {
1370 return false;
1371 }
1372 mGroups[groupIndex] = group;
1373 if (!modelResetInProgress()) {
1374 Q_EMIT dataChanged(createIndex(index.row(), 0), createIndex(index.row(), NumColumns - 1));
1375 }
1376 return true;
1377 }
1378
doRemoveGroup(const KeyGroup & group)1379 bool HierarchicalKeyListModel::doRemoveGroup(const KeyGroup &group)
1380 {
1381 const QModelIndex modelIndex = doMapFromGroup(group, 0);
1382 if (!modelIndex.isValid()) {
1383 return false;
1384 }
1385 const int groupIndex = this->groupIndex(modelIndex);
1386 Q_ASSERT(groupIndex != -1);
1387 if (groupIndex == -1) {
1388 return false;
1389 }
1390 if (!modelResetInProgress()) {
1391 beginRemoveRows(QModelIndex(), modelIndex.row(), modelIndex.row());
1392 }
1393 mGroups.erase(mGroups.begin() + groupIndex);
1394 if (!modelResetInProgress()) {
1395 endRemoveRows();
1396 }
1397 return true;
1398 }
1399
useKeyCache(bool value,KeyList::Options options)1400 void AbstractKeyListModel::useKeyCache(bool value, KeyList::Options options)
1401 {
1402 d->m_keyListOptions = options;
1403 d->m_useKeyCache = value;
1404 if (!d->m_useKeyCache) {
1405 clear(All);
1406 } else {
1407 d->updateFromKeyCache();
1408 }
1409 connect(KeyCache::instance().get(), &KeyCache::keysMayHaveChanged,
1410 this, [this] { d->updateFromKeyCache(); });
1411 }
1412
1413 // static
createFlatKeyListModel(QObject * p)1414 AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(QObject *p)
1415 {
1416 AbstractKeyListModel *const m = new FlatKeyListModel(p);
1417 #ifdef KLEO_MODEL_TEST
1418 new QAbstractItemModelTester(m, p);
1419 #endif
1420 return m;
1421 }
1422
1423 // static
createHierarchicalKeyListModel(QObject * p)1424 AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(QObject *p)
1425 {
1426 AbstractKeyListModel *const m = new HierarchicalKeyListModel(p);
1427 #ifdef KLEO_MODEL_TEST
1428 new QAbstractItemModelTester(m, p);
1429 #endif
1430 return m;
1431 }
1432
1433 #include "keylistmodel.moc"
1434
1435 /*!
1436 \fn AbstractKeyListModel::rowAboutToBeMoved( const QModelIndex & old_parent, int old_row )
1437
1438 Emitted before the removal of a row from that model. It will later
1439 be added to the model again, in response to which rowMoved() will be
1440 emitted. If multiple rows are moved in one go, multiple
1441 rowAboutToBeMoved() signals are emitted before the corresponding
1442 number of rowMoved() signals is emitted - in reverse order.
1443
1444 This works around the absence of move semantics in
1445 QAbstractItemModel. Clients can maintain a stack to perform the
1446 QModelIndex-mapping themselves, or, e.g., to preserve the selection
1447 status of the row:
1448
1449 \code
1450 std::vector<bool> mMovingRowWasSelected; // transient, used when rows are moved
1451 // ...
1452 void slotRowAboutToBeMoved( const QModelIndex & p, int row ) {
1453 mMovingRowWasSelected.push_back( selectionModel()->isSelected( model()->index( row, 0, p ) ) );
1454 }
1455 void slotRowMoved( const QModelIndex & p, int row ) {
1456 const bool wasSelected = mMovingRowWasSelected.back();
1457 mMovingRowWasSelected.pop_back();
1458 if ( wasSelected )
1459 selectionModel()->select( model()->index( row, 0, p ), Select|Rows );
1460 }
1461 \endcode
1462
1463 A similar mechanism could be used to preserve the current item during moves.
1464 */
1465
1466 /*!
1467 \fn AbstractKeyListModel::rowMoved( const QModelIndex & new_parent, int new_parent )
1468
1469 See rowAboutToBeMoved()
1470 */
1471