diff --git a/src/crypto/gui/decryptverifyfileswizard.cpp b/src/crypto/gui/decryptverifyfileswizard.cpp index 540a85853..4a77830de 100644 --- a/src/crypto/gui/decryptverifyfileswizard.cpp +++ b/src/crypto/gui/decryptverifyfileswizard.cpp @@ -1,253 +1,253 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/decryptverifywizard.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "decryptverifyfileswizard.h" #include "decryptverifyoperationwidget.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto; using namespace Kleo::Crypto::Gui; namespace { class HLine : public QFrame { Q_OBJECT public: explicit HLine(QWidget *p = nullptr, Qt::WindowFlags f = {}) : QFrame(p, f) { - setFrameStyle(QFrame::HLine | QFrame::Sunken); + setFrameStyle(static_cast(QFrame::HLine) | static_cast(QFrame::Sunken)); } }; class OperationsWidget : public WizardPage { Q_OBJECT public: explicit OperationsWidget(QWidget *p = nullptr); ~OperationsWidget() override; void setOutputDirectory(const QString &dir) { m_ui.outputDirectoryFNR.setFileName(dir); } QString outputDirectory() const { return m_ui.outputDirectoryFNR.fileName(); } bool useOutputDirectory() const { return m_ui.useOutputDirectoryCB.isChecked(); } void ensureIndexAvailable(unsigned int idx); DecryptVerifyOperationWidget *widget(unsigned int idx) { return m_widgets.at(idx); } bool isComplete() const override { return true; } private: std::vector m_widgets; struct UI { QCheckBox useOutputDirectoryCB; QLabel outputDirectoryLB; FileNameRequester outputDirectoryFNR; ScrollArea scrollArea; // ### replace with KDScrollArea when done QVBoxLayout vlay; QHBoxLayout hlay; explicit UI(OperationsWidget *q); } m_ui; }; } class DecryptVerifyFilesWizard::Private { friend class ::Kleo::Crypto::Gui::DecryptVerifyFilesWizard; DecryptVerifyFilesWizard *const q; public: Private(DecryptVerifyFilesWizard *qq); ~Private(); void ensureIndexAvailable(unsigned int idx) { operationsPage.ensureIndexAvailable(idx); } private: OperationsWidget operationsPage; Gui::ResultPage resultPage; }; DecryptVerifyFilesWizard::DecryptVerifyFilesWizard(QWidget *p, Qt::WindowFlags f) : Wizard(p, f), d(new Private(this)) { } DecryptVerifyFilesWizard::~DecryptVerifyFilesWizard() {} void DecryptVerifyFilesWizard::setOutputDirectory(const QString &dir) { d->operationsPage.setOutputDirectory(dir); } QString DecryptVerifyFilesWizard::outputDirectory() const { return d->operationsPage.outputDirectory(); } bool DecryptVerifyFilesWizard::useOutputDirectory() const { return d->operationsPage.useOutputDirectory(); } DecryptVerifyOperationWidget *DecryptVerifyFilesWizard::operationWidget(unsigned int idx) { d->ensureIndexAvailable(idx); return d->operationsPage.widget(idx); } void DecryptVerifyFilesWizard::onNext(int id) { if (id == OperationsPage) { QTimer::singleShot(0, this, &DecryptVerifyFilesWizard::operationPrepared); } Wizard::onNext(id); } void DecryptVerifyFilesWizard::setTaskCollection(const std::shared_ptr &coll) { kleo_assert(coll); d->resultPage.setTaskCollection(coll); } DecryptVerifyFilesWizard::Private::Private(DecryptVerifyFilesWizard *qq) : q(qq), operationsPage(q), resultPage(q) { q->setPage(DecryptVerifyFilesWizard::OperationsPage, &operationsPage); q->setPage(DecryptVerifyFilesWizard::ResultPage, &resultPage); std::vector order; order.push_back(DecryptVerifyFilesWizard::OperationsPage); order.push_back(DecryptVerifyFilesWizard::ResultPage); q->setPageOrder(order); operationsPage.setCommitPage(true); } DecryptVerifyFilesWizard::Private::~Private() {} OperationsWidget::OperationsWidget(QWidget *p) : WizardPage(p), m_widgets(), m_ui(this) { setTitle(i18n("Choose operations to be performed")); setSubTitle(i18n("Here you can check and, if needed, override " "the operations Kleopatra detected for the input given.")); setCommitPage(true); setCustomNextButton(KGuiItem(i18n("&Decrypt/Verify"))); } OperationsWidget::~OperationsWidget() {} OperationsWidget::UI::UI(OperationsWidget *q) : useOutputDirectoryCB(i18n("Create all output files in a single folder"), q), outputDirectoryLB(i18n("&Output folder:"), q), outputDirectoryFNR(q), scrollArea(q), vlay(q), hlay() { KDAB_SET_OBJECT_NAME(useOutputDirectoryCB); KDAB_SET_OBJECT_NAME(outputDirectoryLB); KDAB_SET_OBJECT_NAME(outputDirectoryFNR); KDAB_SET_OBJECT_NAME(scrollArea); KDAB_SET_OBJECT_NAME(vlay); KDAB_SET_OBJECT_NAME(hlay); outputDirectoryFNR.setFilter(QDir::Dirs); useOutputDirectoryCB.setChecked(true); connect(&useOutputDirectoryCB, &QCheckBox::toggled, &outputDirectoryLB, &QLabel::setEnabled); connect(&useOutputDirectoryCB, &QCheckBox::toggled, &outputDirectoryFNR, &FileNameRequester::setEnabled); Q_ASSERT(qobject_cast(scrollArea.widget()->layout())); static_cast(scrollArea.widget()->layout())->addStretch(1); outputDirectoryLB.setBuddy(&outputDirectoryFNR); hlay.setContentsMargins(0, 0, 0, 0); vlay.addWidget(&scrollArea, 1); vlay.addWidget(&useOutputDirectoryCB); vlay.addLayout(&hlay); hlay.addWidget(&outputDirectoryLB); hlay.addWidget(&outputDirectoryFNR); } void OperationsWidget::ensureIndexAvailable(unsigned int idx) { if (idx < m_widgets.size()) { return; } Q_ASSERT(m_ui.scrollArea.widget()); Q_ASSERT(qobject_cast(m_ui.scrollArea.widget()->layout())); QBoxLayout &blay = *static_cast(m_ui.scrollArea.widget()->layout()); for (unsigned int i = m_widgets.size(); i < idx + 1; ++i) { if (i) { blay.insertWidget(blay.count() - 1, new HLine(m_ui.scrollArea.widget())); } auto w = new DecryptVerifyOperationWidget(m_ui.scrollArea.widget()); blay.insertWidget(blay.count() - 1, w); w->show(); m_widgets.push_back(w); } } #include "decryptverifyfileswizard.moc" diff --git a/src/crypto/gui/signingcertificateselectiondialog.cpp b/src/crypto/gui/signingcertificateselectiondialog.cpp index 252ada2a5..65f6471e3 100644 --- a/src/crypto/gui/signingcertificateselectiondialog.cpp +++ b/src/crypto/gui/signingcertificateselectiondialog.cpp @@ -1,65 +1,66 @@ /* -*- mode: c++; c-basic-offset:4 -*- crypto/gui/signingcertificateselectiondialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "signingcertificateselectiondialog.h" #include "signingcertificateselectionwidget.h" #include "utils/keys.h" +#include "utils/qt-cxx20-compat.h" #include #include #include #include #include using namespace Kleo; using namespace Kleo::Crypto::Gui; SigningCertificateSelectionDialog::SigningCertificateSelectionDialog(QWidget *parent) : QDialog(parent), widget(new SigningCertificateSelectionWidget(this)) { setWindowTitle(i18nc("@title:window", "Select Signing Certificates")); auto mainLayout = new QVBoxLayout(this); mainLayout->addWidget(widget); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::accepted, this, &SigningCertificateSelectionDialog::accept); connect(buttonBox, &QDialogButtonBox::rejected, this, &SigningCertificateSelectionDialog::reject); mainLayout->addWidget(buttonBox); } SigningCertificateSelectionDialog::~SigningCertificateSelectionDialog() {} void SigningCertificateSelectionDialog::setSelectedCertificates(const CertificatePair &certificates) { widget->setSelectedCertificates(certificates); } CertificatePair SigningCertificateSelectionDialog::selectedCertificates() const { return widget->selectedCertificates(); } bool SigningCertificateSelectionDialog::rememberAsDefault() const { return widget->rememberAsDefault(); } void SigningCertificateSelectionDialog::setAllowedProtocols(const std::set &allowedProtocols) { widget->setAllowedProtocols(allowedProtocols); } diff --git a/src/dialogs/expirydialog.cpp b/src/dialogs/expirydialog.cpp index 77167dd0d..e503de127 100644 --- a/src/dialogs/expirydialog.cpp +++ b/src/dialogs/expirydialog.cpp @@ -1,328 +1,330 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/expirydialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "expirydialog.h" +#include "utils/qt-cxx20-compat.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace Kleo; using namespace Kleo::Dialogs; namespace { enum Period { Days, Weeks, Months, Years, NumPeriods }; static QDate date_by_amount_and_unit(int inAmount, int inUnit) { const QDate current = QDate::currentDate(); switch (inUnit) { case Days: return current.addDays(inAmount); case Weeks: return current.addDays(7 * inAmount); case Months: return current.addMonths(inAmount); case Years: return current.addYears(inAmount); default: Q_ASSERT(!"Should not reach here"); } return QDate(); } // these calculations should be precise enough for the forseeable future... static const double DAYS_IN_GREGORIAN_YEAR = 365.2425; static int monthsBetween(const QDate &d1, const QDate &d2) { const int days = d1.daysTo(d2); return qRound(days / DAYS_IN_GREGORIAN_YEAR * 12); } static int yearsBetween(const QDate &d1, const QDate &d2) { const int days = d1.daysTo(d2); return qRound(days / DAYS_IN_GREGORIAN_YEAR); } auto radioButtonSize(const QRadioButton *radioButton) { QStyleOptionButton opt; return radioButton->style()->sizeFromContents(QStyle::CT_RadioButton, &opt, QSize(), radioButton); } } class ExpiryDialog::Private { friend class ::Kleo::Dialogs::ExpiryDialog; ExpiryDialog *const q; public: explicit Private(Mode mode, ExpiryDialog *qq) : q{qq} , mode{mode} , inUnit{Days} , ui{mode, q} { connect(ui.inSB, &QSpinBox::valueChanged, q, [this] () { slotInAmountChanged(); }); connect(ui.inCB, qOverload(&QComboBox::currentIndexChanged), q, [this] () { slotInUnitChanged(); }); connect(ui.onCW, &QCalendarWidget::selectionChanged, q, [this] () { slotOnDateChanged(); }); connect(ui.onCW, &QCalendarWidget::currentPageChanged, q, [this] (int year, int month) { // We select the same day in the month when // a page is switched. auto date = ui.onCW->selectedDate(); if (!date.setDate(year, month, date.day())) { date.setDate(year, month, 1); } ui.onCW->setSelectedDate(date); }); Q_ASSERT(ui.inCB->currentIndex() == inUnit); } private: void slotInAmountChanged(); void slotInUnitChanged(); void slotOnDateChanged(); private: QDate inDate() const; int inAmountByDate(const QDate &date) const; private: ExpiryDialog::Mode mode; int inUnit; struct UI { QRadioButton *neverRB; QRadioButton *inRB; QSpinBox *inSB; QComboBox *inCB; QRadioButton *onRB; QCalendarWidget *onCW; QCheckBox *updateSubkeysCheckBox; explicit UI(Mode mode, Dialogs::ExpiryDialog *qq) { auto mainLayout = new QVBoxLayout{qq}; auto mainWidget = new QWidget{qq}; auto vboxLayout = new QVBoxLayout{mainWidget}; vboxLayout->setContentsMargins(0, 0, 0, 0); { auto label = new QLabel{qq}; label->setText(mode == Mode::UpdateIndividualSubkey ? i18n("Please select when to expire this subkey:") : i18n("Please select when to expire this certificate:")); vboxLayout->addWidget(label); } neverRB = new QRadioButton(i18n("Ne&ver"), mainWidget); neverRB->setChecked(false); vboxLayout->addWidget(neverRB); { auto hboxLayout = new QHBoxLayout; inRB = new QRadioButton{i18n("In"), mainWidget}; inRB->setChecked(false); hboxLayout->addWidget(inRB); inSB = new QSpinBox{mainWidget}; inSB->setEnabled(false); inSB->setAlignment(Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter); inSB->setMinimum(1); hboxLayout->addWidget(inSB); inCB = new QComboBox{mainWidget}; inCB->addItem(i18n("Days")); inCB->addItem(i18n("Weeks")); inCB->addItem(i18n("Months")); inCB->addItem(i18n("Years")); Q_ASSERT(inCB->count() == NumPeriods); inCB->setEnabled(false); hboxLayout->addWidget(inCB); hboxLayout->addStretch(1); vboxLayout->addLayout(hboxLayout); } onRB = new QRadioButton{i18n("On this da&y:"), mainWidget}; onRB->setChecked(true); vboxLayout->addWidget(onRB); { auto hboxLayout = new QHBoxLayout; hboxLayout->addSpacing(radioButtonSize(onRB).width()); onCW = new QCalendarWidget{mainWidget}; onCW->setGridVisible(true); onCW->setMinimumDate(QDate::currentDate().addDays(1)); hboxLayout->addWidget(onCW); hboxLayout->addStretch(1); vboxLayout->addLayout(hboxLayout); } { updateSubkeysCheckBox = new QCheckBox{i18n("Also update the expiry of the subkeys"), qq}; #ifdef QGPGME_SUPPORTS_CHANGING_EXPIRATION_OF_COMPLETE_KEY updateSubkeysCheckBox->setVisible(mode == Mode::UpdateCertificateWithSubkeys); #else updateSubkeysCheckBox->setVisible(false); #endif vboxLayout->addWidget(updateSubkeysCheckBox); } vboxLayout->addStretch(1); mainLayout->addWidget(mainWidget); auto buttonBox = new QDialogButtonBox{QDialogButtonBox::Ok | QDialogButtonBox::Cancel, qq}; auto okButton = buttonBox->button(QDialogButtonBox::Ok); KGuiItem::assign(okButton, KStandardGuiItem::ok()); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); KGuiItem::assign(buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); qq->connect(buttonBox, &QDialogButtonBox::accepted, qq, &QDialog::accept); qq->connect(buttonBox, &QDialogButtonBox::rejected, qq, &QDialog::reject); mainLayout->addWidget(buttonBox); connect(onRB, &QRadioButton::toggled, onCW, &QWidget::setEnabled); connect(inRB, &QRadioButton::toggled, inCB, &QWidget::setEnabled); connect(inRB, &QRadioButton::toggled, inSB, &QWidget::setEnabled); } } ui; }; void ExpiryDialog::Private::slotInUnitChanged() { const int oldInAmount = ui.inSB->value(); const QDate targetDate = date_by_amount_and_unit(oldInAmount, inUnit); inUnit = ui.inCB->currentIndex(); if (targetDate.isValid()) { ui.inSB->setValue(inAmountByDate(targetDate)); } else { slotInAmountChanged(); } } void ExpiryDialog::Private::slotInAmountChanged() { // Only modify onCW when onCW is slave: if (ui.inRB->isChecked()) { ui.onCW->setSelectedDate(inDate()); } } void ExpiryDialog::Private::slotOnDateChanged() { // Only modify inSB/inCB when onCW is master: if (ui.onRB->isChecked()) { ui.inSB->setValue(inAmountByDate(ui.onCW->selectedDate())); } } QDate ExpiryDialog::Private::inDate() const { return date_by_amount_and_unit(ui.inSB->value(), ui.inCB->currentIndex()); } int ExpiryDialog::Private::inAmountByDate(const QDate &selected) const { const QDate current = QDate::currentDate(); switch (ui.inCB->currentIndex()) { case Days: return current.daysTo(selected); case Weeks: return qRound(current.daysTo(selected) / 7.0); case Months: return monthsBetween(current, selected); case Years: return yearsBetween(current, selected); }; Q_ASSERT(!"Should not reach here"); return -1; } ExpiryDialog::ExpiryDialog(Mode mode, QWidget *p) : QDialog{p} , d{new Private{mode, this}} { setWindowTitle(i18nc("@title:window", "Change Expiry")); } ExpiryDialog::~ExpiryDialog() = default; void ExpiryDialog::setDateOfExpiry(const QDate &date) { const QDate current = QDate::currentDate(); if (date.isValid()) { d->ui.onRB->setChecked(true); d->ui.onCW->setSelectedDate(qMax(date, current)); } else { d->ui.neverRB->setChecked(true); d->ui.onCW->setSelectedDate(current); d->ui.inSB->setValue(0); } } QDate ExpiryDialog::dateOfExpiry() const { return d->ui.inRB->isChecked() ? d->inDate() : d->ui.onRB->isChecked() ? d->ui.onCW->selectedDate() : QDate{}; } void ExpiryDialog::setUpdateExpirationOfAllSubkeys(bool update) { d->ui.updateSubkeysCheckBox->setChecked(update); } bool ExpiryDialog::updateExpirationOfAllSubkeys() const { return d->ui.updateSubkeysCheckBox->isChecked(); } #include "moc_expirydialog.cpp" diff --git a/src/dialogs/gencardkeydialog.cpp b/src/dialogs/gencardkeydialog.cpp index 1b26797d5..1cf63a43e 100644 --- a/src/dialogs/gencardkeydialog.cpp +++ b/src/dialogs/gencardkeydialog.cpp @@ -1,174 +1,175 @@ /* dialogs/gencardkeydialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2017 Bundesamt für Sicherheit in der Informationstechnik SPDX-FileContributor: Intevation GmbH SPDX-License-Identifier: GPL-2.0-or-later */ #include "gencardkeydialog.h" +#include "utils/qt-cxx20-compat.h" #include "utils/userinfo.h" #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; class GenCardKeyDialog::Private { public: Private(GenCardKeyDialog *qq, KeyAttributes requiredAttributes): q(qq), mOkButton(nullptr), mNameEdit(nullptr), mEmailEdit(nullptr), mInvalidEmailLabel(nullptr), mAlgorithmCombo(nullptr), mBackupCheckBox(nullptr) { auto vBox = new QVBoxLayout(q); auto grid = new QGridLayout; int row = 0; if (requiredAttributes & KeyOwnerName) { auto nameLabel = new QLabel(i18n("Name:")); mNameEdit = new QLineEdit(userFullName()); grid->addWidget(nameLabel, row, 0); grid->addWidget(mNameEdit, row++, 1); } if (requiredAttributes & KeyOwnerEmail) { auto mailLabel = new QLabel(i18n("EMail:")); mEmailEdit = new QLineEdit(userEmailAddress()); connect(mEmailEdit, &QLineEdit::textChanged, q, [this]() {checkAcceptable();}); mInvalidEmailLabel = new QLabel(QStringLiteral("%2").arg( KColorScheme(QPalette::Active, KColorScheme::View).foreground(KColorScheme::NegativeText).color().name(), i18n("Invalid EMail"))); grid->addWidget(mailLabel, row, 0); grid->addWidget(mEmailEdit, row++, 1); grid->addWidget(mInvalidEmailLabel, row++, 1); } if (requiredAttributes & KeyAlgorithm) { auto algorithmLabel = new QLabel(i18n("Algorithm:")); mAlgorithmCombo = new QComboBox; grid->addWidget(algorithmLabel, row, 0); grid->addWidget(mAlgorithmCombo, row++, 1); } if (requiredAttributes & LocalKeyBackup) { mBackupCheckBox = new QCheckBox(i18n("Backup encryption key")); mBackupCheckBox->setToolTip(i18n("Backup the encryption key in a file.") + QStringLiteral("
") + i18n("You will be asked for a passphrase to protect that file during key generation.")); mBackupCheckBox->setChecked(true); grid->addWidget(mBackupCheckBox, row++, 0, 1, 2); } vBox->addLayout(grid); auto bbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, qq); mOkButton = bbox->button(QDialogButtonBox::Ok); mOkButton->setDefault(true); mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(bbox, &QDialogButtonBox::rejected, q, [this]() {q->reject();}); connect(bbox, &QDialogButtonBox::accepted, q, [this]() {accept();}); vBox->addWidget(bbox); q->setMinimumWidth(400); checkAcceptable(); } void accept() { if (mNameEdit) { params.name = mNameEdit->text(); } if (mEmailEdit) { params.email = mEmailEdit->text(); } if (mAlgorithmCombo) { params.algorithm = mAlgorithmCombo->currentData().toByteArray().toStdString(); } if (mBackupCheckBox) { params.backup = mBackupCheckBox->isChecked(); } q->accept(); } void setSupportedAlgorithms(const std::vector> &algorithms, const std::string &defaultAlgo) { if (!mAlgorithmCombo) { qCWarning(KLEOPATRA_LOG) << "GenCardKeyDialog::setSupportedAlgorithms() called, but algorithm no required key attribute"; return; } mAlgorithmCombo->clear(); for (auto algorithm: algorithms) { mAlgorithmCombo->addItem(algorithm.second, QByteArray::fromStdString(algorithm.first)); } mAlgorithmCombo->setCurrentIndex(mAlgorithmCombo->findData(QByteArray::fromStdString(defaultAlgo))); } void checkAcceptable() { // We only require a valid mail address if (!mEmailEdit) { return; } const QString mail = mEmailEdit->text(); if (!mail.isEmpty() && KEmailAddress::isValidSimpleAddress(mail)) { mOkButton->setEnabled(true); mInvalidEmailLabel->hide(); return; } if (!mail.isEmpty()) { mInvalidEmailLabel->show(); } else { mInvalidEmailLabel->hide(); } mOkButton->setEnabled(false); } GenCardKeyDialog *const q; KeyParams params; QPushButton *mOkButton; QLineEdit *mNameEdit; QLineEdit *mEmailEdit; QLabel *mInvalidEmailLabel; QComboBox *mAlgorithmCombo; QCheckBox *mBackupCheckBox; }; GenCardKeyDialog::GenCardKeyDialog(KeyAttributes requiredAttributes, QWidget *parent) : QDialog(parent), d(new Private(this, requiredAttributes)) { } void GenCardKeyDialog::setSupportedAlgorithms(const std::vector> &algorithms, const std::string &defaultAlgo) { d->setSupportedAlgorithms(algorithms, defaultAlgo); } GenCardKeyDialog::KeyParams GenCardKeyDialog::getKeyParams() const { return d->params; } diff --git a/src/dialogs/ownertrustdialog.cpp b/src/dialogs/ownertrustdialog.cpp index 99c86308e..a8bca844d 100644 --- a/src/dialogs/ownertrustdialog.cpp +++ b/src/dialogs/ownertrustdialog.cpp @@ -1,186 +1,188 @@ /* -*- mode: c++; c-basic-offset:4 -*- dialogs/ownertrustdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2008 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "ownertrustdialog.h" #include "ui_ownertrustdialog.h" +#include "utils/qt-cxx20-compat.h" + #include #include #include #include #include using namespace Kleo; using namespace Kleo::Dialogs; using namespace GpgME; class OwnerTrustDialog::Private { friend class ::Kleo::Dialogs::OwnerTrustDialog; OwnerTrustDialog *const q; public: explicit Private(OwnerTrustDialog *qq) : q(qq), formattedCertificateName(i18n("(unknown certificate)")), originalTrust(Key::Undefined), hasSecret(false), advancedMode(false), ui(qq) { } private: void slotTrustLevelChanged() { enableDisableWidgets(); } void enableDisableWidgets(); private: QString formattedCertificateName; Key::OwnerTrust originalTrust; bool hasSecret : 1; bool advancedMode : 1; struct UI : public Ui::OwnerTrustDialog { explicit UI(Dialogs::OwnerTrustDialog *qq) : Ui::OwnerTrustDialog(), q(qq) { auto mainWidget = new QWidget(q); setupUi(mainWidget); auto buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); auto mainLayout = new QVBoxLayout(q); mainLayout->addWidget(mainWidget); okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); q->connect(buttonBox, &QDialogButtonBox::accepted, q, &QDialog::accept); q->connect(buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); mainLayout->addWidget(buttonBox); } QPushButton *okPB() const { return okButton; } QPushButton *okButton; Dialogs::OwnerTrustDialog *const q; } ui; }; OwnerTrustDialog::OwnerTrustDialog(QWidget *p) : QDialog(p), d(new Private(this)) { connect(d->ui.unknownRB, SIGNAL(toggled(bool)), this, SLOT(slotTrustLevelChanged())); connect(d->ui.neverRB, SIGNAL(toggled(bool)), this, SLOT(slotTrustLevelChanged())); connect(d->ui.marginalRB, SIGNAL(toggled(bool)), this, SLOT(slotTrustLevelChanged())); connect(d->ui.fullRB, SIGNAL(toggled(bool)), this, SLOT(slotTrustLevelChanged())); connect(d->ui.ultimateRB, SIGNAL(toggled(bool)), this, SLOT(slotTrustLevelChanged())); } OwnerTrustDialog::~OwnerTrustDialog() {} void OwnerTrustDialog::setFormattedCertificateName(const QString &formatted) { if (formatted.isEmpty()) { return; } d->formattedCertificateName = formatted; setWindowTitle(i18nc("@title:window", "Change Trust Level of %1", formatted)); d->ui.label->setText(i18nc("@info", "How much do you trust certifications made by %1 to correctly verify authenticity of certificates?", formatted)); } QString OwnerTrustDialog::formattedCertificateName() const { return d->formattedCertificateName; } void OwnerTrustDialog::setHasSecretKey(bool secret) { d->hasSecret = secret; d->enableDisableWidgets(); setOwnerTrust(ownerTrust()); } bool OwnerTrustDialog::hasSecretKey() const { return d->hasSecret; } void OwnerTrustDialog::setAdvancedMode(bool advanced) { d->advancedMode = advanced; d->enableDisableWidgets(); setOwnerTrust(ownerTrust()); } bool OwnerTrustDialog::isAdvancedMode() const { return d->advancedMode; } void OwnerTrustDialog::Private::enableDisableWidgets() { ui.unknownRB ->setEnabled(!hasSecret || advancedMode); ui.neverRB ->setEnabled(!hasSecret || advancedMode); ui.marginalRB->setEnabled(!hasSecret || advancedMode); ui.fullRB ->setEnabled(!hasSecret || advancedMode); ui.ultimateRB->setEnabled(hasSecret || advancedMode); ui.okPB()->setEnabled(q->ownerTrust() != Key::Undefined && q->ownerTrust() != originalTrust); } static void force_set_checked(QAbstractButton *b, bool on) { // work around Qt bug (tested: 4.1.4, 4.2.3, 4.3.4) const bool autoExclusive = b->autoExclusive(); b->setAutoExclusive(false); b->setChecked(b->isEnabled() && on); b->setAutoExclusive(autoExclusive); } void OwnerTrustDialog::setOwnerTrust(Key::OwnerTrust trust) { d->originalTrust = trust; force_set_checked(d->ui.unknownRB, trust == Key::Unknown); force_set_checked(d->ui.neverRB, trust == Key::Never); force_set_checked(d->ui.marginalRB, trust == Key::Marginal); force_set_checked(d->ui.fullRB, trust == Key::Full); force_set_checked(d->ui.ultimateRB, trust == Key::Ultimate); d->enableDisableWidgets(); } Key::OwnerTrust OwnerTrustDialog::ownerTrust() const { if (d->ui.unknownRB->isChecked()) { return Key::Unknown; } if (d->ui.neverRB->isChecked()) { return Key::Never; } if (d->ui.marginalRB->isChecked()) { return Key::Marginal; } if (d->ui.fullRB->isChecked()) { return Key::Full; } if (d->ui.ultimateRB->isChecked()) { return Key::Ultimate; } return Key::Undefined; } #include "moc_ownertrustdialog.cpp" diff --git a/src/dialogs/pivcardapplicationadministrationkeyinputdialog.cpp b/src/dialogs/pivcardapplicationadministrationkeyinputdialog.cpp index eb4b4b29d..094bb867c 100644 --- a/src/dialogs/pivcardapplicationadministrationkeyinputdialog.cpp +++ b/src/dialogs/pivcardapplicationadministrationkeyinputdialog.cpp @@ -1,102 +1,104 @@ /* dialogs/pivcardapplicationadministrationkeyinputdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2020 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "pivcardapplicationadministrationkeyinputdialog.h" +#include "utils/qt-cxx20-compat.h" + #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Dialogs; class PIVCardApplicationAdministrationKeyInputDialog::Private { friend class ::Kleo::Dialogs::PIVCardApplicationAdministrationKeyInputDialog; public: explicit Private(PIVCardApplicationAdministrationKeyInputDialog *qq); void checkAcceptable(); PIVCardApplicationAdministrationKeyInputDialog *const q; QLabel *mLabel; QLineEdit *mHexEncodedAdminKeyEdit; QPushButton *mOkButton; QByteArray adminKey; }; PIVCardApplicationAdministrationKeyInputDialog::Private::Private(PIVCardApplicationAdministrationKeyInputDialog *qq): q(qq), mLabel(new QLabel(qq)), mHexEncodedAdminKeyEdit(new QLineEdit(qq)), mOkButton(nullptr) { auto vBox = new QVBoxLayout(q); { mLabel->setWordWrap(true); vBox->addWidget(mLabel); } { const QFont fixedFont = QFontDatabase::systemFont(QFontDatabase::FixedFont); mHexEncodedAdminKeyEdit->setInputMask(QStringLiteral("HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH:HH;_")); mHexEncodedAdminKeyEdit->setFont(fixedFont); mHexEncodedAdminKeyEdit->setMinimumWidth(QFontMetrics(fixedFont).horizontalAdvance(QStringLiteral("HH:")) * 24); connect(mHexEncodedAdminKeyEdit, &QLineEdit::textChanged, q, [this] () { checkAcceptable(); }); vBox->addWidget(mHexEncodedAdminKeyEdit); } auto bbox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, qq); mOkButton = bbox->button(QDialogButtonBox::Ok); mOkButton->setDefault(true); mOkButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(bbox, &QDialogButtonBox::rejected, q, [this]() {q->reject();}); connect(bbox, &QDialogButtonBox::accepted, q, [this]() {q->accept();}); vBox->addWidget(bbox); q->setMinimumWidth(400); checkAcceptable(); } void PIVCardApplicationAdministrationKeyInputDialog::Private::checkAcceptable() { mOkButton->setEnabled(mHexEncodedAdminKeyEdit->hasAcceptableInput()); } PIVCardApplicationAdministrationKeyInputDialog::PIVCardApplicationAdministrationKeyInputDialog(QWidget *parent) : QDialog(parent), d(new Private(this)) { } void PIVCardApplicationAdministrationKeyInputDialog::setLabelText(const QString& text) { d->mLabel->setText(text); } QString PIVCardApplicationAdministrationKeyInputDialog::labelText() const { return d->mLabel->text(); } QByteArray PIVCardApplicationAdministrationKeyInputDialog::adminKey() const { return QByteArray::fromHex(d->mHexEncodedAdminKeyEdit->text().toUtf8()); } diff --git a/src/kwatchgnupg/kwatchgnupgconfig.cpp b/src/kwatchgnupg/kwatchgnupgconfig.cpp index 5a07c2fd6..a68302d35 100644 --- a/src/kwatchgnupg/kwatchgnupgconfig.cpp +++ b/src/kwatchgnupg/kwatchgnupgconfig.cpp @@ -1,195 +1,197 @@ /* kwatchgnupgconfig.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2001, 2002, 2004 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "kwatchgnupgconfig.h" #include "kwatchgnupg.h" +#include "utils/qt-cxx20-compat.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include static const char *log_levels[] = { "none", "basic", "advanced", "expert", "guru" }; static int log_level_to_int(const QString &loglevel) { if (loglevel == QLatin1String("none")) { return 0; } else if (loglevel == QLatin1String("basic")) { return 1; } else if (loglevel == QLatin1String("advanced")) { return 2; } else if (loglevel == QLatin1String("expert")) { return 3; } else if (loglevel == QLatin1String("guru")) { return 4; } else { // default return 1; } } KWatchGnuPGConfig::KWatchGnuPGConfig(QWidget *parent) : QDialog(parent) { setWindowTitle(i18nc("@title:window", "Configure KWatchGnuPG")); auto mainLayout = new QVBoxLayout(this); mButtonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QPushButton *okButton = mButtonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(mButtonBox, &QDialogButtonBox::rejected, this, &KWatchGnuPGConfig::reject); auto top = new QWidget; mainLayout->addWidget(top); mainLayout->addWidget(mButtonBox); auto vlay = new QVBoxLayout(top); vlay->setContentsMargins(0, 0, 0, 0); auto group = new QGroupBox(i18n("WatchGnuPG"), top); vlay->addWidget(group); auto glay = new QGridLayout(group); glay->setColumnStretch(1, 1); int row = -1; ++row; mExeED = new Kleo::FileNameRequester(group); auto label = new QLabel(i18n("&Executable:"), group); label->setBuddy(mExeED); glay->addWidget(label, row, 0); glay->addWidget(mExeED, row, 1); connect(mExeED, &Kleo::FileNameRequester::fileNameChanged, this, &KWatchGnuPGConfig::slotChanged); ++row; mSocketED = new Kleo::FileNameRequester(group); label = new QLabel(i18n("&Socket:"), group); label->setBuddy(mSocketED); glay->addWidget(label, row, 0); glay->addWidget(mSocketED, row, 1); connect(mSocketED, &Kleo::FileNameRequester::fileNameChanged, this, &KWatchGnuPGConfig::slotChanged); ++row; mLogLevelCB = new QComboBox(group); mLogLevelCB->addItem(i18n("None")); mLogLevelCB->addItem(i18n("Basic")); mLogLevelCB->addItem(i18n("Advanced")); mLogLevelCB->addItem(i18n("Expert")); mLogLevelCB->addItem(i18n("Guru")); label = new QLabel(i18n("Default &log level:"), group); label->setBuddy(mLogLevelCB); glay->addWidget(label, row, 0); glay->addWidget(mLogLevelCB, row, 1); connect(mLogLevelCB, qOverload(&QComboBox::activated), this, &KWatchGnuPGConfig::slotChanged); /******************* Log Window group *******************/ group = new QGroupBox(i18n("Log Window"), top); vlay->addWidget(group); glay = new QGridLayout(group); glay->setColumnStretch(1, 1); row = -1; ++row; mLoglenSB = new KPluralHandlingSpinBox(group); mLoglenSB->setRange(0, 1000000); mLoglenSB->setSingleStep(100); mLoglenSB->setSuffix(ki18ncp("history size spinbox suffix", " line", " lines")); mLoglenSB->setSpecialValueText(i18n("unlimited")); label = new QLabel(i18n("&History size:"), group); label->setBuddy(mLoglenSB); glay->addWidget(label, row, 0); glay->addWidget(mLoglenSB, row, 1); auto button = new QPushButton(i18n("Set &Unlimited"), group); glay->addWidget(button, row, 2); connect(mLoglenSB, &QSpinBox::valueChanged, this, &KWatchGnuPGConfig::slotChanged); connect(button, &QPushButton::clicked, this, &KWatchGnuPGConfig::slotSetHistorySizeUnlimited); ++row; mWordWrapCB = new QCheckBox(i18n("Enable &word wrapping"), group); mWordWrapCB->hide(); // QTextEdit doesn't support word wrapping in LogText mode glay->addWidget(mWordWrapCB, row, 0, 1, 3); connect(mWordWrapCB, &QCheckBox::clicked, this, &KWatchGnuPGConfig::slotChanged); vlay->addStretch(1); connect(okButton, &QPushButton::clicked, this, &KWatchGnuPGConfig::slotSave); } KWatchGnuPGConfig::~KWatchGnuPGConfig() {} void KWatchGnuPGConfig::slotSetHistorySizeUnlimited() { mLoglenSB->setValue(0); } void KWatchGnuPGConfig::loadConfig() { const KConfigGroup watchGnuPG(KSharedConfig::openConfig(), "WatchGnuPG"); mExeED->setFileName(watchGnuPG.readEntry("Executable", WATCHGNUPGBINARY)); mSocketED->setFileName(watchGnuPG.readEntry("Socket", WATCHGNUPGSOCKET)); mLogLevelCB->setCurrentIndex(log_level_to_int(watchGnuPG.readEntry("LogLevel", "basic"))); const KConfigGroup logWindow(KSharedConfig::openConfig(), "LogWindow"); mLoglenSB->setValue(logWindow.readEntry("MaxLogLen", 10000)); mWordWrapCB->setChecked(logWindow.readEntry("WordWrap", false)); mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } void KWatchGnuPGConfig::saveConfig() { KConfigGroup watchGnuPG(KSharedConfig::openConfig(), "WatchGnuPG"); watchGnuPG.writeEntry("Executable", mExeED->fileName()); watchGnuPG.writeEntry("Socket", mSocketED->fileName()); watchGnuPG.writeEntry("LogLevel", log_levels[mLogLevelCB->currentIndex()]); KConfigGroup logWindow(KSharedConfig::openConfig(), "LogWindow"); logWindow.writeEntry("MaxLogLen", mLoglenSB->value()); logWindow.writeEntry("WordWrap", mWordWrapCB->isChecked()); KSharedConfig::openConfig()->sync(); mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(false); } void KWatchGnuPGConfig::slotChanged() { mButtonBox->button(QDialogButtonBox::Ok)->setEnabled(true); } void KWatchGnuPGConfig::slotSave() { saveConfig(); Q_EMIT reconfigure(); accept(); } diff --git a/src/kwatchgnupg/kwatchgnupgmainwin.cpp b/src/kwatchgnupg/kwatchgnupgmainwin.cpp index 06ba99026..63d428cbc 100644 --- a/src/kwatchgnupg/kwatchgnupgmainwin.cpp +++ b/src/kwatchgnupg/kwatchgnupgmainwin.cpp @@ -1,291 +1,293 @@ /* kwatchgnupgmainwin.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2001, 2002, 2004 Klar �vdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "kwatchgnupgmainwin.h" #include "kwatchgnupgconfig.h" #include "kwatchgnupg.h" #include "tray.h" +#include "utils/qt-cxx20-compat.h" + #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #if GPGMEPP_VERSION >= 0x11000 // 1.16.0 # define CRYPTOCONFIG_HAS_GROUPLESS_ENTRY_OVERLOAD #endif KWatchGnuPGMainWindow::KWatchGnuPGMainWindow(QWidget *parent) : KXmlGuiWindow(parent, Qt::Window), mConfig(nullptr) { createActions(); createGUI(); mCentralWidget = new QTextEdit(this); mCentralWidget->setReadOnly(true); setCentralWidget(mCentralWidget); mWatcher = new KProcess; connect(mWatcher, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotWatcherExited(int,QProcess::ExitStatus))); connect(mWatcher, &QProcess::readyReadStandardOutput, this, &KWatchGnuPGMainWindow::slotReadStdout); slotReadConfig(); mSysTray = new KWatchGnuPGTray(this); QAction *act = mSysTray->action(QStringLiteral("quit")); if (act) { connect(act, &QAction::triggered, this, &KWatchGnuPGMainWindow::slotQuit); } setAutoSaveSettings(); } KWatchGnuPGMainWindow::~KWatchGnuPGMainWindow() { delete mWatcher; } void KWatchGnuPGMainWindow::slotClear() { mCentralWidget->clear(); mCentralWidget->append(i18n("[%1] Log cleared", QDateTime::currentDateTime().toString(Qt::ISODate))); } void KWatchGnuPGMainWindow::createActions() { QAction *action = actionCollection()->addAction(QStringLiteral("clear_log")); action->setIcon(QIcon::fromTheme(QStringLiteral("edit-clear-history"))); action->setText(i18n("C&lear History")); connect(action, &QAction::triggered, this, &KWatchGnuPGMainWindow::slotClear); actionCollection()->setDefaultShortcut(action, QKeySequence(Qt::CTRL | Qt::Key_L)); (void)KStandardAction::saveAs(this, &KWatchGnuPGMainWindow::slotSaveAs, actionCollection()); (void)KStandardAction::close(this, &KWatchGnuPGMainWindow::close, actionCollection()); (void)KStandardAction::quit(this, &KWatchGnuPGMainWindow::slotQuit, actionCollection()); (void)KStandardAction::preferences(this, &KWatchGnuPGMainWindow::slotConfigure, actionCollection()); (void)KStandardAction::keyBindings(this, &KWatchGnuPGMainWindow::configureShortcuts, actionCollection()); (void)KStandardAction::configureToolbars(this, &KWatchGnuPGMainWindow::slotConfigureToolbars, actionCollection()); } void KWatchGnuPGMainWindow::configureShortcuts() { KShortcutsDialog::showDialog(actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, this); } void KWatchGnuPGMainWindow::slotConfigureToolbars() { KEditToolBar dlg(factory()); dlg.exec(); } void KWatchGnuPGMainWindow::startWatcher() { disconnect(mWatcher, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotWatcherExited(int,QProcess::ExitStatus))); if (mWatcher->state() == QProcess::Running) { mWatcher->kill(); while (mWatcher->state() == QProcess::Running) { qApp->processEvents(QEventLoop::ExcludeUserInputEvents); } mCentralWidget->append(i18n("[%1] Log stopped", QDateTime::currentDateTime().toString(Qt::ISODate))); mCentralWidget->ensureCursorVisible(); } mWatcher->clearProgram(); { const KConfigGroup config(KSharedConfig::openConfig(), "WatchGnuPG"); *mWatcher << config.readEntry("Executable", WATCHGNUPGBINARY); *mWatcher << QStringLiteral("--force"); *mWatcher << config.readEntry("Socket", WATCHGNUPGSOCKET); } mWatcher->setOutputChannelMode(KProcess::OnlyStdoutChannel); mWatcher->start(); const bool ok = mWatcher->waitForStarted(); if (!ok) { KMessageBox::sorry(this, i18n("The watchgnupg logging process could not be started.\nPlease install watchgnupg somewhere in your $PATH.\nThis log window is unable to display any useful information.")); } else { mCentralWidget->append(i18n("[%1] Log started", QDateTime::currentDateTime().toString(Qt::ISODate))); mCentralWidget->ensureCursorVisible(); } connect(mWatcher, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotWatcherExited(int,QProcess::ExitStatus))); } namespace { QGpgME::CryptoConfigEntry *getCryptoConfigEntry(const QGpgME::CryptoConfig *config, const QString &componentName, const char *entryName) { // copied from utils/compat.cpp in libkleopatra #ifdef CRYPTOCONFIG_HAS_GROUPLESS_ENTRY_OVERLOAD return config->entry(componentName, QString::fromLatin1(entryName)); #else using namespace QGpgME; const CryptoConfigComponent *const comp = config->component(componentName); const QStringList groupNames = comp->groupList(); for (const auto &groupName : groupNames) { const CryptoConfigGroup *const group = comp ? comp->group(groupName) : nullptr; if (CryptoConfigEntry *const entry = group->entry(QString::fromLatin1(entryName))) { return entry; } } return nullptr; #endif } } void KWatchGnuPGMainWindow::setGnuPGConfig() { QStringList logclients; // Get config object QGpgME::CryptoConfig *const cconfig = QGpgME::cryptoConfig(); if (!cconfig) { return; } KConfigGroup config(KSharedConfig::openConfig(), "WatchGnuPG"); const QStringList comps = cconfig->componentList(); for (QStringList::const_iterator it = comps.constBegin(); it != comps.constEnd(); ++it) { const QGpgME::CryptoConfigComponent *const comp = cconfig->component(*it); Q_ASSERT(comp); { QGpgME::CryptoConfigEntry *const entry = getCryptoConfigEntry(cconfig, comp->name(), "log-file"); if (entry) { entry->setStringValue(QLatin1String("socket://") + config.readEntry("Socket", WATCHGNUPGSOCKET)); logclients << QStringLiteral("%1 (%2)").arg(*it, comp->description()); } } { QGpgME::CryptoConfigEntry *const entry = getCryptoConfigEntry(cconfig, comp->name(), "debug-level"); if (entry) { entry->setStringValue(config.readEntry("LogLevel", "basic")); } } } cconfig->sync(true); if (logclients.isEmpty()) { KMessageBox::sorry(nullptr, i18n("There are no components available that support logging.")); } } void KWatchGnuPGMainWindow::slotWatcherExited(int, QProcess::ExitStatus) { if (KMessageBox::questionYesNo(this, i18n("The watchgnupg logging process died.\nDo you want to try to restart it?"), QString(), KGuiItem(i18n("Try Restart")), KGuiItem(i18n("Do Not Try"))) == KMessageBox::Yes) { mCentralWidget->append(i18n("====== Restarting logging process =====")); mCentralWidget->ensureCursorVisible(); startWatcher(); } else { KMessageBox::sorry(this, i18n("The watchgnupg logging process is not running.\nThis log window is unable to display any useful information.")); } } void KWatchGnuPGMainWindow::slotReadStdout() { if (!mWatcher) { return; } while (mWatcher->canReadLine()) { QString str = QString::fromUtf8(mWatcher->readLine()); if (str.endsWith(QLatin1Char('\n'))) { str.chop(1); } if (str.endsWith(QLatin1Char('\r'))) { str.chop(1); } mCentralWidget->append(str); mCentralWidget->ensureCursorVisible(); if (!isVisible()) { // Change tray icon to show something happened // PENDING(steffen) mSysTray->setAttention(true); } } } void KWatchGnuPGMainWindow::show() { mSysTray->setAttention(false); KMainWindow::show(); } void KWatchGnuPGMainWindow::slotSaveAs() { const QString filename = QFileDialog::getSaveFileName(this, i18n("Save Log to File")); if (filename.isEmpty()) { return; } QFile file(filename); if (file.open(QIODevice::WriteOnly)) { QTextStream(&file) << mCentralWidget->document()->toRawText(); } else KMessageBox::information(this, i18n("Could not save file %1: %2", filename, file.errorString())); } void KWatchGnuPGMainWindow::slotQuit() { disconnect(mWatcher, SIGNAL(finished(int,QProcess::ExitStatus)), this, SLOT(slotWatcherExited(int,QProcess::ExitStatus))); mWatcher->kill(); qApp->quit(); } void KWatchGnuPGMainWindow::slotConfigure() { if (!mConfig) { mConfig = new KWatchGnuPGConfig(this); mConfig->setObjectName(QStringLiteral("config dialog")); connect(mConfig, &KWatchGnuPGConfig::reconfigure, this, &KWatchGnuPGMainWindow::slotReadConfig); } mConfig->loadConfig(); mConfig->exec(); } void KWatchGnuPGMainWindow::slotReadConfig() { const KConfigGroup config(KSharedConfig::openConfig(), "LogWindow"); const int maxLogLen = config.readEntry("MaxLogLen", 10000); mCentralWidget->document()->setMaximumBlockCount(maxLogLen < 1 ? -1 : maxLogLen); setGnuPGConfig(); startWatcher(); } bool KWatchGnuPGMainWindow::queryClose() { if (!qApp->isSavingSession()) { hide(); return false; } return KMainWindow::queryClose(); } diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp index 2e2d95127..343ff3284 100644 --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -1,778 +1,779 @@ /* -*- mode: c++; c-basic-offset:4 -*- mainwindow.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB SPDX-License-Identifier: GPL-2.0-or-later */ #include #include "mainwindow.h" #include "aboutdata.h" #include "settings.h" #include #include "view/padwidget.h" #include "view/searchbar.h" #include "view/tabwidget.h" #include "view/keylistcontroller.h" #include "view/keycacheoverlay.h" #include "view/smartcardwidget.h" #include "view/welcomewidget.h" #include "commands/selftestcommand.h" #include "commands/importcrlcommand.h" #include "commands/importcertificatefromfilecommand.h" #include "commands/decryptverifyfilescommand.h" #include "commands/signencryptfilescommand.h" #include "conf/groupsconfigdialog.h" #include "utils/detail_p.h" #include #include "utils/action_data.h" #include "utils/filedialog.h" #include "utils/clipboardmenu.h" #include "utils/gui-helper.h" +#include "utils/qt-cxx20-compat.h" #include "dialogs/updatenotification.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include using namespace std::chrono_literals; using namespace Kleo; using namespace Kleo::Commands; using namespace GpgME; static KGuiItem KStandardGuiItem_quit() { static const QString app = KAboutData::applicationData().displayName(); KGuiItem item = KStandardGuiItem::quit(); item.setText(xi18nc("@action:button", "&Quit %1", app)); return item; } static KGuiItem KStandardGuiItem_close() { KGuiItem item = KStandardGuiItem::close(); item.setText(i18nc("@action:button", "Only &Close Window")); return item; } static bool isQuitting = false; namespace { static const std::vector mainViewActionNames = { QStringLiteral("view_certificate_overview"), QStringLiteral("manage_smartcard"), QStringLiteral("pad_view") }; class CertificateView : public QWidget, public FocusFirstChild { Q_OBJECT public: CertificateView(QWidget* parent = nullptr) : QWidget{parent} , ui{this} { } SearchBar *searchBar() const { return ui.searchBar; } TabWidget *tabWidget() const { return ui.tabWidget; } void focusFirstChild(Qt::FocusReason reason) override { ui.searchBar->lineEdit()->setFocus(reason); } private: struct UI { TabWidget *tabWidget = nullptr; SearchBar *searchBar = nullptr; explicit UI(CertificateView *q) { auto vbox = new QVBoxLayout{q}; vbox->setSpacing(0); searchBar = new SearchBar{q}; vbox->addWidget(searchBar); tabWidget = new TabWidget{q}; vbox->addWidget(tabWidget); tabWidget->connectSearchBar(searchBar); } } ui; }; } class MainWindow::Private { friend class ::MainWindow; MainWindow *const q; public: explicit Private(MainWindow *qq); ~Private(); template void createAndStart() { (new T(this->currentView(), &this->controller))->start(); } template void createAndStart(QAbstractItemView *view) { (new T(view, &this->controller))->start(); } template void createAndStart(const QStringList &a) { (new T(a, this->currentView(), &this->controller))->start(); } template void createAndStart(const QStringList &a, QAbstractItemView *view) { (new T(a, view, &this->controller))->start(); } void closeAndQuit() { const QString app = KAboutData::applicationData().displayName(); const int rc = KMessageBox::questionYesNoCancel(q, xi18n("%1 may be used by other applications as a service." "You may instead want to close this window without exiting %1.", app), i18nc("@title:window", "Really Quit?"), KStandardGuiItem_close(), KStandardGuiItem_quit(), KStandardGuiItem::cancel(), QLatin1String("really-quit-") + app.toLower()); if (rc == KMessageBox::Cancel) { return; } isQuitting = true; if (!q->close()) { return; } // WARNING: 'this' might be deleted at this point! if (rc == KMessageBox::No) { qApp->quit(); } } void configureToolbars() { KEditToolBar dlg(q->factory()); dlg.exec(); } void editKeybindings() { KShortcutsDialog::showDialog(q->actionCollection(), KShortcutsEditor::LetterShortcutsAllowed, q); updateSearchBarClickMessage(); } void updateSearchBarClickMessage() { const QString shortcutStr = focusToClickSearchAction->shortcut().toString(); ui.searchTab->searchBar()->updateClickMessage(shortcutStr); } void updateStatusBar() { if (Kleo::gnupgUsesDeVsCompliance()) { auto statusBar = std::make_unique(); auto statusLbl = std::make_unique(Formatting::deVsString(Kleo::gnupgIsDeVsCompliant())); const auto color = KColorScheme(QPalette::Active, KColorScheme::View).foreground( Kleo::gnupgIsDeVsCompliant() ? KColorScheme::NormalText: KColorScheme::NegativeText ).color(); const auto background = KColorScheme(QPalette::Active, KColorScheme::View).background( Kleo::gnupgIsDeVsCompliant() ? KColorScheme::PositiveBackground : KColorScheme::NegativeBackground ).color(); statusLbl->setStyleSheet(QStringLiteral("QLabel { color: %1; background-color: %2; }"). arg(color.name()).arg(background.name())); statusBar->insertPermanentWidget(0, statusLbl.release()); q->setStatusBar(statusBar.release()); // QMainWindow takes ownership } else { q->setStatusBar(nullptr); } } void selfTest() { createAndStart(); } void configureGroups() { if (KConfigDialog::showDialog(GroupsConfigDialog::dialogName())) { return; } KConfigDialog *dialog = new GroupsConfigDialog(q); dialog->show(); } void showHandbook(); void gnupgLogViewer() { const QString exec = QStandardPaths::findExecutable(QStringLiteral("kwatchgnupg")); if (exec.isEmpty()) { KMessageBox::error( q, i18n("Could not start the GnuPG Log Viewer (kwatchgnupg). " "Please check your installation."), i18n("Error Starting KWatchGnuPG")); } else { QProcess::startDetached(QStringLiteral("kwatchgnupg"), QStringList()); } } void forceUpdateCheck() { UpdateNotification::forceUpdateCheck(q); } void slotConfigCommitted(); void slotContextMenuRequested(QAbstractItemView *, const QPoint &p) { if (auto const menu = qobject_cast(q->factory()->container(QStringLiteral("listview_popup"), q))) { menu->exec(p); } else { qCDebug(KLEOPATRA_LOG) << "no \"listview_popup\" in kleopatra's ui.rc file"; } } void slotFocusQuickSearch() { ui.searchTab->searchBar()->lineEdit()->setFocus(); } void showView(const QString &actionName, QWidget *widget) { const auto coll = q->actionCollection(); if (coll) { for ( const QString &name : mainViewActionNames ) { if (auto action = coll->action(name)) { action->setChecked(name == actionName); } } } ui.stackWidget->setCurrentWidget(widget); if (auto ffci = dynamic_cast(widget)) { ffci->focusFirstChild(Qt::TabFocusReason); } } void showCertificateView() { if (KeyCache::instance()->keys().empty()) { showView(QStringLiteral("view_certificate_overview"), ui.welcomeWidget); } else { showView(QStringLiteral("view_certificate_overview"), ui.searchTab); } } void showSmartcardView() { showView(QStringLiteral("manage_smartcard"), ui.scWidget); } void showPadView() { if (!ui.padWidget) { ui.padWidget = new PadWidget; ui.stackWidget->addWidget(ui.padWidget); } showView(QStringLiteral("pad_view"), ui.padWidget); ui.stackWidget->resize(ui.padWidget->sizeHint()); } void restartDaemons() { Kleo::killDaemons(); } private: void setupActions(); QAbstractItemView *currentView() const { return ui.searchTab->tabWidget()->currentView(); } void keyListingDone() { const auto curWidget = ui.stackWidget->currentWidget(); if (curWidget == ui.scWidget || curWidget == ui.padWidget) { return; } showCertificateView(); } private: Kleo::KeyListController controller; bool firstShow : 1; struct UI { CertificateView *searchTab = nullptr; PadWidget *padWidget = nullptr; SmartCardWidget *scWidget = nullptr; WelcomeWidget *welcomeWidget = nullptr; QStackedWidget *stackWidget = nullptr; explicit UI(MainWindow *q); } ui; QAction *focusToClickSearchAction = nullptr; ClipboardMenu *clipboadMenu = nullptr; }; MainWindow::Private::UI::UI(MainWindow *q) : padWidget(nullptr) { auto mainWidget = new QWidget{q}; auto mainLayout = new QVBoxLayout(mainWidget); stackWidget = new QStackedWidget{q}; searchTab = new CertificateView{q}; stackWidget->addWidget(searchTab); new KeyCacheOverlay(mainWidget, q); scWidget = new SmartCardWidget{q}; stackWidget->addWidget(scWidget); welcomeWidget = new WelcomeWidget{q}; stackWidget->addWidget(welcomeWidget); mainLayout->addWidget(stackWidget); q->setCentralWidget(mainWidget); } MainWindow::Private::Private(MainWindow *qq) : q(qq), controller(q), firstShow(true), ui(q) { KDAB_SET_OBJECT_NAME(controller); AbstractKeyListModel *flatModel = AbstractKeyListModel::createFlatKeyListModel(q); AbstractKeyListModel *hierarchicalModel = AbstractKeyListModel::createHierarchicalKeyListModel(q); KDAB_SET_OBJECT_NAME(flatModel); KDAB_SET_OBJECT_NAME(hierarchicalModel); controller.setFlatModel(flatModel); controller.setHierarchicalModel(hierarchicalModel); controller.setTabWidget(ui.searchTab->tabWidget()); ui.searchTab->tabWidget()->setFlatModel(flatModel); ui.searchTab->tabWidget()->setHierarchicalModel(hierarchicalModel); setupActions(); ui.stackWidget->setCurrentWidget(ui.searchTab); if (auto action = q->actionCollection()->action(QStringLiteral("view_certificate_overview"))) { action->setChecked(true); } connect(&controller, SIGNAL(contextMenuRequested(QAbstractItemView*,QPoint)), q, SLOT(slotContextMenuRequested(QAbstractItemView*,QPoint))); connect(KeyCache::instance().get(), &KeyCache::keyListingDone, q, [this] () {keyListingDone();}); q->createGUI(QStringLiteral("kleopatra.rc")); // make toolbar buttons accessible by keyboard if (auto toolbar = q->findChild()) { auto toolbarButtons = toolbar->findChildren(); for (auto b : toolbarButtons) { b->setFocusPolicy(Qt::TabFocus); } // move toolbar and its child widgets before the central widget in the tab order; // this is necessary to make Shift+Tab work as expected forceSetTabOrder(q, toolbar); auto toolbarChildren = toolbar->findChildren(); std::for_each(std::rbegin(toolbarChildren), std::rend(toolbarChildren), [toolbar](auto w) { forceSetTabOrder(toolbar, w); }); } q->setAcceptDrops(true); // set default window size q->resize(QSize(1024, 500)); q->setAutoSaveSettings(); updateSearchBarClickMessage(); updateStatusBar(); if (KeyCache::instance()->initialized()) { keyListingDone(); } } MainWindow::Private::~Private() {} MainWindow::MainWindow(QWidget *parent, Qt::WindowFlags flags) : KXmlGuiWindow(parent, flags), d(new Private(this)) {} MainWindow::~MainWindow() {} void MainWindow::Private::setupActions() { KActionCollection *const coll = q->actionCollection(); const std::vector action_data = { // see keylistcontroller.cpp for more actions // Tools menu #ifndef Q_OS_WIN { "tools_start_kwatchgnupg", i18n("GnuPG Log Viewer"), QString(), "kwatchgnupg", q, [this](bool) { gnupgLogViewer(); }, QString() }, #endif { "tools_restart_backend", i18nc("@action:inmenu", "Restart Background Processes"), i18nc("@info:tooltip", "Restart the background processes, e.g. after making changes to the configuration."), "view-refresh", q, [this](bool) { restartDaemons(); }, {} }, // Help menu #ifdef Q_OS_WIN { "help_check_updates", i18n("Check for updates"), QString(), "gpg4win-compact", q, [this](bool) { forceUpdateCheck(); }, QString() }, #endif // View menu { "view_certificate_overview", i18nc("@action show certificate overview", "Certificates"), i18n("Show certificate overview"), "view-certificate", q, [this](bool) { showCertificateView(); }, QString() }, { "pad_view", i18nc("@action show input / output area for encrypting/signing resp. decrypting/verifying text", "Notepad"), i18n("Show pad for encrypting/decrypting and signing/verifying text"), "note", q, [this](bool) { showPadView(); }, QString() }, { "manage_smartcard", i18nc("@action show smartcard management view", "Smartcards"), i18n("Show smartcard management"), "auth-sim-locked", q, [this](bool) { showSmartcardView(); }, QString() }, // Settings menu { "settings_self_test", i18n("Perform Self-Test"), QString(), nullptr, q, [this](bool) { selfTest(); }, QString() }, { "configure_groups", i18n("Configure Groups..."), QString(), "group", q, [this](bool) { configureGroups(); }, QString() } }; make_actions_from_data(action_data, coll); if (!Settings().groupsEnabled()) { if (auto action = coll->action(QStringLiteral("configure_groups"))) { delete action; } } for ( const QString &name : mainViewActionNames ) { if (auto action = coll->action(name)) { action->setCheckable(true); } } KStandardAction::close(q, SLOT(close()), coll); KStandardAction::quit(q, SLOT(closeAndQuit()), coll); KStandardAction::configureToolbars(q, SLOT(configureToolbars()), coll); KStandardAction::keyBindings(q, SLOT(editKeybindings()), coll); KStandardAction::preferences(qApp, SLOT(openOrRaiseConfigDialog()), coll); focusToClickSearchAction = new QAction(i18n("Set Focus to Quick Search"), q); coll->addAction(QStringLiteral("focus_to_quickseach"), focusToClickSearchAction); coll->setDefaultShortcut(focusToClickSearchAction, QKeySequence(Qt::ALT | Qt::Key_Q)); connect(focusToClickSearchAction, SIGNAL(triggered(bool)), q, SLOT(slotFocusQuickSearch())); clipboadMenu = new ClipboardMenu(q); clipboadMenu->setMainWindow(q); clipboadMenu->clipboardMenu()->setIcon(QIcon::fromTheme(QStringLiteral("edit-paste"))); clipboadMenu->clipboardMenu()->setPopupMode(QToolButton::InstantPopup); coll->addAction(QStringLiteral("clipboard_menu"), clipboadMenu->clipboardMenu()); /* Add additional help actions for documentation */ const auto compendium = new DocAction(QIcon::fromTheme(QStringLiteral("gpg4win-compact")), i18n("Gpg4win Compendium"), i18nc("The Gpg4win compendium is only available" "at this point (24.7.2017) in german and english." "Please check with Gpg4win before translating this filename.", "gpg4win-compendium-en.pdf"), QStringLiteral("../share/gpg4win")); coll->addAction(QStringLiteral("help_doc_compendium"), compendium); /* Documentation centered around the german approved VS-NfD mode for official * RESTRICTED communication. This is only available in some distributions with * the focus on official communications. */ const auto symguide = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("Password-based encryption"), i18nc("Only available in German and English. Leave to English for other languages.", "handout_symmetric_encryption_gnupg_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_symenc"), symguide); const auto quickguide = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("Quickguide"), i18nc("Only available in German and English. Leave to English for other languages.", "handout_sign_encrypt_gnupg_en.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_quickguide"), quickguide); const auto vsa10573 = new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")), i18n("SecOps VSA-10573"), i18nc("Only available in German and English. Leave to English for other languages.", "BSI-VSA-10573-ENG_secops-20220207.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_vsa10573"), vsa10573); const auto vsa10584 = new DocAction(QIcon::fromTheme(QStringLiteral("dvipdf")), i18n("SecOps VSA-10584"), i18nc("Only available in German and English. Leave to English for other languages.", "BSI-VSA-10584-ENG_secops-20220207.pdf"), QStringLiteral("../share/doc/gnupg-vsd")); coll->addAction(QStringLiteral("help_doc_vsa10584"), vsa10584); const auto man_gpg = new DocAction(QIcon::fromTheme(QStringLiteral("help-contextual")), i18n("GnuPG Manual"), QStringLiteral("gnupg.pdf"), QStringLiteral("../share/doc/gnupg")); coll->addAction(QStringLiteral("help_doc_gnupg"), man_gpg); q->setStandardToolBarMenuEnabled(true); controller.createActions(coll); ui.searchTab->tabWidget()->createActions(coll); } void MainWindow::Private::slotConfigCommitted() { controller.updateConfig(); updateStatusBar(); } void MainWindow::closeEvent(QCloseEvent *e) { // KMainWindow::closeEvent() insists on quitting the application, // so do not let it touch the event... qCDebug(KLEOPATRA_LOG); if (d->controller.hasRunningCommands()) { if (d->controller.shutdownWarningRequired()) { const int ret = KMessageBox::warningContinueCancel(this, i18n("There are still some background operations ongoing. " "These will be terminated when closing the window. " "Proceed?"), i18n("Ongoing Background Tasks")); if (ret != KMessageBox::Continue) { e->ignore(); return; } } d->controller.cancelCommands(); if (d->controller.hasRunningCommands()) { // wait for them to be finished: setEnabled(false); QEventLoop ev; QTimer::singleShot(100ms, &ev, &QEventLoop::quit); connect(&d->controller, &KeyListController::commandsExecuting, &ev, &QEventLoop::quit); ev.exec(); if (d->controller.hasRunningCommands()) qCWarning(KLEOPATRA_LOG) << "controller still has commands running, this may crash now..."; setEnabled(true); } } if (isQuitting || qApp->isSavingSession()) { d->ui.searchTab->tabWidget()->saveViews(KSharedConfig::openConfig().data()); KConfigGroup grp(KConfigGroup(KSharedConfig::openConfig(), autoSaveGroup())); saveMainWindowSettings(grp); e->accept(); } else { e->ignore(); hide(); } } void MainWindow::showEvent(QShowEvent *e) { KXmlGuiWindow::showEvent(e); if (d->firstShow) { d->ui.searchTab->tabWidget()->loadViews(KSharedConfig::openConfig().data()); d->firstShow = false; } if (!savedGeometry.isEmpty()) { restoreGeometry(savedGeometry); } } void MainWindow::hideEvent(QHideEvent *e) { savedGeometry = saveGeometry(); KXmlGuiWindow::hideEvent(e); } void MainWindow::importCertificatesFromFile(const QStringList &files) { if (!files.empty()) { d->createAndStart(files); } } static QStringList extract_local_files(const QMimeData *data) { const QList urls = data->urls(); // begin workaround KDE/Qt misinterpretation of text/uri-list QList::const_iterator end = urls.end(); if (urls.size() > 1 && !urls.back().isValid()) { --end; } // end workaround QStringList result; std::transform(urls.begin(), end, std::back_inserter(result), std::mem_fn(&QUrl::toLocalFile)); result.erase(std::remove_if(result.begin(), result.end(), std::mem_fn(&QString::isEmpty)), result.end()); return result; } static bool can_decode_local_files(const QMimeData *data) { if (!data) { return false; } return !extract_local_files(data).empty(); } void MainWindow::dragEnterEvent(QDragEnterEvent *e) { qCDebug(KLEOPATRA_LOG); if (can_decode_local_files(e->mimeData())) { e->acceptProposedAction(); } } void MainWindow::dropEvent(QDropEvent *e) { qCDebug(KLEOPATRA_LOG); if (!can_decode_local_files(e->mimeData())) { return; } e->setDropAction(Qt::CopyAction); const QStringList files = extract_local_files(e->mimeData()); const unsigned int classification = classify(files); QMenu menu; QAction *const signEncrypt = menu.addAction(i18n("Sign/Encrypt...")); QAction *const decryptVerify = mayBeAnyMessageType(classification) ? menu.addAction(i18n("Decrypt/Verify...")) : nullptr; if (signEncrypt || decryptVerify) { menu.addSeparator(); } QAction *const importCerts = mayBeAnyCertStoreType(classification) ? menu.addAction(i18n("Import Certificates")) : nullptr; QAction *const importCRLs = mayBeCertificateRevocationList(classification) ? menu.addAction(i18n("Import CRLs")) : nullptr; if (importCerts || importCRLs) { menu.addSeparator(); } if (!signEncrypt && !decryptVerify && !importCerts && !importCRLs) { return; } menu.addAction(i18n("Cancel")); const QAction *const chosen = menu.exec(mapToGlobal(e->pos())); if (!chosen) { return; } if (chosen == signEncrypt) { d->createAndStart(files); } else if (chosen == decryptVerify) { d->createAndStart(files); } else if (chosen == importCerts) { d->createAndStart(files); } else if (chosen == importCRLs) { d->createAndStart(files); } e->accept(); } void MainWindow::readProperties(const KConfigGroup &cg) { qCDebug(KLEOPATRA_LOG); KXmlGuiWindow::readProperties(cg); setHidden(cg.readEntry("hidden", false)); } void MainWindow::saveProperties(KConfigGroup &cg) { qCDebug(KLEOPATRA_LOG); KXmlGuiWindow::saveProperties(cg); cg.writeEntry("hidden", isHidden()); } #include "mainwindow.moc" #include "moc_mainwindow.cpp" diff --git a/src/utils/qt-cxx20-compat.h b/src/utils/qt-cxx20-compat.h new file mode 100644 index 000000000..4871f19f1 --- /dev/null +++ b/src/utils/qt-cxx20-compat.h @@ -0,0 +1,20 @@ +/* -*- mode: c++; c-basic-offset:4 -*- + utils/qt-cxx20-compat.h + + This file is part of Kleopatra, the KDE keymanager + SPDX-FileCopyrightText: 2022 g10 Code GmbH + SPDX-FileContributor: Ingo Klöcker + + SPDX-License-Identifier: GPL-2.0-or-later +*/ + +#pragma once + +#include + +// define some bitwise operators to avoid warning that bitwise operation between +// different enumeration types is deprecated +inline int operator|(Qt::Modifier modifier, Qt::Key key) +{ + return static_cast(modifier) | static_cast(key); +}