File indexing completed on 2024-06-16 04:56:17

0001 /* -*- mode: c++; c-basic-offset: 4; indent-tabs-mode: nil; -*-
0002     view/searchbar.cpp
0003 
0004     This file is part of Kleopatra, the KDE keymanager
0005     SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
0006     SPDX-FileCopyrightText: 2022 g10 Code GmbH
0007     SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 
0012 #include <config-kleopatra.h>
0013 
0014 #include "searchbar.h"
0015 
0016 #include <Libkleo/Algorithm>
0017 #include <Libkleo/KeyCache>
0018 #include <Libkleo/KeyFilter>
0019 #include <Libkleo/KeyFilterManager>
0020 
0021 #include <KLocalizedString>
0022 
0023 #include <QComboBox>
0024 #include <QHBoxLayout>
0025 #include <QLineEdit>
0026 #include <QPushButton>
0027 #include <QSortFilterProxyModel>
0028 
0029 #include <gpgme++/key.h>
0030 
0031 #include "kleopatra_debug.h"
0032 
0033 using namespace Kleo;
0034 
0035 namespace
0036 {
0037 
0038 class ProxyModel : public QSortFilterProxyModel
0039 {
0040     Q_OBJECT
0041 
0042 public:
0043     using QSortFilterProxyModel::QSortFilterProxyModel;
0044 
0045 protected:
0046     bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const override
0047     {
0048         const QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
0049         const auto matchContexts = qvariant_cast<KeyFilter::MatchContexts>(sourceModel()->data(index, KeyFilterManager::FilterMatchContextsRole));
0050         return matchContexts & KeyFilter::Filtering;
0051     }
0052 };
0053 
0054 }
0055 
0056 class SearchBar::Private
0057 {
0058     friend class ::Kleo::SearchBar;
0059     SearchBar *const q;
0060 
0061 public:
0062     explicit Private(SearchBar *qq);
0063     ~Private();
0064 
0065 private:
0066     void slotKeyFilterChanged(int idx)
0067     {
0068         Q_EMIT q->keyFilterChanged(keyFilter(idx));
0069     }
0070 
0071     std::shared_ptr<KeyFilter> keyFilter(int idx) const
0072     {
0073         const QModelIndex mi = proxyModel->mapToSource(proxyModel->index(idx, 0));
0074         return KeyFilterManager::instance()->fromModelIndex(mi);
0075     }
0076 
0077     std::shared_ptr<KeyFilter> currentKeyFilter() const
0078     {
0079         return keyFilter(combo->currentIndex());
0080     }
0081 
0082     QString currentKeyFilterID() const
0083     {
0084         if (const std::shared_ptr<KeyFilter> f = currentKeyFilter()) {
0085             return f->id();
0086         } else {
0087             return QString();
0088         }
0089     }
0090 
0091     static auto notCertifiedKeysFilterId()
0092     {
0093         static const QString filterId = QStringLiteral("not-certified-certificates");
0094         return filterId;
0095     }
0096 
0097     void listNotCertifiedKeys() const
0098     {
0099         lineEdit->clear();
0100         combo->setCurrentIndex(combo->findData(notCertifiedKeysFilterId()));
0101         Q_EMIT q->keyFilterChanged(keyFilter(combo->currentIndex()));
0102     }
0103 
0104     void showOrHideCertifyButton() const
0105     {
0106         if (!KeyCache::instance()->initialized()) {
0107             return;
0108         }
0109         const auto filter = KeyFilterManager::instance()->keyFilterByID(notCertifiedKeysFilterId());
0110         if (filter) {
0111             if (Kleo::any_of(KeyCache::instance()->keys(), [filter](const auto &key) {
0112                     return filter->matches(key, KeyFilter::Filtering);
0113                 })) {
0114                 certifyButton->show();
0115                 return;
0116             }
0117         } else {
0118             qCDebug(KLEOPATRA_LOG) << __func__ << "Key filter with id" << notCertifiedKeysFilterId() << "not found";
0119         }
0120         certifyButton->hide();
0121     }
0122 
0123 private:
0124     ProxyModel *proxyModel;
0125     QLineEdit *lineEdit;
0126     QComboBox *combo;
0127     QPushButton *certifyButton;
0128 };
0129 
0130 SearchBar::Private::Private(SearchBar *qq)
0131     : q(qq)
0132 {
0133     auto layout = new QHBoxLayout(q);
0134     layout->setContentsMargins(0, 0, 0, 0);
0135     lineEdit = new QLineEdit(q);
0136     lineEdit->setClearButtonEnabled(true);
0137     lineEdit->setPlaceholderText(i18n("Search..."));
0138     lineEdit->setAccessibleName(i18n("Filter certificates by text"));
0139     lineEdit->setToolTip(i18n("Show only certificates that match the entered search term."));
0140     layout->addWidget(lineEdit, /*stretch=*/1);
0141     combo = new QComboBox(q);
0142     combo->setAccessibleName(i18n("Filter certificates by category"));
0143     combo->setToolTip(i18n("Show only certificates that belong to the selected category."));
0144     layout->addWidget(combo);
0145     certifyButton = new QPushButton(q);
0146     certifyButton->setIcon(QIcon::fromTheme(QStringLiteral("security-medium")));
0147     certifyButton->setAccessibleName(i18n("Show not certified certificates"));
0148     certifyButton->setToolTip(
0149         i18n("Some certificates are not yet certified. "
0150              "Click here to see a list of these certificates."
0151              "<br/><br/>"
0152              "Certification is required to make sure that the certificates "
0153              "actually belong to the identity they claim to belong to."));
0154     certifyButton->hide();
0155     layout->addWidget(certifyButton);
0156 
0157     proxyModel = new ProxyModel{q};
0158     proxyModel->setSourceModel(KeyFilterManager::instance()->model());
0159     proxyModel->sort(0, Qt::AscendingOrder);
0160     combo->setModel(proxyModel);
0161 
0162     KDAB_SET_OBJECT_NAME(layout);
0163     KDAB_SET_OBJECT_NAME(lineEdit);
0164     KDAB_SET_OBJECT_NAME(combo);
0165     KDAB_SET_OBJECT_NAME(certifyButton);
0166 
0167     connect(lineEdit, &QLineEdit::textChanged, q, &SearchBar::stringFilterChanged);
0168     connect(combo, &QComboBox::currentIndexChanged, q, [this](int index) {
0169         slotKeyFilterChanged(index);
0170     });
0171     connect(certifyButton, &QPushButton::clicked, q, [this]() {
0172         listNotCertifiedKeys();
0173     });
0174 
0175     connect(KeyCache::instance().get(), &KeyCache::keyListingDone, q, [this]() {
0176         showOrHideCertifyButton();
0177     });
0178     showOrHideCertifyButton();
0179 }
0180 
0181 SearchBar::Private::~Private()
0182 {
0183 }
0184 
0185 SearchBar::SearchBar(QWidget *parent, Qt::WindowFlags f)
0186     : QWidget(parent, f)
0187     , d(new Private(this))
0188 {
0189 }
0190 
0191 SearchBar::~SearchBar()
0192 {
0193 }
0194 
0195 void SearchBar::updateClickMessage(const QString &shortcutStr)
0196 {
0197     d->lineEdit->setPlaceholderText(i18n("Search...<%1>", shortcutStr));
0198 }
0199 
0200 // slot
0201 void SearchBar::setStringFilter(const QString &filter)
0202 {
0203     d->lineEdit->setText(filter);
0204 }
0205 
0206 // slot
0207 void SearchBar::setKeyFilter(const std::shared_ptr<KeyFilter> &kf)
0208 {
0209     const QModelIndex sourceIndex = KeyFilterManager::instance()->toModelIndex(kf);
0210     const QModelIndex proxyIndex = d->proxyModel->mapFromSource(sourceIndex);
0211     if (proxyIndex.isValid()) {
0212         d->combo->setCurrentIndex(proxyIndex.row());
0213     } else {
0214         d->combo->setCurrentIndex(0);
0215     }
0216 }
0217 
0218 // slot
0219 void SearchBar::setChangeStringFilterEnabled(bool on)
0220 {
0221     d->lineEdit->setEnabled(on);
0222 }
0223 
0224 // slot
0225 void SearchBar::setChangeKeyFilterEnabled(bool on)
0226 {
0227     d->combo->setEnabled(on);
0228 }
0229 
0230 QLineEdit *SearchBar::lineEdit() const
0231 {
0232     return d->lineEdit;
0233 }
0234 
0235 #include "moc_searchbar.cpp"
0236 #include "searchbar.moc"