File indexing completed on 2024-06-23 05:13:49

0001 /*  crypto/gui/certificateselectionline.cpp
0002 
0003     This file is part of Kleopatra, the KDE keymanager
0004     SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
0005 
0006     SPDX-FileCopyrightText: 2016 Bundesamt für Sicherheit in der Informationstechnik
0007     SPDX-FileContributor: Intevation GmbH
0008 
0009     SPDX-License-Identifier: GPL-2.0-or-later
0010 */
0011 #include "certificateselectionline.h"
0012 
0013 #include <QComboBox>
0014 #include <QLabel>
0015 #include <QStackedWidget>
0016 #include <QStyle>
0017 #include <QStyleOptionComboBox>
0018 #include <QStylePainter>
0019 #include <QToolButton>
0020 
0021 #include "utils/kleo_assert.h"
0022 
0023 #include <KLocalizedString>
0024 
0025 #include <Libkleo/Formatting>
0026 #include <Libkleo/Predicates>
0027 
0028 using namespace Kleo;
0029 using namespace GpgME;
0030 
0031 Q_DECLARE_METATYPE(GpgME::Key)
0032 
0033 namespace
0034 {
0035 
0036 static QString make_initial_text(const std::vector<Key> &keys)
0037 {
0038     if (keys.empty()) {
0039         return i18n("(no matching certificates found)");
0040     } else {
0041         return i18n("Please select a certificate");
0042     }
0043 }
0044 
0045 // A QComboBox with an initial text (as known from web browsers)
0046 //
0047 // only works with read-only QComboBoxen, doesn't affect sizeHint
0048 // as it should...
0049 //
0050 class ComboBox : public QComboBox
0051 {
0052     Q_OBJECT
0053     Q_PROPERTY(QString initialText READ initialText WRITE setInitialText)
0054     Q_PROPERTY(QIcon initialIcon READ initialIcon WRITE setInitialIcon)
0055 public:
0056     explicit ComboBox(QWidget *parent = nullptr)
0057         : QComboBox(parent)
0058         , m_initialText()
0059         , m_initialIcon()
0060     {
0061     }
0062 
0063     explicit ComboBox(const QString &initialText, QWidget *parent = nullptr)
0064         : QComboBox(parent)
0065         , m_initialText(initialText)
0066         , m_initialIcon()
0067     {
0068     }
0069 
0070     explicit ComboBox(const QIcon &initialIcon, const QString &initialText, QWidget *parent = nullptr)
0071         : QComboBox(parent)
0072         , m_initialText(initialText)
0073         , m_initialIcon(initialIcon)
0074     {
0075     }
0076 
0077     [[nodiscard]] QString initialText() const
0078     {
0079         return m_initialText;
0080     }
0081     [[nodiscard]] QIcon initialIcon() const
0082     {
0083         return m_initialIcon;
0084     }
0085 
0086 public Q_SLOTS:
0087     void setInitialText(const QString &txt)
0088     {
0089         if (txt == m_initialText) {
0090             return;
0091         }
0092         m_initialText = txt;
0093         if (currentIndex() == -1) {
0094             update();
0095         }
0096     }
0097     void setInitialIcon(const QIcon &icon)
0098     {
0099         if (icon.cacheKey() == m_initialIcon.cacheKey()) {
0100             return;
0101         }
0102         m_initialIcon = icon;
0103         if (currentIndex() == -1) {
0104             update();
0105         }
0106     }
0107 
0108 protected:
0109     void paintEvent(QPaintEvent *) override
0110     {
0111         QStylePainter p(this);
0112         p.setPen(palette().color(QPalette::Text));
0113         QStyleOptionComboBox opt;
0114         initStyleOption(&opt);
0115         p.drawComplexControl(QStyle::CC_ComboBox, opt);
0116 
0117         if (currentIndex() == -1) {
0118             opt.currentText = m_initialText;
0119             opt.currentIcon = m_initialIcon;
0120         }
0121         p.drawControl(QStyle::CE_ComboBoxLabel, opt);
0122     }
0123 
0124 private:
0125     QString m_initialText;
0126     QIcon m_initialIcon;
0127 };
0128 } // anonymous namespace
0129 
0130 class Kleo::KeysComboBox : public ComboBox
0131 {
0132     Q_OBJECT
0133 public:
0134     explicit KeysComboBox(QWidget *parent = nullptr)
0135         : ComboBox(parent)
0136     {
0137     }
0138     explicit KeysComboBox(const QString &initialText, QWidget *parent = nullptr)
0139         : ComboBox(initialText, parent)
0140     {
0141     }
0142     explicit KeysComboBox(const std::vector<Key> &keys, QWidget *parent = nullptr)
0143         : ComboBox(make_initial_text(keys), parent)
0144     {
0145         setKeys(keys);
0146     }
0147 
0148     void setKeys(const std::vector<Key> &keys)
0149     {
0150         clear();
0151         for (const Key &key : keys) {
0152             addItem(Formatting::formatForComboBox(key), QVariant::fromValue(key));
0153         }
0154     }
0155 
0156     std::vector<Key> keys() const
0157     {
0158         std::vector<Key> result;
0159         result.reserve(count());
0160         for (int i = 0, end = count(); i != end; ++i) {
0161             result.push_back(qvariant_cast<Key>(itemData(i)));
0162         }
0163         return result;
0164     }
0165 
0166     int findOrAdd(const Key &key)
0167     {
0168         for (int i = 0, end = count(); i != end; ++i)
0169             if (_detail::ByFingerprint<std::equal_to>()(key, qvariant_cast<Key>(itemData(i)))) {
0170                 return i;
0171             }
0172         insertItem(0, Formatting::formatForComboBox(key), QVariant::fromValue(key));
0173         return 0;
0174     }
0175 
0176     void addAndSelectCertificate(const Key &key)
0177     {
0178         setCurrentIndex(findOrAdd(key));
0179     }
0180 
0181     Key currentKey() const
0182     {
0183         return qvariant_cast<Key>(itemData(currentIndex()));
0184     }
0185 };
0186 
0187 CertificateSelectionLine::CertificateSelectionLine(const QString &toFrom,
0188                                                    const QString &mailbox,
0189                                                    const std::vector<Key> &pgp,
0190                                                    bool pgpAmbig,
0191                                                    const std::vector<Key> &cms,
0192                                                    bool cmsAmbig,
0193                                                    QWidget *q,
0194                                                    QGridLayout &glay)
0195     : pgpAmbiguous(pgpAmbig)
0196     , cmsAmbiguous(cmsAmbig)
0197     , mToFromLB(new QLabel(toFrom, q))
0198     , mMailboxLB(new QLabel(mailbox, q))
0199     , mSbox(new QStackedWidget(q))
0200     , mPgpCB(new KeysComboBox(pgp, mSbox))
0201     , mCmsCB(new KeysComboBox(cms, mSbox))
0202     , noProtocolCB(new KeysComboBox(i18n("(please choose between OpenPGP and S/MIME first)"), mSbox))
0203     , mToolTB(new QToolButton(q))
0204 {
0205     QFont bold;
0206     bold.setBold(true);
0207     mToFromLB->setFont(bold);
0208 
0209     mMailboxLB->setTextFormat(Qt::PlainText);
0210     mToolTB->setText(i18n("..."));
0211 
0212     mPgpCB->setEnabled(!pgp.empty());
0213     mCmsCB->setEnabled(!cms.empty());
0214     noProtocolCB->setEnabled(false);
0215 
0216     mPgpCB->setKeys(pgp);
0217     if (pgpAmbiguous) {
0218         mPgpCB->setCurrentIndex(-1);
0219     }
0220 
0221     mCmsCB->setKeys(cms);
0222     if (cmsAmbiguous) {
0223         mCmsCB->setCurrentIndex(-1);
0224     }
0225 
0226     mSbox->addWidget(mPgpCB);
0227     mSbox->addWidget(mCmsCB);
0228     mSbox->addWidget(noProtocolCB);
0229     mSbox->setCurrentWidget(noProtocolCB);
0230 
0231     const int row = glay.rowCount();
0232     int col = 0;
0233     glay.addWidget(mToFromLB, row, col++);
0234     glay.addWidget(mMailboxLB, row, col++);
0235     glay.addWidget(mSbox, row, col++);
0236     glay.addWidget(mToolTB, row, col++);
0237     Q_ASSERT(col == NumColumns);
0238 
0239     q->connect(mPgpCB, SIGNAL(currentIndexChanged(int)), SLOT(slotCompleteChanged()));
0240     q->connect(mCmsCB, SIGNAL(currentIndexChanged(int)), SLOT(slotCompleteChanged()));
0241     q->connect(mToolTB, SIGNAL(clicked()), SLOT(slotCertificateSelectionDialogRequested()));
0242 }
0243 
0244 QString CertificateSelectionLine::mailboxText() const
0245 {
0246     return mMailboxLB->text();
0247 }
0248 
0249 void CertificateSelectionLine::addAndSelectCertificate(const Key &key) const
0250 {
0251     if (KeysComboBox *const cb = comboBox(key.protocol())) {
0252         cb->addAndSelectCertificate(key);
0253         cb->setEnabled(true);
0254     }
0255 }
0256 
0257 void CertificateSelectionLine::showHide(Protocol proto, bool &first, bool showAll, bool op) const
0258 {
0259     if (op && (showAll || wasInitiallyAmbiguous(proto))) {
0260         mToFromLB->setVisible(first);
0261         first = false;
0262 
0263         QFont font = mMailboxLB->font();
0264         font.setBold(wasInitiallyAmbiguous(proto));
0265         mMailboxLB->setFont(font);
0266 
0267         mSbox->setCurrentIndex(proto);
0268 
0269         mMailboxLB->show();
0270         mSbox->show();
0271         mToolTB->show();
0272     } else {
0273         mToFromLB->hide();
0274         mMailboxLB->hide();
0275         mSbox->hide();
0276         mToolTB->hide();
0277     }
0278 }
0279 
0280 bool CertificateSelectionLine::wasInitiallyAmbiguous(Protocol proto) const
0281 {
0282     return (proto == OpenPGP && pgpAmbiguous) || (proto == CMS && cmsAmbiguous);
0283 }
0284 
0285 bool CertificateSelectionLine::isStillAmbiguous(Protocol proto) const
0286 {
0287     kleo_assert(proto == OpenPGP || proto == CMS);
0288     const KeysComboBox *const cb = comboBox(proto);
0289     return cb->currentIndex() == -1;
0290 }
0291 
0292 Key CertificateSelectionLine::key(Protocol proto) const
0293 {
0294     kleo_assert(proto == OpenPGP || proto == CMS);
0295     const KeysComboBox *const cb = comboBox(proto);
0296     return cb->currentKey();
0297 }
0298 
0299 const QToolButton *CertificateSelectionLine::toolButton() const
0300 {
0301     return mToolTB;
0302 }
0303 
0304 void CertificateSelectionLine::kill()
0305 {
0306     delete mToFromLB;
0307     delete mMailboxLB;
0308     delete mSbox;
0309     delete mToolTB;
0310 }
0311 
0312 KeysComboBox *CertificateSelectionLine::comboBox(Protocol proto) const
0313 {
0314     if (proto == OpenPGP) {
0315         return mPgpCB;
0316     }
0317     if (proto == CMS) {
0318         return mCmsCB;
0319     }
0320     return nullptr;
0321 }
0322 
0323 #include "certificateselectionline.moc"