1 /* -*- mode: c++; c-basic-offset:4 -*-
2 models/keylistmodel.cpp
4 This file is part of Kleopatra, the KDE keymanager
5 Copyright (c) 2007 Klarälvdalens Datakonsult AB
7 Kleopatra is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 Kleopatra is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 In addition, as a special exception, the copyright holders give
22 permission to link the code of this program with any edition of
23 the Qt library by Trolltech AS, Norway (or with modified versions
24 of Qt that use the same license as Qt), and distribute linked
25 combinations including the two. You must obey the GNU General
26 Public License in all respects for all of the code used other than
27 Qt. If you modify this file, you may extend this exception to
28 your version of the file, but you are not obligated to do so. If
29 you do not wish to do so, delete this exception statement from
33 #include <config-kleopatra.h>
35 #include "keylistmodel.h"
36 #include "Libkleo/Predicates"
38 #ifdef KLEO_MODEL_TEST
39 # include "modeltest.h"
42 #include <utils/formatting.h>
44 #include <Libkleo/KeyFilterManager>
45 #include <Libkleo/KeyFilter>
47 #include <KLocalizedString>
54 #include <gpgme++/key.h>
56 #ifndef Q_MOC_RUN // QTBUG-22829
57 #include <boost/bind.hpp>
58 #include <boost/graph/topological_sort.hpp>
59 #include <boost/graph/adjacency_list.hpp>
70 #include <ext/algorithm> // for is_sorted
73 using namespace GpgME
;
76 /****************************************************************************
78 ** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
79 ** Contact: Qt Software Information (qt-info@nokia.com)
81 ** This file is part of the QtCore module of the Qt Toolkit.
84 ** Licensees holding valid Qt Commercial licenses may use this file in
85 ** accordance with the Qt Commercial License Agreement provided with the
86 ** Software or, alternatively, in accordance with the terms contained in
87 ** a written agreement between you and Nokia.
90 ** GNU General Public License Usage
91 ** Alternatively, this file may be used under the terms of the GNU
92 ** General Public License versions 2.0 or 3.0 as published by the Free
93 ** Software Foundation and appearing in the file LICENSE.GPL included in
94 ** the packaging of this file. Please review the following information
95 ** to ensure GNU General Public Licensing requirements will be met:
96 ** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
97 ** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
98 ** exception, Nokia gives you certain additional rights. These rights
99 ** are described in the Nokia Qt GPL Exception version 1.3, included in
100 ** the file GPL_EXCEPTION.txt in this package.
102 ** Qt for Windows(R) Licensees
103 ** As a special exception, Nokia, as the sole copyright holder for Qt
104 ** Designer, grants users of the Qt/Eclipse Integration plug-in the
105 ** right for the Qt/Eclipse Integration to link to functionality
106 ** provided by Qt Designer and its related libraries.
108 ** If you are unsure which license is appropriate for your use, please
109 ** contact the sales department at qt-sales@nokia.com.
111 ****************************************************************************/
114 These functions are based on Peter J. Weinberger's hash function
115 (from the Dragon Book). The constant 24 in the original function
116 was replaced with 23 to produce fewer collisions on input such as
117 "a", "aa", "aaa", "aaaa", ...
120 // adjustment to null-terminated strings
121 // (c) 2008 Klarälvdalens Datakonsult AB
122 static uint
hash(const uchar
*p
)
129 if ((g
= (h
& 0xf0000000)) != 0) {
138 // end Nokia-copyrighted code
141 static inline uint
qHash(const char *data
)
144 return 1; // something != 0
146 return ::hash(reinterpret_cast<const uchar
*>(data
));
149 class AbstractKeyListModel::Private
152 Private() : m_toolTipOptions(Formatting::Validity
) {}
153 int m_toolTipOptions
;
154 mutable QHash
<const char *, QVariant
> prettyEMailCache
;
156 AbstractKeyListModel::AbstractKeyListModel(QObject
*p
)
157 : QAbstractItemModel(p
), KeyListModelInterface(), d(new Private
)
162 AbstractKeyListModel::~AbstractKeyListModel() {}
164 void AbstractKeyListModel::setToolTipOptions(int opts
)
166 d
->m_toolTipOptions
= opts
;
169 int AbstractKeyListModel::toolTipOptions() const
171 return d
->m_toolTipOptions
;
174 Key
AbstractKeyListModel::key(const QModelIndex
&idx
) const
177 return doMapToKey(idx
);
183 std::vector
<Key
> AbstractKeyListModel::keys(const QList
<QModelIndex
> &indexes
) const
185 std::vector
<Key
> result
;
186 result
.reserve(indexes
.size());
187 std::transform(indexes
.begin(), indexes
.end(),
188 std::back_inserter(result
),
189 boost::bind(&AbstractKeyListModel::key
, this, _1
));
190 result
.erase(std::unique(result
.begin(), result
.end(), _detail::ByFingerprint
<std::equal_to
>()), result
.end());
194 QModelIndex
AbstractKeyListModel::index(const Key
&key
, int col
) const
196 if (key
.isNull() || col
< 0 || col
>= NumColumns
) {
197 return QModelIndex();
199 return doMapFromKey(key
, col
);
203 QList
<QModelIndex
> AbstractKeyListModel::indexes(const std::vector
<Key
> &keys
) const
205 QList
<QModelIndex
> result
;
206 std::transform(keys
.begin(), keys
.end(),
207 std::back_inserter(result
),
208 // if some compilers are complaining about ambiguous overloads, use this line instead:
209 //bind( static_cast<QModelIndex(AbstractKeyListModel::*)(const Key&,int)const>( &AbstractKeyListModel::index ), this, _1, 0 ) );
210 boost::bind(&AbstractKeyListModel::index
, this, _1
, 0));
214 void AbstractKeyListModel::setKeys(const std::vector
<Key
> &keys
)
220 QModelIndex
AbstractKeyListModel::addKey(const Key
&key
)
222 const std::vector
<Key
> vec(1, key
);
223 const QList
<QModelIndex
> l
= doAddKeys(vec
);
224 return l
.empty() ? QModelIndex() : l
.front();
227 void AbstractKeyListModel::removeKey(const Key
&key
)
233 d
->prettyEMailCache
.remove(key
.primaryFingerprint());
236 QList
<QModelIndex
> AbstractKeyListModel::addKeys(const std::vector
<Key
> &keys
)
238 std::vector
<Key
> sorted
;
239 sorted
.reserve(keys
.size());
240 std::remove_copy_if(keys
.begin(), keys
.end(),
241 std::back_inserter(sorted
),
242 boost::bind(&Key::isNull
, _1
));
243 std::sort(sorted
.begin(), sorted
.end(), _detail::ByFingerprint
<std::less
>());
244 return doAddKeys(sorted
);
247 void AbstractKeyListModel::clear()
250 d
->prettyEMailCache
.clear();
254 int AbstractKeyListModel::columnCount(const QModelIndex
&) const
259 QVariant
AbstractKeyListModel::headerData(int section
, Qt::Orientation o
, int role
) const
261 if (o
== Qt::Horizontal
)
262 if (role
== Qt::DisplayRole
|| role
== Qt::EditRole
|| role
== Qt::ToolTipRole
)
264 case PrettyName
: return i18n("Name");
265 case PrettyEMail
: return i18n("E-Mail");
266 case ValidFrom
: return i18n("Valid From");
267 case ValidUntil
: return i18n("Valid Until");
268 case TechnicalDetails
: return i18n("Details");
269 case ShortKeyID
: return i18n("Key-ID");
275 static QVariant
returnIfValid(const QColor
&t
)
284 static QVariant
returnIfValid(const QIcon
&t
)
293 QVariant
AbstractKeyListModel::data(const QModelIndex
&index
, int role
) const
295 const Key key
= this->key(index
);
300 const int column
= index
.column();
302 if (role
== Qt::DisplayRole
|| role
== Qt::EditRole
)
305 return Formatting::prettyName(key
);
307 if (const char *const fpr
= key
.primaryFingerprint()) {
308 const QHash
<const char *, QVariant
>::const_iterator it
= d
->prettyEMailCache
.constFind(fpr
);
309 if (it
!= d
->prettyEMailCache
.constEnd()) {
312 return d
->prettyEMailCache
[fpr
] = Formatting::prettyEMail(key
);
318 if (role
== Qt::EditRole
) {
319 return Formatting::creationDate(key
);
321 return Formatting::creationDateString(key
);
324 if (role
== Qt::EditRole
) {
325 return Formatting::expirationDate(key
);
327 return Formatting::expirationDateString(key
);
329 case TechnicalDetails
:
330 return Formatting::type(key
);
332 return QString::fromLatin1(key
.shortKeyID());
336 else if (role
== Qt::ToolTipRole
) {
337 return Formatting::toolTip(key
, toolTipOptions());
338 } else if (role
== Qt::FontRole
) {
339 return KeyFilterManager::instance()->font(key
, (column
== ShortKeyID
) ? QFont(QStringLiteral("courier")) : QFont());
340 } else if (role
== Qt::DecorationRole
) {
341 return column
== Icon
? returnIfValid(KeyFilterManager::instance()->icon(key
)) : QVariant();
342 } else if (role
== Qt::BackgroundRole
) {
343 return returnIfValid(KeyFilterManager::instance()->bgColor(key
));
344 } else if (role
== Qt::ForegroundRole
) {
345 return returnIfValid(KeyFilterManager::instance()->fgColor(key
));
346 } else if (role
== FingerprintRole
) {
347 return QString::fromLatin1(key
.primaryFingerprint());
354 template <typename Base
>
355 class TableModelMixin
: public Base
358 explicit TableModelMixin(QObject
*p
= Q_NULLPTR
) : Base(p
) {}
359 ~TableModelMixin() {}
362 QModelIndex
index(int row
, int column
, const QModelIndex
&pidx
= QModelIndex()) const Q_DECL_OVERRIDE
364 return this->hasIndex(row
, column
, pidx
) ? this->createIndex(row
, column
, Q_NULLPTR
) : QModelIndex();
368 QModelIndex
parent(const QModelIndex
&) const Q_DECL_OVERRIDE
370 return QModelIndex();
372 bool hasChildren(const QModelIndex
&pidx
) const Q_DECL_OVERRIDE
374 return (pidx
.model() == this || !pidx
.isValid()) && this->rowCount(pidx
) > 0 && this->columnCount(pidx
) > 0;
378 class FlatKeyListModel
380 : public TableModelMixin
<AbstractKeyListModel
>
382 : public AbstractKeyListModel
387 explicit FlatKeyListModel(QObject
*parent
= Q_NULLPTR
);
390 int rowCount(const QModelIndex
&pidx
) const Q_DECL_OVERRIDE
392 return pidx
.isValid() ? 0 : mKeysByFingerprint
.size();
396 Key
doMapToKey(const QModelIndex
&index
) const Q_DECL_OVERRIDE
;
397 QModelIndex
doMapFromKey(const Key
&key
, int col
) const Q_DECL_OVERRIDE
;
398 QList
<QModelIndex
> doAddKeys(const std::vector
<Key
> &keys
) Q_DECL_OVERRIDE
;
399 void doRemoveKey(const Key
&key
) Q_DECL_OVERRIDE
;
400 void doClear() Q_DECL_OVERRIDE
{
401 mKeysByFingerprint
.clear();
405 std::vector
<Key
> mKeysByFingerprint
;
408 class HierarchicalKeyListModel
: public AbstractKeyListModel
412 explicit HierarchicalKeyListModel(QObject
*parent
= Q_NULLPTR
);
413 ~HierarchicalKeyListModel();
415 int rowCount(const QModelIndex
&pidx
) const Q_DECL_OVERRIDE
;
416 using AbstractKeyListModel::index
;
417 QModelIndex
index(int row
, int col
, const QModelIndex
&pidx
) const Q_DECL_OVERRIDE
;
418 QModelIndex
parent(const QModelIndex
&idx
) const Q_DECL_OVERRIDE
;
420 bool hasChildren(const QModelIndex
&pidx
) const Q_DECL_OVERRIDE
422 return rowCount(pidx
) > 0;
426 Key
doMapToKey(const QModelIndex
&index
) const Q_DECL_OVERRIDE
;
427 QModelIndex
doMapFromKey(const Key
&key
, int col
) const Q_DECL_OVERRIDE
;
428 QList
<QModelIndex
> doAddKeys(const std::vector
<Key
> &keys
) Q_DECL_OVERRIDE
;
429 void doRemoveKey(const Key
&key
) Q_DECL_OVERRIDE
;
430 void doClear() Q_DECL_OVERRIDE
{
432 mKeysByFingerprint
.clear();
433 mKeysByExistingParent
.clear();
434 mKeysByNonExistingParent
.clear();
438 void addTopLevelKey(const Key
&key
);
439 void addKeyWithParent(const char *issuer_fpr
, const Key
&key
);
440 void addKeyWithoutParent(const char *issuer_fpr
, const Key
&key
);
443 typedef std::map
< std::string
, std::vector
<Key
> > Map
;
444 std::vector
<Key
> mKeysByFingerprint
; // all keys
445 Map mKeysByExistingParent
, mKeysByNonExistingParent
; // parent->child map
446 std::vector
<Key
> mTopLevels
; // all roots + parent-less
449 static const char *cleanChainID(const Key
&key
)
454 if (const char *chid
= key
.chainID()) {
462 FlatKeyListModel::FlatKeyListModel(QObject
*p
)
463 : TableModelMixin
<AbstractKeyListModel
>(p
),
469 FlatKeyListModel::~FlatKeyListModel() {}
471 Key
FlatKeyListModel::doMapToKey(const QModelIndex
&idx
) const
473 assert(idx
.isValid());
474 if (static_cast<unsigned>(idx
.row()) < mKeysByFingerprint
.size() && idx
.column() < NumColumns
) {
475 return mKeysByFingerprint
[ idx
.row() ];
481 QModelIndex
FlatKeyListModel::doMapFromKey(const Key
&key
, int col
) const
483 assert(!key
.isNull());
484 const std::vector
<Key
>::const_iterator it
485 = std::lower_bound(mKeysByFingerprint
.begin(), mKeysByFingerprint
.end(),
486 key
, _detail::ByFingerprint
<std::less
>());
487 if (it
== mKeysByFingerprint
.end() || !_detail::ByFingerprint
<std::equal_to
>()(*it
, key
)) {
488 return QModelIndex();
490 return createIndex(it
- mKeysByFingerprint
.begin(), col
);
494 QList
<QModelIndex
> FlatKeyListModel::doAddKeys(const std::vector
<Key
> &keys
)
497 assert(__gnu_cxx::is_sorted(keys
.begin(), keys
.end(), _detail::ByFingerprint
<std::less
>()));
500 return QList
<QModelIndex
>();
503 for (std::vector
<Key
>::const_iterator it
= keys
.begin(), end
= keys
.end(); it
!= end
; ++it
) {
505 // find an insertion point:
506 const std::vector
<Key
>::iterator pos
= std::upper_bound(mKeysByFingerprint
.begin(), mKeysByFingerprint
.end(), *it
, _detail::ByFingerprint
<std::less
>());
507 const unsigned int idx
= std::distance(mKeysByFingerprint
.begin(), pos
);
509 if (idx
> 0 && qstrcmp(mKeysByFingerprint
[idx
- 1].primaryFingerprint(), it
->primaryFingerprint()) == 0) {
510 // key existed before - replace with new one:
511 mKeysByFingerprint
[idx
- 1] = *it
;
512 Q_EMIT
dataChanged(createIndex(idx
- 1, 0), createIndex(idx
- 1, NumColumns
- 1));
515 beginInsertRows(QModelIndex(), idx
, idx
);
516 mKeysByFingerprint
.insert(pos
, *it
);
521 return indexes(keys
);
524 void FlatKeyListModel::doRemoveKey(const Key
&key
)
526 const std::vector
<Key
>::iterator it
527 = qBinaryFind(mKeysByFingerprint
.begin(), mKeysByFingerprint
.end(),
528 key
, _detail::ByFingerprint
<std::less
>());
529 if (it
== mKeysByFingerprint
.end()) {
533 const unsigned int row
= std::distance(mKeysByFingerprint
.begin(), it
);
534 beginRemoveRows(QModelIndex(), row
, row
);
535 mKeysByFingerprint
.erase(it
);
539 HierarchicalKeyListModel::HierarchicalKeyListModel(QObject
*p
)
540 : AbstractKeyListModel(p
),
541 mKeysByFingerprint(),
542 mKeysByExistingParent(),
543 mKeysByNonExistingParent(),
549 HierarchicalKeyListModel::~HierarchicalKeyListModel() {}
551 int HierarchicalKeyListModel::rowCount(const QModelIndex
&pidx
) const
555 if (!pidx
.isValid()) {
556 return mTopLevels
.size();
559 if (pidx
.column() != 0) {
563 // non-toplevel item - find the number of subjects for this issuer:
564 const Key issuer
= this->key(pidx
);
565 const char *const fpr
= issuer
.primaryFingerprint();
569 const Map::const_iterator it
= mKeysByExistingParent
.find(fpr
);
570 if (it
== mKeysByExistingParent
.end()) {
573 return it
->second
.size();
576 QModelIndex
HierarchicalKeyListModel::index(int row
, int col
, const QModelIndex
&pidx
) const
579 if (row
< 0 || col
< 0 || col
>= NumColumns
) {
580 return QModelIndex();
584 if (!pidx
.isValid()) {
585 if (static_cast<unsigned>(row
) < mTopLevels
.size()) {
586 return index(mTopLevels
[row
], col
);
588 return QModelIndex();
592 // non-toplevel item - find the row'th subject of this key:
593 const Key issuer
= this->key(pidx
);
594 const char *const fpr
= issuer
.primaryFingerprint();
596 return QModelIndex();
598 const Map::const_iterator it
= mKeysByExistingParent
.find(fpr
);
599 if (it
== mKeysByExistingParent
.end() || static_cast<unsigned>(row
) >= it
->second
.size()) {
600 return QModelIndex();
602 return index(it
->second
[row
], col
);
605 QModelIndex
HierarchicalKeyListModel::parent(const QModelIndex
&idx
) const
607 const Key key
= this->key(idx
);
608 if (key
.isNull() || key
.isRoot()) {
609 return QModelIndex();
611 const std::vector
<Key
>::const_iterator it
612 = qBinaryFind(mKeysByFingerprint
.begin(), mKeysByFingerprint
.end(),
613 cleanChainID(key
), _detail::ByFingerprint
<std::less
>());
614 return it
!= mKeysByFingerprint
.end() ? index(*it
) : QModelIndex();
617 Key
HierarchicalKeyListModel::doMapToKey(const QModelIndex
&idx
) const
620 if (!idx
.isValid()) {
624 const char *const issuer_fpr
= static_cast<const char *>(idx
.internalPointer());
625 if (!issuer_fpr
|| !*issuer_fpr
) {
627 if (static_cast<unsigned>(idx
.row()) >= mTopLevels
.size()) {
630 return mTopLevels
[idx
.row()];
635 const Map::const_iterator it
636 = mKeysByExistingParent
.find(issuer_fpr
);
637 if (it
== mKeysByExistingParent
.end() || static_cast<unsigned>(idx
.row()) >= it
->second
.size()) {
640 return it
->second
[idx
.row()];
643 QModelIndex
HierarchicalKeyListModel::doMapFromKey(const Key
&key
, int col
) const
647 return QModelIndex();
650 const char *issuer_fpr
= cleanChainID(key
);
652 // we need to look in the toplevels list,...
653 const std::vector
<Key
> *v
= &mTopLevels
;
654 if (issuer_fpr
&& *issuer_fpr
) {
655 const std::map
< std::string
, std::vector
<Key
> >::const_iterator it
656 = mKeysByExistingParent
.find(issuer_fpr
);
657 // ...unless we find an existing parent:
658 if (it
!= mKeysByExistingParent
.end()) {
661 issuer_fpr
= 0; // force internalPointer to zero for toplevels
665 const std::vector
<Key
>::const_iterator it
666 = std::lower_bound(v
->begin(), v
->end(), key
, _detail::ByFingerprint
<std::less
>());
667 if (it
== v
->end() || !_detail::ByFingerprint
<std::equal_to
>()(*it
, key
)) {
668 return QModelIndex();
671 const unsigned int row
= std::distance(v
->begin(), it
);
672 return createIndex(row
, col
, const_cast<char * /* thanks, Trolls :/ */ >(issuer_fpr
));
675 void HierarchicalKeyListModel::addKeyWithParent(const char *issuer_fpr
, const Key
&key
)
678 assert(issuer_fpr
); assert(*issuer_fpr
); assert(!key
.isNull());
680 std::vector
<Key
> &subjects
= mKeysByExistingParent
[issuer_fpr
];
682 // find insertion point:
683 const std::vector
<Key
>::iterator it
= std::lower_bound(subjects
.begin(), subjects
.end(), key
, _detail::ByFingerprint
<std::less
>());
684 const int row
= std::distance(subjects
.begin(), it
);
686 if (it
!= subjects
.end() && qstricmp(it
->primaryFingerprint(), key
.primaryFingerprint()) == 0) {
689 Q_EMIT
dataChanged(createIndex(row
, 0, const_cast<char *>(issuer_fpr
)), createIndex(row
, NumColumns
- 1, const_cast<char *>(issuer_fpr
)));
691 // doesn't exist -> insert
692 const std::vector
<Key
>::const_iterator pos
= qBinaryFind(mKeysByFingerprint
.begin(), mKeysByFingerprint
.end(), issuer_fpr
, _detail::ByFingerprint
<std::less
>());
693 assert(pos
!= mKeysByFingerprint
.end());
694 beginInsertRows(index(*pos
), row
, row
);
695 subjects
.insert(it
, key
);
700 void HierarchicalKeyListModel::addKeyWithoutParent(const char *issuer_fpr
, const Key
&key
)
703 assert(issuer_fpr
); assert(*issuer_fpr
); assert(!key
.isNull());
705 std::vector
<Key
> &subjects
= mKeysByNonExistingParent
[issuer_fpr
];
707 // find insertion point:
708 const std::vector
<Key
>::iterator it
= std::lower_bound(subjects
.begin(), subjects
.end(), key
, _detail::ByFingerprint
<std::less
>());
710 if (it
!= subjects
.end() && qstricmp(it
->primaryFingerprint(), key
.primaryFingerprint()) == 0)
715 // doesn't exist -> insert
717 subjects
.insert(it
, key
);
723 void HierarchicalKeyListModel::addTopLevelKey(const Key
&key
)
726 // find insertion point:
727 const std::vector
<Key
>::iterator it
= std::lower_bound(mTopLevels
.begin(), mTopLevels
.end(), key
, _detail::ByFingerprint
<std::less
>());
728 const int row
= std::distance(mTopLevels
.begin(), it
);
730 if (it
!= mTopLevels
.end() && qstricmp(it
->primaryFingerprint(), key
.primaryFingerprint()) == 0) {
733 Q_EMIT
dataChanged(createIndex(row
, 0), createIndex(row
, NumColumns
- 1));
735 // doesn't exist -> insert
736 beginInsertRows(QModelIndex(), row
, row
);
737 mTopLevels
.insert(it
, key
);
743 // sorts 'keys' such that parent always come before their children:
744 static std::vector
<Key
> topological_sort(const std::vector
<Key
> &keys
)
747 boost::adjacency_list
<> graph(keys
.size());
749 // add edges from children to parents:
750 for (unsigned int i
= 0, end
= keys
.size(); i
!= end
; ++i
) {
751 const char *const issuer_fpr
= cleanChainID(keys
[i
]);
752 if (!issuer_fpr
|| !*issuer_fpr
) {
755 const std::vector
<Key
>::const_iterator it
756 = qBinaryFind(keys
.begin(), keys
.end(), issuer_fpr
, _detail::ByFingerprint
<std::less
>());
757 if (it
== keys
.end()) {
760 add_edge(i
, std::distance(keys
.begin(), it
), graph
);
763 std::vector
<int> order
;
764 order
.reserve(keys
.size());
765 topological_sort(graph
, std::back_inserter(order
));
767 assert(order
.size() == keys
.size());
769 std::vector
<Key
> result
;
770 result
.reserve(keys
.size());
771 Q_FOREACH (int i
, order
) {
772 result
.push_back(keys
[i
]);
777 QList
<QModelIndex
> HierarchicalKeyListModel::doAddKeys(const std::vector
<Key
> &keys
)
780 assert(__gnu_cxx::is_sorted(keys
.begin(), keys
.end(), _detail::ByFingerprint
<std::less
>()));
783 return QList
<QModelIndex
>();
786 const std::vector
<Key
> oldKeys
= mKeysByFingerprint
;
788 std::vector
<Key
> merged
;
789 merged
.reserve(keys
.size() + mKeysByFingerprint
.size());
790 std::set_union(keys
.begin(), keys
.end(),
791 mKeysByFingerprint
.begin(), mKeysByFingerprint
.end(),
792 std::back_inserter(merged
), _detail::ByFingerprint
<std::less
>());
794 mKeysByFingerprint
= merged
;
796 std::set
<Key
, _detail::ByFingerprint
<std::less
> > changedParents
;
798 Q_FOREACH (const Key
&key
, topological_sort(keys
)) {
800 // check to see whether this key is a parent for a previously parent-less group:
801 const char *const fpr
= key
.primaryFingerprint();
806 const bool keyAlreadyExisted
= qBinaryFind(oldKeys
.begin(), oldKeys
.end(), key
, _detail::ByFingerprint
<std::less
>()) != oldKeys
.end();
808 const Map::iterator it
= mKeysByNonExistingParent
.find(fpr
);
809 const std::vector
<Key
> children
= it
!= mKeysByNonExistingParent
.end() ? it
->second
: std::vector
<Key
>();
810 if (it
!= mKeysByNonExistingParent
.end()) {
811 mKeysByNonExistingParent
.erase(it
);
814 // Step 1: For new keys, remove children from toplevel:
816 if (!keyAlreadyExisted
) {
817 std::vector
<Key
>::iterator last
= mTopLevels
.begin();
818 std::vector
<Key
>::iterator lastFP
= mKeysByFingerprint
.begin();
820 Q_FOREACH (const Key
&k
, children
) {
821 last
= qBinaryFind(last
, mTopLevels
.end(), k
, _detail::ByFingerprint
<std::less
>());
822 assert(last
!= mTopLevels
.end());
823 const int row
= std::distance(mTopLevels
.begin(), last
);
825 lastFP
= qBinaryFind(lastFP
, mKeysByFingerprint
.end(), k
, _detail::ByFingerprint
<std::less
>());
826 assert(lastFP
!= mKeysByFingerprint
.end());
828 Q_EMIT
rowAboutToBeMoved(QModelIndex(), row
);
829 beginRemoveRows(QModelIndex(), row
, row
);
830 last
= mTopLevels
.erase(last
);
831 lastFP
= mKeysByFingerprint
.erase(lastFP
);
835 // Step 2: add/update key
837 const char *const issuer_fpr
= cleanChainID(key
);
838 if (!issuer_fpr
|| !*issuer_fpr
)
839 // root or something...
842 } else if (std::binary_search(mKeysByFingerprint
.begin(), mKeysByFingerprint
.end(), issuer_fpr
, _detail::ByFingerprint
<std::less
>()))
845 addKeyWithParent(issuer_fpr
, key
);
847 // parent does't exist yet...
849 addKeyWithoutParent(issuer_fpr
, key
);
852 const QModelIndex key_idx
= index(key
);
853 QModelIndex key_parent
= key_idx
.parent();
854 while (key_parent
.isValid()) {
855 changedParents
.insert(doMapToKey(key_parent
));
856 key_parent
= key_parent
.parent();
859 // Step 3: Add children to new parent ( == key )
861 if (!keyAlreadyExisted
&& !children
.empty()) {
863 const QModelIndex new_parent
= index(key
);
864 // Q_EMIT the rowMoved() signals in reversed direction, so the
865 // implementation can use a stack for mapping.
866 for (int i
= children
.size() - 1; i
>= 0; --i
) {
867 Q_EMIT
rowMoved(new_parent
, i
);
871 //Q_EMIT dataChanged for all parents with new children. This triggers KeyListSortFilterProxyModel to
872 //show a parent node if it just got children matching the proxy's filter
873 Q_FOREACH (const Key
&i
, changedParents
) {
874 const QModelIndex idx
= index(i
);
876 Q_EMIT
dataChanged(idx
.sibling(idx
.row(), 0), idx
.sibling(idx
.row(), NumColumns
- 1));
879 return indexes(keys
);
882 void HierarchicalKeyListModel::doRemoveKey(const Key
&key
)
884 const QModelIndex idx
= index(key
);
885 if (!idx
.isValid()) {
889 const char *const fpr
= key
.primaryFingerprint();
890 if (mKeysByExistingParent
.find(fpr
) != mKeysByExistingParent
.end()) {
891 //handle non-leave nodes:
892 std::vector
<Key
> keys
= mKeysByFingerprint
;
893 const std::vector
<Key
>::iterator it
= qBinaryFind(keys
.begin(), keys
.end(),
894 key
, _detail::ByFingerprint
<std::less
>());
895 if (it
== keys
.end()) {
899 // FIXME for simplicity, we just clear the model and re-add all keys minus the removed one. This is suboptimal,
900 // but acceptable given that deletion of non-leave nodes is rather rare.
906 //handle leave nodes:
908 const std::vector
<Key
>::iterator it
= qBinaryFind(mKeysByFingerprint
.begin(), mKeysByFingerprint
.end(),
909 key
, _detail::ByFingerprint
<std::less
>());
911 assert(it
!= mKeysByFingerprint
.end());
912 assert(mKeysByNonExistingParent
.find(fpr
) == mKeysByNonExistingParent
.end());
913 assert(mKeysByExistingParent
.find(fpr
) == mKeysByExistingParent
.end());
915 beginRemoveRows(parent(idx
), idx
.row(), idx
.row());
916 mKeysByFingerprint
.erase(it
);
918 const char *const issuer_fpr
= cleanChainID(key
);
920 const std::vector
<Key
>::iterator tlIt
= qBinaryFind(mTopLevels
.begin(), mTopLevels
.end(), key
, _detail::ByFingerprint
<std::less
>());
921 if (tlIt
!= mTopLevels
.end()) {
922 mTopLevels
.erase(tlIt
);
925 if (issuer_fpr
&& *issuer_fpr
) {
926 const Map::iterator nexIt
= mKeysByNonExistingParent
.find(issuer_fpr
);
927 if (nexIt
!= mKeysByNonExistingParent
.end()) {
928 const std::vector
<Key
>::iterator eit
= qBinaryFind(nexIt
->second
.begin(), nexIt
->second
.end(), key
, _detail::ByFingerprint
<std::less
>());
929 if (eit
!= nexIt
->second
.end()) {
930 nexIt
->second
.erase(eit
);
932 if (nexIt
->second
.empty()) {
933 mKeysByNonExistingParent
.erase(nexIt
);
937 const Map::iterator exIt
= mKeysByExistingParent
.find(issuer_fpr
);
938 if (exIt
!= mKeysByExistingParent
.end()) {
939 const std::vector
<Key
>::iterator eit
= qBinaryFind(exIt
->second
.begin(), exIt
->second
.end(), key
, _detail::ByFingerprint
<std::less
>());
940 if (eit
!= exIt
->second
.end()) {
941 exIt
->second
.erase(eit
);
943 if (exIt
->second
.empty()) {
944 mKeysByExistingParent
.erase(exIt
);
952 AbstractKeyListModel
*AbstractKeyListModel::createFlatKeyListModel(QObject
*p
)
954 AbstractKeyListModel
*const m
= new FlatKeyListModel(p
);
955 #ifdef KLEO_MODEL_TEST
962 AbstractKeyListModel
*AbstractKeyListModel::createHierarchicalKeyListModel(QObject
*p
)
964 AbstractKeyListModel
*const m
= new HierarchicalKeyListModel(p
);
965 #ifdef KLEO_MODEL_TEST
971 #include "keylistmodel.moc"
974 \fn AbstractKeyListModel::rowAboutToBeMoved( const QModelIndex & old_parent, int old_row )
976 Emitted before the removal of a row from that model. It will later
977 be added to the model again, in response to which rowMoved() will be
978 emitted. If multiple rows are moved in one go, multiple
979 rowAboutToBeMoved() signals are emitted before the corresponding
980 number of rowMoved() signals is emitted - in reverse order.
982 This works around the absence of move semantics in
983 QAbstractItemModel. Clients can maintain a stack to perform the
984 QModelIndex-mapping themselves, or, e.g., to preserve the selection
988 std::vector<bool> mMovingRowWasSelected; // transient, used when rows are moved
990 void slotRowAboutToBeMoved( const QModelIndex & p, int row ) {
991 mMovingRowWasSelected.push_back( selectionModel()->isSelected( model()->index( row, 0, p ) ) );
993 void slotRowMoved( const QModelIndex & p, int row ) {
994 const bool wasSelected = mMovingRowWasSelected.back();
995 mMovingRowWasSelected.pop_back();
997 selectionModel()->select( model()->index( row, 0, p ), Select|Rows );
1001 A similar mechanism could be used to preserve the current item during moves.
1005 \fn AbstractKeyListModel::rowMoved( const QModelIndex & new_parent, int new_parent )
1007 See rowAboutToBeMoved()