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"