Libkleo

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

KDE's Doxygen guidelines are available online.