Libkleo

useridselectioncombo.cpp
1/* This file is part of Kleopatra, the KDE keymanager
2 SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include <config-libkleo.h>
8
9#include "useridselectioncombo.h"
10
11#include "progressbar.h"
12
13#include <libkleo/defaultkeyfilter.h>
14#include <libkleo/dn.h>
15#include <libkleo/formatting.h>
16#include <libkleo/keycache.h>
17#include <libkleo/keylist.h>
18#include <libkleo/keylistmodel.h>
19#include <libkleo/keylistsortfilterproxymodel.h>
20#include <libkleo/useridproxymodel.h>
21
22#include <kleo_ui_debug.h>
23
24#include <KLocalizedString>
25
26#include <QList>
27#include <QSortFilterProxyModel>
28#include <QTimer>
29
30#include <gpgme++/key.h>
31
32using namespace Kleo;
33
34#if !UNITY_BUILD
35Q_DECLARE_METATYPE(GpgME::Key)
36#endif
37namespace
38{
39class SortFilterProxyModel : public KeyListSortFilterProxyModel
40{
41 Q_OBJECT
42
43public:
44 using KeyListSortFilterProxyModel::KeyListSortFilterProxyModel;
45
46 void setAlwaysAcceptedKey(const QString &fingerprint)
47 {
48 if (fingerprint == mFingerprint) {
49 return;
50 }
51 mFingerprint = fingerprint;
52 invalidate();
53 }
54
55protected:
56 bool filterAcceptsRow(int source_row, const QModelIndex &source_parent) const override
57 {
58 if (!mFingerprint.isEmpty()) {
59 const QModelIndex index = sourceModel()->index(source_row, 0, source_parent);
60 const auto fingerprint = sourceModel()->data(index, KeyList::FingerprintRole).toString();
61 if (fingerprint == mFingerprint) {
62 return true;
63 }
64 }
65
66 return KeyListSortFilterProxyModel::filterAcceptsRow(source_row, source_parent);
67 }
68
69private:
70 QString mFingerprint;
71};
72
73static QString formatUserID(const GpgME::UserID &userID)
74{
75 QString name;
76 QString email;
77
78 if (userID.parent().protocol() == GpgME::OpenPGP) {
79 name = QString::fromUtf8(userID.name());
80 email = QString::fromUtf8(userID.email());
81 } else {
82 const Kleo::DN dn(userID.id());
83 name = dn[QStringLiteral("CN")];
84 email = dn[QStringLiteral("EMAIL")];
85 if (name.isEmpty()) {
86 name = Kleo::DN(userID.parent().userID(0).id())[QStringLiteral("CN")];
87 }
88 }
89 return email.isEmpty() ? name : name.isEmpty() ? email : i18nc("Name <email>", "%1 <%2>", name, email);
90}
91
92class SortAndFormatCertificatesProxyModel : public QSortFilterProxyModel
93{
94 Q_OBJECT
95
96public:
97 SortAndFormatCertificatesProxyModel(KeyUsage::Flags usageFlags, QObject *parent = nullptr)
98 : QSortFilterProxyModel{parent}
99 , mIconProvider{usageFlags}
100 {
101 }
102
103private:
104 bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
105 {
106 const auto leftUserId = sourceModel()->data(left, KeyList::UserIDRole).value<GpgME::UserID>();
107 const auto rightUserId = sourceModel()->data(right, KeyList::UserIDRole).value<GpgME::UserID>();
108 if (leftUserId.isNull()) {
109 return false;
110 }
111 if (rightUserId.isNull()) {
112 return true;
113 }
114 const auto leftNameAndEmail = formatUserID(leftUserId);
115 const auto rightNameAndEmail = formatUserID(rightUserId);
116 const int cmp = QString::localeAwareCompare(leftNameAndEmail, rightNameAndEmail);
117 if (cmp) {
118 return cmp < 0;
119 }
120
121 if (leftUserId.validity() != rightUserId.validity()) {
122 return leftUserId.validity() > rightUserId.validity();
123 }
124
125 /* Both have the same validity, check which one is newer. */
126 time_t leftTime = 0;
127 for (const GpgME::Subkey &s : leftUserId.parent().subkeys()) {
128 if (s.isBad()) {
129 continue;
130 }
131 if (s.creationTime() > leftTime) {
132 leftTime = s.creationTime();
133 }
134 }
135 time_t rightTime = 0;
136 for (const GpgME::Subkey &s : rightUserId.parent().subkeys()) {
137 if (s.isBad()) {
138 continue;
139 }
140 if (s.creationTime() > rightTime) {
141 rightTime = s.creationTime();
142 }
143 }
144 if (rightTime != leftTime) {
145 return leftTime > rightTime;
146 }
147
148 // as final resort we compare the fingerprints
149 return strcmp(leftUserId.parent().primaryFingerprint(), rightUserId.parent().primaryFingerprint()) < 0;
150 }
151
152protected:
153 QVariant data(const QModelIndex &index, int role) const override
154 {
155 if (!index.isValid()) {
156 return QVariant();
157 }
158
159 const auto userId = QSortFilterProxyModel::data(index, KeyList::UserIDRole).value<GpgME::UserID>();
160 Q_ASSERT(!userId.isNull());
161 if (userId.isNull()) {
162 return QVariant();
163 }
164
165 switch (role) {
166 case Qt::DisplayRole:
168 const auto nameAndEmail = formatUserID(userId);
169 if (Kleo::KeyCache::instance()->pgpOnly()) {
170 return i18nc("Name <email> (validity, created: date)",
171 "%1 (%2, created: %3)",
172 nameAndEmail,
173 Kleo::Formatting::complianceStringShort(userId),
174 Kleo::Formatting::creationDateString(userId.parent()));
175 } else {
176 return i18nc("Name <email> (validity, type, created: date)",
177 "%1 (%2, %3, created: %4)",
178 nameAndEmail,
179 Kleo::Formatting::complianceStringShort(userId),
180 Formatting::displayName(userId.parent().protocol()),
181 Kleo::Formatting::creationDateString(userId.parent()));
182 }
183 }
184 case Qt::ToolTipRole: {
185 using namespace Kleo::Formatting;
186 return Kleo::Formatting::toolTip(userId, Validity | Issuer | Subject | Fingerprint | ExpiryDates | UserIDs);
187 }
188 case Qt::DecorationRole: {
189 return mIconProvider.icon(userId.parent());
190 }
191 default:
192 return QSortFilterProxyModel::data(index, role);
193 }
194 }
195
196private:
197 Formatting::IconProvider mIconProvider;
198};
199
200class CustomItemsProxyModel : public QSortFilterProxyModel
201{
202 Q_OBJECT
203
204private:
205 struct CustomItem {
206 QIcon icon;
207 QString text;
208 QVariant data;
209 QString toolTip;
210 };
211
212public:
213 CustomItemsProxyModel(QObject *parent = nullptr)
214 : QSortFilterProxyModel(parent)
215 {
216 }
217
218 ~CustomItemsProxyModel() override
219 {
220 qDeleteAll(mFrontItems);
221 qDeleteAll(mBackItems);
222 }
223
224 bool isCustomItem(const int row) const
225 {
226 return row < mFrontItems.count() || row >= mFrontItems.count() + QSortFilterProxyModel::rowCount();
227 }
228
229 void prependItem(const QIcon &icon, const QString &text, const QVariant &data, const QString &toolTip)
230 {
231 beginInsertRows(QModelIndex(), 0, 0);
232 mFrontItems.push_front(new CustomItem{icon, text, data, toolTip});
233 endInsertRows();
234 }
235
236 void appendItem(const QIcon &icon, const QString &text, const QVariant &data, const QString &toolTip)
237 {
238 beginInsertRows(QModelIndex(), rowCount(), rowCount());
239 mBackItems.push_back(new CustomItem{icon, text, data, toolTip});
240 endInsertRows();
241 }
242
243 void removeCustomItem(const QVariant &data)
244 {
245 for (int i = 0; i < mFrontItems.count(); ++i) {
246 if (mFrontItems[i]->data == data) {
247 beginRemoveRows(QModelIndex(), i, i);
248 delete mFrontItems.takeAt(i);
249 endRemoveRows();
250 return;
251 }
252 }
253 for (int i = 0; i < mBackItems.count(); ++i) {
254 if (mBackItems[i]->data == data) {
255 const int index = mFrontItems.count() + QSortFilterProxyModel::rowCount() + i;
256 beginRemoveRows(QModelIndex(), index, index);
257 delete mBackItems.takeAt(i);
258 endRemoveRows();
259 return;
260 }
261 }
262 }
263
264 int rowCount(const QModelIndex &parent = QModelIndex()) const override
265 {
266 return mFrontItems.count() + QSortFilterProxyModel::rowCount(parent) + mBackItems.count();
267 }
268
269 int columnCount(const QModelIndex &parent = QModelIndex()) const override
270 {
271 Q_UNUSED(parent)
272 // pretend that there is only one column to workaround a bug in
273 // QAccessibleTable which provides the accessibility interface for the
274 // pop-up of the combo box
275 return 1;
276 }
277
278 QModelIndex mapToSource(const QModelIndex &index) const override
279 {
280 if (!index.isValid()) {
281 return {};
282 }
283 if (!isCustomItem(index.row())) {
284 const int sourceRow = index.row() - mFrontItems.count();
285 return QSortFilterProxyModel::mapToSource(createIndex(sourceRow, index.column(), index.internalPointer()));
286 }
287 return {};
288 }
289
290 QModelIndex mapFromSource(const QModelIndex &source_index) const override
291 {
292 const QModelIndex idx = QSortFilterProxyModel::mapFromSource(source_index);
293 return createIndex(mFrontItems.count() + idx.row(), idx.column(), idx.internalPointer());
294 }
295
296 QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
297 {
298 if (row < 0 || row >= rowCount()) {
299 return {};
300 }
301 if (row < mFrontItems.count()) {
302 return createIndex(row, column, mFrontItems[row]);
303 } else if (row >= mFrontItems.count() + QSortFilterProxyModel::rowCount()) {
304 return createIndex(row, column, mBackItems[row - mFrontItems.count() - QSortFilterProxyModel::rowCount()]);
305 } else {
306 const QModelIndex mi = QSortFilterProxyModel::index(row - mFrontItems.count(), column, parent);
307 return createIndex(row, column, mi.internalPointer());
308 }
309 }
310
311 Qt::ItemFlags flags(const QModelIndex &index) const override
312 {
313 Q_UNUSED(index)
315 }
316
317 QModelIndex parent(const QModelIndex &) const override
318 {
319 // Flat list
320 return {};
321 }
322
323 QVariant data(const QModelIndex &index, int role) const override
324 {
325 if (!index.isValid()) {
326 return QVariant();
327 }
328
329 if (isCustomItem(index.row())) {
330 Q_ASSERT(!mFrontItems.isEmpty() || !mBackItems.isEmpty());
331 auto ci = static_cast<CustomItem *>(index.internalPointer());
332 switch (role) {
333 case Qt::DisplayRole:
334 return ci->text;
336 return ci->icon;
337 case Qt::UserRole:
338 return ci->data;
339 case Qt::ToolTipRole:
340 return ci->toolTip;
341 default:
342 return QVariant();
343 }
344 }
345
346 return QSortFilterProxyModel::data(index, role);
347 }
348
349private:
350 QList<CustomItem *> mFrontItems;
351 QList<CustomItem *> mBackItems;
352};
353
354} // anonymous namespace
355
356namespace Kleo
357{
358class UserIDSelectionComboPrivate
359{
360public:
361 UserIDSelectionComboPrivate(UserIDSelectionCombo *parent, bool secretOnly_, KeyUsage::Flags usage)
362 : wasEnabled(true)
363 , secretOnly{secretOnly_}
364 , usageFlags{usage}
365 , q{parent}
366 {
367 }
368
369 /* Selects the first key with a UID addrSpec that matches
370 * the mPerfectMatchMbox variable.
371 *
372 * The idea here is that if there are keys like:
373 *
374 * tom-store@abc.com
375 * susi-store@abc.com
376 * store@abc.com
377 *
378 * And the user wants to send a mail to "store@abc.com"
379 * the filter should still show tom and susi (because they
380 * both are part of store) but the key for "store" should
381 * be preselected.
382 *
383 * Returns true if one was selected. False otherwise. */
384 bool selectPerfectIdMatch() const
385 {
386 if (mPerfectMatchMbox.isEmpty()) {
387 return false;
388 }
389
390 for (int i = 0; i < proxyModel->rowCount(); ++i) {
391 const auto idx = proxyModel->index(i, 0, QModelIndex());
392 const auto userID = idx.data(KeyList::UserIDRole).value<GpgME::UserID>();
393 if (userID.isNull()) {
394 // WTF?
395 continue;
396 }
397 if (QString::fromStdString(userID.addrSpec()) == mPerfectMatchMbox) {
398 q->setCurrentIndex(i);
399 return true;
400 }
401 }
402 return false;
403 }
404
405 /* Updates the current key with the default key if the key matches
406 * the current key filter. */
407 void updateWithDefaultKey()
408 {
409 GpgME::Protocol filterProto = GpgME::UnknownProtocol;
410
411 const auto filter = dynamic_cast<const DefaultKeyFilter *>(sortFilterProxy->keyFilter().get());
412 if (filter && filter->isOpenPGP() == DefaultKeyFilter::Set) {
413 filterProto = GpgME::OpenPGP;
414 } else if (filter && filter->isOpenPGP() == DefaultKeyFilter::NotSet) {
415 filterProto = GpgME::CMS;
416 }
417
418 QString defaultKey = defaultKeys.value(filterProto);
419 if (defaultKey.isEmpty()) {
420 // Fallback to unknown protocol
421 defaultKey = defaultKeys.value(GpgME::UnknownProtocol);
422 }
423 // make sure that the default key is not filtered out unless it has the wrong protocol
424 if (filterProto == GpgME::UnknownProtocol) {
425 sortFilterProxy->setAlwaysAcceptedKey(defaultKey);
426 } else {
427 const auto key = KeyCache::instance()->findByFingerprint(defaultKey.toLatin1().constData());
428 if (!key.isNull() && key.protocol() == filterProto) {
429 sortFilterProxy->setAlwaysAcceptedKey(defaultKey);
430 } else {
431 sortFilterProxy->setAlwaysAcceptedKey({});
432 }
433 }
434 q->setCurrentKey(defaultKey);
435 }
436
437 void storeCurrentSelectionBeforeModelChange()
438 {
439 userIDBeforeModelChange = q->currentUserID();
440 customItemBeforeModelChange = q->currentData();
441 }
442
443 void restoreCurrentSelectionAfterModelChange()
444 {
445 if (!userIDBeforeModelChange.isNull()) {
446 q->setCurrentUserID(userIDBeforeModelChange);
447 } else if (customItemBeforeModelChange.isValid()) {
448 const auto index = q->findData(customItemBeforeModelChange);
449 if (index != -1) {
450 q->setCurrentIndex(index);
451 } else {
452 updateWithDefaultKey();
453 }
454 }
455 }
456
457 Kleo::AbstractKeyListModel *model = nullptr;
458 UserIDProxyModel *userIdProxy = nullptr;
459 SortFilterProxyModel *sortFilterProxy = nullptr;
460 SortAndFormatCertificatesProxyModel *sortAndFormatProxy = nullptr;
461 CustomItemsProxyModel *proxyModel = nullptr;
462 std::shared_ptr<Kleo::KeyCache> cache;
464 bool wasEnabled = false;
465 bool useWasEnabled = false;
466 bool secretOnly = false;
467 bool initialKeyListingDone = false;
468 QString mPerfectMatchMbox;
469 GpgME::UserID userIDBeforeModelChange;
470 QVariant customItemBeforeModelChange;
471 KeyUsage::Flags usageFlags;
472
473private:
474 UserIDSelectionCombo *const q;
475};
476
477}
478
479using namespace Kleo;
480
481UserIDSelectionCombo::UserIDSelectionCombo(QWidget *parent)
482 : UserIDSelectionCombo(true, KeyUsage::None, parent)
483{
484}
485
486UserIDSelectionCombo::UserIDSelectionCombo(bool secretOnly, QWidget *parent)
487 : UserIDSelectionCombo(secretOnly, KeyUsage::None, parent)
488{
489}
490
491UserIDSelectionCombo::UserIDSelectionCombo(KeyUsage::Flags usage, QWidget *parent)
492 : UserIDSelectionCombo{false, usage, parent}
493{
494}
495
496UserIDSelectionCombo::UserIDSelectionCombo(KeyUsage::Flag usage, QWidget *parent)
497 : UserIDSelectionCombo{false, usage, parent}
498{
499}
500
501UserIDSelectionCombo::UserIDSelectionCombo(bool secretOnly, KeyUsage::Flags usage, QWidget *parent)
502 : QComboBox(parent)
503 , d(new UserIDSelectionComboPrivate(this, secretOnly, usage))
504{
505 // set a non-empty string as accessible description to prevent screen readers
506 // from reading the tool tip which isn't meant for screen readers
507 setAccessibleDescription(QStringLiteral(" "));
508 d->model = Kleo::AbstractKeyListModel::createFlatKeyListModel(this);
509
510 d->userIdProxy = new UserIDProxyModel(this);
511 d->userIdProxy->setSourceModel(d->model);
512
513 d->sortFilterProxy = new SortFilterProxyModel(this);
514 d->sortFilterProxy->setSourceModel(d->userIdProxy);
515
516 d->sortAndFormatProxy = new SortAndFormatCertificatesProxyModel{usage, this};
517 d->sortAndFormatProxy->setSourceModel(d->sortFilterProxy);
518 // initialize dynamic sorting
519 d->sortAndFormatProxy->sort(0);
520
521 d->proxyModel = new CustomItemsProxyModel{this};
522 d->proxyModel->setSourceModel(d->sortAndFormatProxy);
523
524 setModel(d->proxyModel);
525 connect(this, &QComboBox::currentIndexChanged, this, [this](int row) {
526 if (row >= 0 && row < d->proxyModel->rowCount()) {
527 if (d->proxyModel->isCustomItem(row)) {
528 Q_EMIT customItemSelected(currentData(Qt::UserRole));
529 } else {
530 Q_EMIT currentKeyChanged(currentKey());
531 }
532 }
533 });
534
535 d->cache = Kleo::KeyCache::mutableInstance();
536
537 connect(model(), &QAbstractItemModel::rowsAboutToBeInserted, this, [this]() {
538 d->storeCurrentSelectionBeforeModelChange();
539 });
540 connect(model(), &QAbstractItemModel::rowsInserted, this, [this]() {
541 d->restoreCurrentSelectionAfterModelChange();
542 });
543 connect(model(), &QAbstractItemModel::rowsAboutToBeRemoved, this, [this]() {
544 d->storeCurrentSelectionBeforeModelChange();
545 });
546 connect(model(), &QAbstractItemModel::rowsRemoved, this, [this]() {
547 d->restoreCurrentSelectionAfterModelChange();
548 });
549 connect(model(), &QAbstractItemModel::modelAboutToBeReset, this, [this]() {
550 d->storeCurrentSelectionBeforeModelChange();
551 });
552 connect(model(), &QAbstractItemModel::modelReset, this, [this]() {
553 d->restoreCurrentSelectionAfterModelChange();
554 });
555
556 QTimer::singleShot(0, this, &UserIDSelectionCombo::init);
557}
558
559UserIDSelectionCombo::~UserIDSelectionCombo() = default;
560
561void UserIDSelectionCombo::init()
562{
563 connect(d->cache.get(), &Kleo::KeyCache::keyListingDone, this, [this]() {
564 // Set useKeyCache ensures that the cache is populated
565 // so this can be a blocking call if the cache is not initialized
566 if (!d->initialKeyListingDone) {
567 d->model->useKeyCache(true, d->secretOnly ? KeyList::SecretKeysOnly : KeyList::AllKeys);
568 }
569 d->proxyModel->removeCustomItem(QStringLiteral("-libkleo-loading-keys"));
570
571 // We use the useWasEnabled state variable to decide if we should
572 // change the enable / disable state based on the keylist done signal.
573 // If we triggered the refresh useWasEnabled is true and we want to
574 // enable / disable again after our refresh, as the refresh disabled it.
575 //
576 // But if a keyListingDone signal comes from just a generic refresh
577 // triggered by someone else we don't want to change the enable / disable
578 // state.
579 if (d->useWasEnabled) {
580 setEnabled(d->wasEnabled);
581 d->useWasEnabled = false;
582 }
583 Q_EMIT keyListingFinished();
584 });
585
586 connect(this, &UserIDSelectionCombo::keyListingFinished, this, [this]() {
587 if (!d->initialKeyListingDone) {
588 d->updateWithDefaultKey();
589 d->initialKeyListingDone = true;
590 }
591 });
592
593 if (!d->cache->initialized()) {
594 refreshKeys();
595 } else {
596 d->model->useKeyCache(true, d->secretOnly ? KeyList::SecretKeysOnly : KeyList::AllKeys);
597 Q_EMIT keyListingFinished();
598 }
599
600 connect(this, &QComboBox::currentIndexChanged, this, [this]() {
601 setToolTip(currentData(Qt::ToolTipRole).toString());
602 });
603}
604
605void UserIDSelectionCombo::setKeyFilter(const std::shared_ptr<const KeyFilter> &kf)
606{
607 d->sortFilterProxy->setKeyFilter(kf);
608 d->updateWithDefaultKey();
609}
610
611std::shared_ptr<const KeyFilter> UserIDSelectionCombo::keyFilter() const
612{
613 return d->sortFilterProxy->keyFilter();
614}
615
616void UserIDSelectionCombo::setIdFilter(const QString &id)
617{
618 d->sortFilterProxy->setFilterRegularExpression(id);
619 d->mPerfectMatchMbox = id;
620 d->updateWithDefaultKey();
621}
622
623QString UserIDSelectionCombo::idFilter() const
624{
625 return d->sortFilterProxy->filterRegularExpression().pattern();
626}
627
628GpgME::Key Kleo::UserIDSelectionCombo::currentKey() const
629{
630 return currentData(KeyList::KeyRole).value<GpgME::Key>();
631}
632
633void Kleo::UserIDSelectionCombo::setCurrentKey(const GpgME::Key &key)
634{
635 const int idx = findData(QString::fromLatin1(key.primaryFingerprint()), KeyList::FingerprintRole, Qt::MatchExactly);
636 if (idx > -1) {
637 setCurrentIndex(idx);
638 } else if (!d->selectPerfectIdMatch()) {
639 d->updateWithDefaultKey();
640 }
641 setToolTip(currentData(Qt::ToolTipRole).toString());
642}
643
644void Kleo::UserIDSelectionCombo::setCurrentKey(const QString &fingerprint)
645{
646 const auto cur = currentKey();
647 if (!cur.isNull() && !fingerprint.isEmpty() && fingerprint == QLatin1StringView(cur.primaryFingerprint())) {
648 // already set; still emit a changed signal because the current key may
649 // have become the item at the current index by changes in the underlying model
650 Q_EMIT currentKeyChanged(cur);
651 return;
652 }
653 const int idx = findData(fingerprint, KeyList::FingerprintRole, Qt::MatchExactly);
654 if (idx > -1) {
655 setCurrentIndex(idx);
656 } else if (!d->selectPerfectIdMatch()) {
657 setCurrentIndex(0);
658 }
659 setToolTip(currentData(Qt::ToolTipRole).toString());
660}
661
662GpgME::UserID Kleo::UserIDSelectionCombo::currentUserID() const
663{
664 return currentData(KeyList::UserIDRole).value<GpgME::UserID>();
665}
666
667void Kleo::UserIDSelectionCombo::setCurrentUserID(const GpgME::UserID &userID)
668{
669 for (auto i = 0; i < count(); i++) {
670 const auto &other = itemData(i, KeyList::UserIDRole).value<GpgME::UserID>();
671 if (!qstrcmp(userID.id(), other.id()) && !qstrcmp(userID.parent().primaryFingerprint(), other.parent().primaryFingerprint())) {
672 setCurrentIndex(i);
673 setToolTip(currentData(Qt::ToolTipRole).toString());
674 return;
675 }
676 }
677 if (!d->selectPerfectIdMatch()) {
678 d->updateWithDefaultKey();
679 setToolTip(currentData(Qt::ToolTipRole).toString());
680 }
681}
682
683void UserIDSelectionCombo::refreshKeys()
684{
685 d->wasEnabled = isEnabled();
686 d->useWasEnabled = true;
687 setEnabled(false);
688 const bool wasBlocked = blockSignals(true);
689 prependCustomItem(QIcon(), i18n("Loading keys ..."), QStringLiteral("-libkleo-loading-keys"));
691 blockSignals(wasBlocked);
692 d->cache->startKeyListing();
693}
694
695void UserIDSelectionCombo::appendCustomItem(const QIcon &icon, const QString &text, const QVariant &data, const QString &toolTip)
696{
697 d->proxyModel->appendItem(icon, text, data, toolTip);
698}
699
700void UserIDSelectionCombo::appendCustomItem(const QIcon &icon, const QString &text, const QVariant &data)
701{
702 appendCustomItem(icon, text, data, QString());
703}
704
705void UserIDSelectionCombo::prependCustomItem(const QIcon &icon, const QString &text, const QVariant &data, const QString &toolTip)
706{
707 d->proxyModel->prependItem(icon, text, data, toolTip);
708}
709
710void UserIDSelectionCombo::prependCustomItem(const QIcon &icon, const QString &text, const QVariant &data)
711{
712 prependCustomItem(icon, text, data, QString());
713}
714
715void UserIDSelectionCombo::removeCustomItem(const QVariant &data)
716{
717 d->proxyModel->removeCustomItem(data);
718}
719
720void Kleo::UserIDSelectionCombo::setDefaultKey(const QString &fingerprint, GpgME::Protocol proto)
721{
722 d->defaultKeys.insert(proto, fingerprint);
723 d->updateWithDefaultKey();
724}
725
726void Kleo::UserIDSelectionCombo::setDefaultKey(const QString &fingerprint)
727{
728 setDefaultKey(fingerprint, GpgME::UnknownProtocol);
729}
730
731QString Kleo::UserIDSelectionCombo::defaultKey(GpgME::Protocol proto) const
732{
733 return d->defaultKeys.value(proto);
734}
735
736QString Kleo::UserIDSelectionCombo::defaultKey() const
737{
738 return defaultKey(GpgME::UnknownProtocol);
739}
740#include "useridselectioncombo.moc"
741
742#include "moc_useridselectioncombo.cpp"
DN parser and reorderer.
Definition dn.h:27
Default implementation of key filter class.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
char * toString(const EngineQuery &query)
void modelAboutToBeReset()
void rowsAboutToBeInserted(const QModelIndex &parent, int start, int end)
void rowsAboutToBeRemoved(const QModelIndex &parent, int first, int last)
void rowsInserted(const QModelIndex &parent, int first, int last)
void rowsRemoved(const QModelIndex &parent, int first, int last)
const char * constData() const const
void setCurrentIndex(int index)
void currentIndexChanged(int index)
int findData(const QVariant &data, int role, Qt::MatchFlags flags) const const
T value(const Key &key, const T &defaultValue) const const
int column() const const
void * internalPointer() const const
bool isValid() const const
int row() const const
Q_EMITQ_EMIT
bool blockSignals(bool block)
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual QVariant data(const QModelIndex &index, int role) const const override
virtual QModelIndex index(int row, int column, const QModelIndex &parent) const const override
virtual QModelIndex mapFromSource(const QModelIndex &sourceIndex) const const override
virtual QModelIndex mapToSource(const QModelIndex &proxyIndex) const const override
virtual int rowCount(const QModelIndex &parent) const const override
QChar * data()
QString fromLatin1(QByteArrayView str)
QString fromStdString(const std::string &str)
QString fromUtf8(QByteArrayView str)
bool isEmpty() const const
int localeAwareCompare(QStringView s1, QStringView s2)
QByteArray toLatin1() const const
DisplayRole
typedef ItemFlags
MatchExactly
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
bool isValid() const const
T value() const const
bool isEnabled() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri May 10 2024 11:44:05 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.