Page MenuHome GnuPG

No OneTemporary

diff --git a/src/conf/appearanceconfigwidget.cpp b/src/conf/appearanceconfigwidget.cpp
index bcd68db77..48877e56c 100644
--- a/src/conf/appearanceconfigwidget.cpp
+++ b/src/conf/appearanceconfigwidget.cpp
@@ -1,903 +1,904 @@
/*
This file is part of kleopatra, the KDE key manager
SPDX-FileCopyrightText: 2002, 2004, 2008 Klarälvdalens Datakonsult AB
SPDX-FileCopyrightText: 2002, 2003 Marc Mutz <mutz@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "appearanceconfigwidget.h"
#include <settings.h>
#include <Libkleo/DNAttributeOrderConfigWidget>
#include <Libkleo/DnAttributes>
#include <Libkleo/ExpiryCheckerConfig>
#include <Libkleo/KeyFilterManager>
#include <Libkleo/SystemInfo>
#include <KConfig>
#include <KConfigGroup>
#include <KIconDialog>
#include <KLocalization>
#include <KLocalizedString>
#include <KMessageWidget>
#include <KSeparator>
#include <QApplication>
#include <QCheckBox>
#include <QColor>
#include <QColorDialog>
#include <QFont>
#include <QFontDialog>
#include <QGridLayout>
#include <QIcon>
#include <QLabel>
#include <QListWidget>
#include <QRegularExpression>
#include <QSpinBox>
#include <QString>
#include <QVBoxLayout>
#include <algorithm>
using namespace Kleo;
using namespace Kleo::Config;
enum {
HasFontRole = Qt::UserRole + 0x1234, /*!< Records that the user has chosen completely different font (as opposed to italic/bold/strikeout) */
IconNameRole, /*!< Records the name of the icon (since QIcon won't give it out again, once set) */
MayChangeForegroundRole,
MayChangeBackgroundRole,
MayChangeFontRole,
MayChangeItalicRole,
MayChangeBoldRole,
MayChangeStrikeOutRole,
MayChangeIconRole,
StoredForegroundRole, /*!< Stores the actual configured foreground color */
StoredBackgroundRole, /*!< Stores the actual configured background color */
ConfigGroupRole,
EndDummy,
};
static QFont tryToFindFontFor(const QListWidgetItem *item)
{
if (item)
if (const QListWidget *const lw = item->listWidget()) {
return lw->font();
}
return QApplication::font("QListWidget");
}
static bool is(const QListWidgetItem *item, bool (QFont::*func)() const)
{
if (!item) {
return false;
}
const QVariant v = item->data(Qt::FontRole);
if (!v.isValid() || v.userType() != QMetaType::QFont) {
return false;
}
return (v.value<QFont>().*func)();
}
static bool is_italic(const QListWidgetItem *item)
{
return is(item, &QFont::italic);
}
static bool is_bold(const QListWidgetItem *item)
{
return is(item, &QFont::bold);
}
static bool is_strikeout(const QListWidgetItem *item)
{
return is(item, &QFont::strikeOut);
}
static void set(QListWidgetItem *item, bool on, void (QFont::*func)(bool))
{
if (!item) {
return;
}
const QVariant v = item->data(Qt::FontRole);
QFont font = v.isValid() && v.userType() == QMetaType::QFont ? v.value<QFont>() : tryToFindFontFor(item);
(font.*func)(on);
item->setData(Qt::FontRole, font);
}
static void set_italic(QListWidgetItem *item, bool on)
{
set(item, on, &QFont::setItalic);
}
static void set_bold(QListWidgetItem *item, bool on)
{
set(item, on, &QFont::setBold);
}
static void set_strikeout(QListWidgetItem *item, bool on)
{
set(item, on, &QFont::setStrikeOut);
}
static void apply_config(const KConfigGroup &group, QListWidgetItem *item)
{
if (!item) {
return;
}
const QString name = group.readEntry("Name");
item->setText(name.isEmpty() ? i18nc("Key filter without user-assigned name", "<unnamed>") : name);
item->setData(ConfigGroupRole, QVariant::fromValue(group));
const QColor fg = group.readEntry("foreground-color", QColor());
item->setData(StoredForegroundRole, fg.isValid() ? QBrush(fg) : QVariant());
if (!SystemInfo::isHighContrastModeActive()) {
item->setData(Qt::ForegroundRole, fg.isValid() ? QBrush(fg) : QVariant());
}
item->setData(MayChangeForegroundRole, !group.isEntryImmutable("foreground-color"));
const QColor bg = group.readEntry("background-color", QColor());
item->setData(StoredBackgroundRole, bg.isValid() ? QBrush(bg) : QVariant());
if (!SystemInfo::isHighContrastModeActive()) {
item->setData(Qt::BackgroundRole, bg.isValid() ? QBrush(bg) : QVariant());
}
item->setData(MayChangeBackgroundRole, !group.isEntryImmutable("background-color"));
const QFont defaultFont = tryToFindFontFor(item);
if (group.hasKey("font")) {
const QFont font = group.readEntry("font", defaultFont);
item->setData(Qt::FontRole, font != defaultFont ? font : QVariant());
item->setData(HasFontRole, font != defaultFont);
} else {
QFont font = defaultFont;
font.setStrikeOut(group.readEntry("font-strikeout", false));
font.setItalic(group.readEntry("font-italic", false));
font.setBold(group.readEntry("font-bold", false));
item->setData(Qt::FontRole, font);
item->setData(HasFontRole, false);
}
item->setData(MayChangeFontRole, !group.isEntryImmutable("font"));
item->setData(MayChangeItalicRole, !group.isEntryImmutable("font-italic"));
item->setData(MayChangeBoldRole, !group.isEntryImmutable("font-bold"));
item->setData(MayChangeStrikeOutRole, !group.isEntryImmutable("font-strikeout"));
const QString iconName = group.readEntry("icon");
item->setData(Qt::DecorationRole, iconName.isEmpty() ? QVariant() : QIcon::fromTheme(iconName));
item->setData(IconNameRole, iconName.isEmpty() ? QVariant() : iconName);
item->setData(MayChangeIconRole, !group.isEntryImmutable("icon"));
}
static void resetString(QListWidgetItem *item, int role, int allowRole, const char *key)
{
if (item && item->data(allowRole).toBool()) {
auto config = item->data(ConfigGroupRole).value<KConfigGroup>();
config.revertToDefault(key);
item->setData(role, config.readEntry(key, QString()));
}
}
static void resetColor(QListWidgetItem *item, int role, int allowRole, const char *key)
{
if (item && item->data(allowRole).toBool()) {
auto config = item->data(ConfigGroupRole).value<KConfigGroup>();
config.revertToDefault(key);
const auto value = config.readEntry(key, QColor{});
item->setData(role, value.isValid() ? value : QVariant());
}
}
static void erase_if_allowed(QListWidgetItem *item, const int allowRole[], size_t numAllowRoles)
{
if (!item) {
return;
}
for (unsigned int i = 0; i < numAllowRoles; ++i)
if (!item->data(allowRole[i]).toBool()) {
return;
}
auto config = item->data(ConfigGroupRole).value<KConfigGroup>();
config.revertToDefault("font");
config.revertToDefault("font-bold");
config.revertToDefault("font-strikeout");
config.revertToDefault("font-italic");
auto value = config.readEntry<QFont>("font", tryToFindFontFor(item));
value.setBold(config.readEntry("font-bold", false));
value.setStrikeOut(config.readEntry("font-strikeout", false));
value.setItalic(config.readEntry("font-italic", false));
item->setData(Qt::FontRole, value);
}
static void set_default_appearance(QListWidgetItem *item)
{
if (!item) {
return;
}
if (!SystemInfo::isHighContrastModeActive()) {
resetColor(item, Qt::ForegroundRole, MayChangeForegroundRole, "foreground-color");
resetColor(item, Qt::BackgroundRole, MayChangeBackgroundRole, "background-color");
}
resetColor(item, StoredForegroundRole, MayChangeForegroundRole, "foreground-color");
resetColor(item, StoredBackgroundRole, MayChangeBackgroundRole, "background-color");
resetString(item, IconNameRole, MayChangeIconRole, "icon");
item->setData(Qt::DecorationRole, QIcon::fromTheme(item->data(ConfigGroupRole).value<KConfigGroup>().readEntry("icon", QString())));
static const int fontAllowRoles[] = {
MayChangeFontRole,
MayChangeItalicRole,
MayChangeBoldRole,
MayChangeStrikeOutRole,
};
erase_if_allowed(item, fontAllowRoles, sizeof(fontAllowRoles) / sizeof(int));
}
static void writeOrDelete(KConfigGroup &group, const char *key, const QVariant &value)
{
if (value.isValid()) {
group.writeEntry(key, value);
} else {
group.revertToDefault(key);
}
}
static QVariant brush2color(const QVariant &v)
{
if (v.isValid()) {
if (v.userType() == QMetaType::QColor) {
return v;
} else if (v.userType() == QMetaType::QBrush) {
return v.value<QBrush>().color();
}
}
return QVariant();
}
static void save_to_config(const QListWidgetItem *item, KConfigGroup &group)
{
if (!item) {
return;
}
writeOrDelete(group, "foreground-color", brush2color(item->data(StoredForegroundRole)));
writeOrDelete(group, "background-color", brush2color(item->data(StoredBackgroundRole)));
writeOrDelete(group, "icon", item->data(IconNameRole));
group.deleteEntry("font");
group.deleteEntry("font-strikeout");
group.deleteEntry("font-italic");
group.deleteEntry("font-bold");
if (item->data(HasFontRole).toBool()) {
writeOrDelete(group, "font", item->data(Qt::FontRole));
return;
}
if (is_strikeout(item)) {
group.writeEntry("font-strikeout", true);
}
if (is_italic(item)) {
group.writeEntry("font-italic", true);
}
if (is_bold(item)) {
group.writeEntry("font-bold", true);
}
}
static void kiosk_enable(QWidget *w, const QListWidgetItem *item, int allowRole)
{
if (!w) {
return;
}
if (item && !item->data(allowRole).toBool()) {
w->setEnabled(false);
w->setToolTip(i18nc("@info:tooltip", "This parameter has been locked down by the system administrator."));
} else {
w->setEnabled(item);
w->setToolTip(QString());
}
}
class Ui_AppearanceConfigWidget
{
public:
QTabWidget *tabWidget;
KMessageWidget *highContrastMsg;
QListWidget *categoriesLV;
QPushButton *iconButton;
QPushButton *foregroundButton;
QPushButton *backgroundButton;
QPushButton *fontButton;
QCheckBox *italicCB;
QCheckBox *boldCB;
QCheckBox *strikeoutCB;
QPushButton *defaultLookPB;
QCheckBox *tooltipValidityCheckBox;
QCheckBox *tooltipOwnerCheckBox;
QCheckBox *tooltipDetailsCheckBox;
QCheckBox *showExpirationCheckBox;
QSpinBox *ownCertificateThresholdSpinBox;
QSpinBox *otherCertificateThresholdSpinBox;
void setupUi(QWidget *parent)
{
if (parent->objectName().isEmpty())
parent->setObjectName(QString::fromUtf8("AppearanceConfigWidget"));
auto mainLayout = new QVBoxLayout{parent};
mainLayout->setContentsMargins({});
tabWidget = new QTabWidget(parent);
tabWidget->setDocumentMode(true);
tabWidget->setObjectName(QString::fromUtf8("tabWidget"));
{
auto tab = new QWidget{parent};
auto tabLayout = new QVBoxLayout{tab};
tabLayout->addWidget(new KSeparator{tab});
auto label = new QLabel{tab};
label->setText(i18nc("@info", "Show the following information in certificate list tooltips:"));
tabLayout->addWidget(label);
tooltipValidityCheckBox = new QCheckBox{i18nc("@option:check", "Show validity"), tab};
tabLayout->addWidget(tooltipValidityCheckBox);
tooltipOwnerCheckBox = new QCheckBox{i18nc("@option:check", "Show owner information"), tab};
tabLayout->addWidget(tooltipOwnerCheckBox);
tooltipDetailsCheckBox = new QCheckBox{i18nc("@option:check", "Show technical details"), tab};
tabLayout->addWidget(tooltipDetailsCheckBox);
tabLayout->addWidget(new KSeparator{tab});
showExpirationCheckBox = new QCheckBox{i18nc("@option:check", "Show upcoming certificate expiration"), tab};
tabLayout->addWidget(showExpirationCheckBox);
{
auto gridLayout = new QGridLayout;
const ExpiryCheckerConfig expiryConfig;
{
auto label = new QLabel{i18nc("@label:spinbox", "Threshold for own certificates:"), tab};
ownCertificateThresholdSpinBox = new QSpinBox{tab};
label->setBuddy(ownCertificateThresholdSpinBox);
const auto configItem = expiryConfig.ownKeyThresholdInDaysItem();
ownCertificateThresholdSpinBox->setMinimum(configItem->minValue().toInt());
ownCertificateThresholdSpinBox->setMaximum(configItem->maxValue().toInt());
ownCertificateThresholdSpinBox->setSpecialValueText(i18nc("@item never show expiry notification", "never"));
KLocalization::setupSpinBoxFormatString(ownCertificateThresholdSpinBox, ki18ncp("@item:valuesuffix", "%v day", "%v days"));
ownCertificateThresholdSpinBox->setToolTip(
i18nc("@info:tooltip", "Select the number of days you want to be warned in advance, if your own certificate is about to expire soon."));
gridLayout->addWidget(label, 0, 0);
gridLayout->addWidget(ownCertificateThresholdSpinBox, 0, 1);
}
{
auto label = new QLabel{i18nc("@label:spinbox", "Threshold for other certificates:"), tab};
otherCertificateThresholdSpinBox = new QSpinBox{tab};
label->setBuddy(otherCertificateThresholdSpinBox);
const auto configItem = expiryConfig.otherKeyThresholdInDaysItem();
otherCertificateThresholdSpinBox->setMinimum(configItem->minValue().toInt());
otherCertificateThresholdSpinBox->setMaximum(configItem->maxValue().toInt());
otherCertificateThresholdSpinBox->setSpecialValueText(i18nc("@item never show expiry notification", "never"));
KLocalization::setupSpinBoxFormatString(otherCertificateThresholdSpinBox, ki18ncp("@item:valuesuffix", "%v day", "%v days"));
otherCertificateThresholdSpinBox->setToolTip(
i18nc("@info:tooltip",
"Select the number of days you want to be warned in advance, if another person's certificate is about to expire soon."));
gridLayout->addWidget(label, 1, 0);
gridLayout->addWidget(otherCertificateThresholdSpinBox, 1, 1);
}
gridLayout->setColumnStretch(2, 1);
tabLayout->addLayout(gridLayout);
}
tabLayout->addStretch(1);
tabWidget->addTab(tab, i18nc("@title:tab", "General"));
}
auto tab_2 = new QWidget();
tab_2->setObjectName(QString::fromUtf8("tab_2"));
auto gridLayout = new QGridLayout(tab_2);
gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
highContrastMsg = new KMessageWidget(tab_2);
highContrastMsg->setObjectName(QString::fromUtf8("highContrastMsg"));
gridLayout->addWidget(highContrastMsg, 0, 0, 1, 2);
categoriesLV = new QListWidget(tab_2);
categoriesLV->setObjectName(QString::fromUtf8("categoriesLV"));
+ categoriesLV->setAccessibleName(i18nc("@label", "Categories"));
gridLayout->addWidget(categoriesLV, 1, 0, 1, 1);
auto vboxLayout = new QVBoxLayout();
vboxLayout->setObjectName(QString::fromUtf8("vboxLayout"));
iconButton = new QPushButton(tab_2);
iconButton->setText(i18nc("@action:button", "Set Icon..."));
iconButton->setObjectName(QString::fromUtf8("iconButton"));
iconButton->setEnabled(false);
vboxLayout->addWidget(iconButton);
foregroundButton = new QPushButton(tab_2);
foregroundButton->setText(i18nc("@action:button", "Set Text Color..."));
foregroundButton->setObjectName(QString::fromUtf8("foregroundButton"));
foregroundButton->setEnabled(false);
vboxLayout->addWidget(foregroundButton);
backgroundButton = new QPushButton(tab_2);
backgroundButton->setText(i18nc("@action:button", "Set Background Color..."));
backgroundButton->setObjectName(QString::fromUtf8("backgroundButton"));
backgroundButton->setEnabled(false);
vboxLayout->addWidget(backgroundButton);
fontButton = new QPushButton(tab_2);
fontButton->setText(i18nc("@action:button", "Set Font..."));
fontButton->setObjectName(QString::fromUtf8("fontButton"));
fontButton->setEnabled(false);
vboxLayout->addWidget(fontButton);
italicCB = new QCheckBox(tab_2);
italicCB->setText(i18nc("@option:check", "Italic"));
italicCB->setObjectName(QString::fromUtf8("italicCB"));
italicCB->setEnabled(false);
vboxLayout->addWidget(italicCB);
boldCB = new QCheckBox(tab_2);
boldCB->setText(i18nc("@option:check", "Bold"));
boldCB->setObjectName(QString::fromUtf8("boldCB"));
boldCB->setEnabled(false);
vboxLayout->addWidget(boldCB);
strikeoutCB = new QCheckBox(tab_2);
strikeoutCB->setText(i18nc("@option:check", "Strikeout"));
strikeoutCB->setObjectName(QString::fromUtf8("strikeoutCB"));
strikeoutCB->setEnabled(false);
vboxLayout->addWidget(strikeoutCB);
vboxLayout->addStretch(1);
defaultLookPB = new QPushButton(tab_2);
defaultLookPB->setText(i18nc("@action:button", "Default Appearance"));
defaultLookPB->setObjectName(QString::fromUtf8("defaultLookPB"));
defaultLookPB->setEnabled(false);
vboxLayout->addWidget(defaultLookPB);
gridLayout->addLayout(vboxLayout, 1, 1, 1, 1);
tabWidget->addTab(tab_2, i18nc("@title:tab", "Certificate Categories"));
mainLayout->addWidget(tabWidget);
}
};
class AppearanceConfigWidget::Private : public Ui_AppearanceConfigWidget
{
friend class ::Kleo::Config::AppearanceConfigWidget;
AppearanceConfigWidget *const q;
public:
explicit Private(AppearanceConfigWidget *qq)
: Ui_AppearanceConfigWidget()
, q(qq)
{
setupUi(q);
if (QLayout *const l = q->layout()) {
l->setContentsMargins(0, 0, 0, 0);
}
highContrastMsg->setVisible(SystemInfo::isHighContrastModeActive());
highContrastMsg->setMessageType(KMessageWidget::Warning);
highContrastMsg->setIcon(q->style()->standardIcon(QStyle::SP_MessageBoxWarning, nullptr, q));
highContrastMsg->setText(i18n("The preview of colors is disabled because high-contrast mode is active."));
highContrastMsg->setCloseButtonVisible(false);
if (Kleo::Settings{}.cmsEnabled()) {
auto w = new QWidget;
dnOrderWidget = new DNAttributeOrderConfigWidget{w};
dnOrderWidget->setObjectName(QLatin1StringView("dnOrderWidget"));
(new QVBoxLayout(w))->addWidget(dnOrderWidget);
tabWidget->addTab(w, i18n("DN-Attribute Order"));
connect(dnOrderWidget, &DNAttributeOrderConfigWidget::changed, q, &AppearanceConfigWidget::changed);
}
connect(iconButton, SIGNAL(clicked()), q, SLOT(slotIconClicked()));
#ifndef QT_NO_COLORDIALOG
connect(foregroundButton, SIGNAL(clicked()), q, SLOT(slotForegroundClicked()));
connect(backgroundButton, SIGNAL(clicked()), q, SLOT(slotBackgroundClicked()));
#else
foregroundButton->hide();
backgroundButton->hide();
#endif
#ifndef QT_NO_FONTDIALOG
connect(fontButton, SIGNAL(clicked()), q, SLOT(slotFontClicked()));
#else
fontButton->hide();
#endif
auto emitChanged = [this]() {
Q_EMIT q->changed();
};
connect(categoriesLV, SIGNAL(itemSelectionChanged()), q, SLOT(slotSelectionChanged()));
connect(defaultLookPB, SIGNAL(clicked()), q, SLOT(slotDefaultClicked()));
connect(italicCB, SIGNAL(toggled(bool)), q, SLOT(slotItalicToggled(bool)));
connect(boldCB, SIGNAL(toggled(bool)), q, SLOT(slotBoldToggled(bool)));
connect(strikeoutCB, SIGNAL(toggled(bool)), q, SLOT(slotStrikeOutToggled(bool)));
connect(tooltipValidityCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotTooltipValidityChanged(bool)));
connect(tooltipOwnerCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotTooltipOwnerChanged(bool)));
connect(tooltipDetailsCheckBox, SIGNAL(toggled(bool)), q, SLOT(slotTooltipDetailsChanged(bool)));
connect(showExpirationCheckBox, &QCheckBox::toggled, q, emitChanged);
connect(ownCertificateThresholdSpinBox, &QSpinBox::valueChanged, q, emitChanged);
connect(otherCertificateThresholdSpinBox, &QSpinBox::valueChanged, q, emitChanged);
}
private:
void enableDisableActions(QListWidgetItem *item);
QListWidgetItem *selectedItem() const;
private:
void slotIconClicked();
#ifndef QT_NO_COLORDIALOG
void slotForegroundClicked();
void slotBackgroundClicked();
#endif
#ifndef QT_NO_FONTDIALOG
void slotFontClicked();
#endif
void slotSelectionChanged();
void slotDefaultClicked();
void slotItalicToggled(bool);
void slotBoldToggled(bool);
void slotStrikeOutToggled(bool);
void slotTooltipValidityChanged(bool);
void slotTooltipOwnerChanged(bool);
void slotTooltipDetailsChanged(bool);
private:
Kleo::DNAttributeOrderConfigWidget *dnOrderWidget = nullptr;
};
AppearanceConfigWidget::AppearanceConfigWidget(QWidget *p, Qt::WindowFlags f)
: QWidget(p, f)
, d(new Private(this))
{
// load();
}
AppearanceConfigWidget::~AppearanceConfigWidget()
{
}
void AppearanceConfigWidget::Private::slotSelectionChanged()
{
enableDisableActions(selectedItem());
}
QListWidgetItem *AppearanceConfigWidget::Private::selectedItem() const
{
const QList<QListWidgetItem *> items = categoriesLV->selectedItems();
return items.empty() ? nullptr : items.front();
}
void AppearanceConfigWidget::Private::enableDisableActions(QListWidgetItem *item)
{
kiosk_enable(iconButton, item, MayChangeIconRole);
#ifndef QT_NO_COLORDIALOG
kiosk_enable(foregroundButton, item, MayChangeForegroundRole);
kiosk_enable(backgroundButton, item, MayChangeBackgroundRole);
#endif
#ifndef QT_NO_FONTDIALOG
kiosk_enable(fontButton, item, MayChangeFontRole);
#endif
kiosk_enable(italicCB, item, MayChangeItalicRole);
kiosk_enable(boldCB, item, MayChangeBoldRole);
kiosk_enable(strikeoutCB, item, MayChangeStrikeOutRole);
defaultLookPB->setEnabled(item);
italicCB->setChecked(is_italic(item));
boldCB->setChecked(is_bold(item));
strikeoutCB->setChecked(is_strikeout(item));
}
void AppearanceConfigWidget::Private::slotDefaultClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
set_default_appearance(item);
enableDisableActions(item);
Q_EMIT q->changed();
}
void AppearanceConfigWidget::defaults()
{
// use temporary KConfigSkeleton instances for (re)setting the values to the defaults;
// the setters respect the immutability of the individual settings, so that we don't have
// to check this ourselves
Settings settings;
settings.setShowExpiryNotifications(settings.findItem(QStringLiteral("ShowExpiryNotifications"))->getDefault().toBool());
d->showExpirationCheckBox->setChecked(settings.showExpiryNotifications());
{
ExpiryCheckerConfig expiryConfig;
expiryConfig.setOwnKeyThresholdInDays(expiryConfig.ownKeyThresholdInDaysItem()->getDefault().toInt());
d->ownCertificateThresholdSpinBox->setValue(expiryConfig.ownKeyThresholdInDays());
expiryConfig.setOtherKeyThresholdInDays(expiryConfig.otherKeyThresholdInDaysItem()->getDefault().toInt());
d->otherCertificateThresholdSpinBox->setValue(expiryConfig.otherKeyThresholdInDays());
}
// This simply means "default look for every category"
for (int i = 0, end = d->categoriesLV->count(); i != end; ++i) {
set_default_appearance(d->categoriesLV->item(i));
}
settings.setShowValidity(settings.findItem(QStringLiteral("ShowValidity"))->getDefault().toBool());
d->tooltipValidityCheckBox->setChecked(settings.showValidity());
settings.setShowOwnerInformation(settings.findItem(QStringLiteral("ShowOwnerInformation"))->getDefault().toBool());
d->tooltipOwnerCheckBox->setChecked(settings.showOwnerInformation());
settings.setShowCertificateDetails(settings.findItem(QStringLiteral("ShowCertificateDetails"))->getDefault().toBool());
d->tooltipDetailsCheckBox->setChecked(settings.showCertificateDetails());
if (d->dnOrderWidget) {
if (!settings.isImmutable(QStringLiteral("AttributeOrder"))) {
d->dnOrderWidget->setAttributeOrder(DNAttributes::defaultOrder());
}
}
Q_EMIT changed();
}
bool greaterThanBySpecificity(const QString &lhs, const QString &rhs)
{
const auto lFilter = KeyFilterManager::instance()->keyFilterByID(lhs);
const auto rFilter = KeyFilterManager::instance()->keyFilterByID(rhs);
if (!lFilter) {
return false;
}
if (!rFilter) {
return true;
}
return lFilter->specificity() > rFilter->specificity();
}
void AppearanceConfigWidget::load()
{
const Settings settings;
d->showExpirationCheckBox->setChecked(settings.showExpiryNotifications());
d->showExpirationCheckBox->setEnabled(!settings.isImmutable(QStringLiteral("ShowExpiryNotifications")));
{
const ExpiryCheckerConfig expiryConfig;
d->ownCertificateThresholdSpinBox->setValue(expiryConfig.ownKeyThresholdInDays());
d->ownCertificateThresholdSpinBox->setEnabled(!expiryConfig.ownKeyThresholdInDaysItem()->isImmutable());
d->otherCertificateThresholdSpinBox->setValue(expiryConfig.otherKeyThresholdInDays());
d->otherCertificateThresholdSpinBox->setEnabled(!expiryConfig.otherKeyThresholdInDaysItem()->isImmutable());
}
if (d->dnOrderWidget) {
d->dnOrderWidget->setAttributeOrder(DNAttributes::order());
d->dnOrderWidget->setEnabled(!settings.isImmutable(QStringLiteral("AttributeOrder")));
}
d->categoriesLV->clear();
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"));
if (!config) {
return;
}
QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$")));
std::ranges::stable_sort(groups, greaterThanBySpecificity);
for (const QString &group : groups) {
const KConfigGroup configGroup{config, group};
const bool isCmsSpecificKeyFilter = !configGroup.readEntry("is-openpgp-key", true);
auto item = new QListWidgetItem{d->categoriesLV};
// hide CMS-specific filters if CMS is disabled; we hide those filters
// instead of skipping them, so that they are not removed on save
item->setHidden(isCmsSpecificKeyFilter && !Kleo::Settings{}.cmsEnabled());
apply_config(configGroup, item);
}
d->tooltipValidityCheckBox->setChecked(settings.showValidity());
d->tooltipValidityCheckBox->setEnabled(!settings.isImmutable(QStringLiteral("ShowValidity")));
d->tooltipOwnerCheckBox->setChecked(settings.showOwnerInformation());
d->tooltipOwnerCheckBox->setEnabled(!settings.isImmutable(QStringLiteral("ShowOwnerInformation")));
d->tooltipDetailsCheckBox->setChecked(settings.showCertificateDetails());
d->tooltipDetailsCheckBox->setEnabled(!settings.isImmutable(QStringLiteral("ShowCertificateDetails")));
}
void AppearanceConfigWidget::save()
{
Settings settings;
settings.setShowExpiryNotifications(d->showExpirationCheckBox->isChecked());
if (d->dnOrderWidget) {
settings.setAttributeOrder(d->dnOrderWidget->attributeOrder());
DNAttributes::setOrder(settings.attributeOrder());
}
{
ExpiryCheckerConfig expiryConfig;
expiryConfig.setOwnKeyThresholdInDays(d->ownCertificateThresholdSpinBox->value());
expiryConfig.setOtherKeyThresholdInDays(d->otherCertificateThresholdSpinBox->value());
expiryConfig.save();
}
settings.setShowValidity(d->tooltipValidityCheckBox->isChecked());
settings.setShowOwnerInformation(d->tooltipOwnerCheckBox->isChecked());
settings.setShowCertificateDetails(d->tooltipDetailsCheckBox->isChecked());
settings.save();
KSharedConfigPtr config = KSharedConfig::openConfig(QStringLiteral("libkleopatrarc"));
if (!config) {
return;
}
// We know (assume) that the groups in the config object haven't changed,
// so we just iterate over them and over the listviewitems, and map one-to-one.
QStringList groups = config->groupList().filter(QRegularExpression(QStringLiteral("^Key Filter #\\d+$")));
std::ranges::stable_sort(groups, greaterThanBySpecificity);
#if 0
if (groups.isEmpty()) {
// If we created the default categories ourselves just now, then we need to make up their list
Q3ListViewItemIterator lvit(categoriesLV);
for (; lvit.current(); ++lvit) {
groups << lvit.current()->text(0);
}
}
#endif
for (int i = 0, end = std::min<int>(groups.size(), d->categoriesLV->count()); i != end; ++i) {
const QListWidgetItem *const item = d->categoriesLV->item(i);
Q_ASSERT(item);
KConfigGroup group(config, groups[i]);
save_to_config(item, group);
}
config->sync();
KeyFilterManager::instance()->reload();
}
void AppearanceConfigWidget::Private::slotIconClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
const QString iconName = KIconDialog::getIcon(/* repeating default arguments begin */
KIconLoader::Desktop,
KIconLoader::Application,
false,
0,
false,
/* repeating default arguments end */
q);
if (iconName.isEmpty()) {
return;
}
item->setIcon(QIcon::fromTheme(iconName));
item->setData(IconNameRole, iconName);
Q_EMIT q->changed();
}
#ifndef QT_NO_COLORDIALOG
void AppearanceConfigWidget::Private::slotForegroundClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
const QVariant v = brush2color(item->data(StoredForegroundRole));
const QColor initial = v.isValid() ? v.value<QColor>() : categoriesLV->palette().color(QPalette::Normal, QPalette::Text);
const QColor c = QColorDialog::getColor(initial, q);
if (c.isValid()) {
item->setData(StoredForegroundRole, QBrush(c));
if (!SystemInfo::isHighContrastModeActive()) {
item->setData(Qt::ForegroundRole, QBrush(c));
}
Q_EMIT q->changed();
}
}
void AppearanceConfigWidget::Private::slotBackgroundClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
const QVariant v = brush2color(item->data(StoredBackgroundRole));
const QColor initial = v.isValid() ? v.value<QColor>() : categoriesLV->palette().color(QPalette::Normal, QPalette::Base);
const QColor c = QColorDialog::getColor(initial, q);
if (c.isValid()) {
item->setData(StoredBackgroundRole, QBrush(c));
if (!SystemInfo::isHighContrastModeActive()) {
item->setData(Qt::BackgroundRole, QBrush(c));
}
Q_EMIT q->changed();
}
}
#endif // QT_NO_COLORDIALOG
#ifndef QT_NO_FONTDIALOG
void AppearanceConfigWidget::Private::slotFontClicked()
{
QListWidgetItem *const item = selectedItem();
if (!item) {
return;
}
const QVariant v = item->data(Qt::FontRole);
bool ok = false;
const QFont defaultFont = tryToFindFontFor(item);
const QFont initial = v.isValid() && v.userType() == QMetaType::QFont ? v.value<QFont>() : defaultFont;
QFont f = QFontDialog::getFont(&ok, initial, q);
if (!ok) {
return;
}
// disallow circumventing KIOSK:
if (!item->data(MayChangeItalicRole).toBool()) {
f.setItalic(initial.italic());
}
if (!item->data(MayChangeBoldRole).toBool()) {
f.setBold(initial.bold());
}
if (!item->data(MayChangeStrikeOutRole).toBool()) {
f.setStrikeOut(initial.strikeOut());
}
item->setData(Qt::FontRole, f != defaultFont ? f : QVariant());
item->setData(HasFontRole, true);
Q_EMIT q->changed();
}
#endif // QT_NO_FONTDIALOG
void AppearanceConfigWidget::Private::slotItalicToggled(bool on)
{
set_italic(selectedItem(), on);
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotBoldToggled(bool on)
{
set_bold(selectedItem(), on);
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotStrikeOutToggled(bool on)
{
set_strikeout(selectedItem(), on);
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotTooltipValidityChanged(bool)
{
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotTooltipOwnerChanged(bool)
{
Q_EMIT q->changed();
}
void AppearanceConfigWidget::Private::slotTooltipDetailsChanged(bool)
{
Q_EMIT q->changed();
}
#include "moc_appearanceconfigwidget.cpp"
diff --git a/src/dialogs/deletecertificatesdialog.cpp b/src/dialogs/deletecertificatesdialog.cpp
index b231a8298..8ea3b5dda 100644
--- a/src/dialogs/deletecertificatesdialog.cpp
+++ b/src/dialogs/deletecertificatesdialog.cpp
@@ -1,293 +1,297 @@
/*
This file is part of Kleopatra, the KDE keymanager
SPDX-FileCopyrightText: 2009 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include <config-kleopatra.h>
#include "deletecertificatesdialog.h"
#include <view/keytreeview.h>
#include <Libkleo/Algorithm>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
#include <Libkleo/KeyListModel>
#include <Libkleo/Stl_Util>
#include "kleopatra_debug.h"
#include <KConfigGroup>
#include <KLocalizedString>
#include <KMessageBox>
#include <KSharedConfig>
#include <KStandardGuiItem>
#include <QCursor>
#include <QDialogButtonBox>
#include <QLabel>
#include <QListWidget>
#include <QPushButton>
#include <QScreen>
#include <QTreeView>
#include <QVBoxLayout>
#include <QWhatsThis>
#include <gpgme++/key.h>
using namespace Kleo;
using namespace Kleo::Dialogs;
using namespace GpgME;
static int maxRecommendedWidth(QList<QListWidget *> widgets)
{
if (widgets.isEmpty()) {
return -1;
}
auto metrics = widgets[0]->fontMetrics();
auto maxWidth = -1;
for (const auto &widget : widgets) {
for (int i = 0; i < widget->count(); i++) {
auto width = metrics.boundingRect(widget->item(i)->text()).width();
if (width > maxWidth) {
maxWidth = width;
}
}
}
return std::min(widgets[0]->screen()->size().width(), maxWidth);
}
class DeleteCertificatesDialog::Private
{
friend class ::Kleo::Dialogs::DeleteCertificatesDialog;
DeleteCertificatesDialog *const q;
public:
explicit Private(DeleteCertificatesDialog *qq)
: q(qq)
, ui(q)
{
}
void readConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("DeleteCertificatesDialog"));
const QSize size = dialog.readEntry("Size", QSize(600, 400));
if (size.isValid()) {
q->resize(size);
}
}
void writeConfig()
{
KConfigGroup dialog(KSharedConfig::openStateConfig(), QStringLiteral("DeleteCertificatesDialog"));
dialog.writeEntry("Size", q->size());
dialog.sync();
}
void checkGroups(const std::vector<Key> &keys)
{
if (keys.empty()) {
return;
}
const auto &groups = KeyCache::instance()->groups();
QSet<QString> foundGroups;
for (const auto &key : keys) {
for (const auto &group : groups) {
if (group.keys().contains(key)) {
if (ui.groupsList->findItems(group.name(), Qt::MatchExactly).isEmpty()) {
ui.groupsList->addItem(group.name());
}
}
}
}
ui.groupsLB.setVisible(ui.groupsList->count() > 0);
ui.groupsList->setVisible(ui.groupsList->count() > 0);
ui.groupsList->setVisible(ui.groupsList->count() > 0);
if (ui.groupsList->count() == 1) {
ui.groupsLB.setText(i18np("The certificate is part of a group. Deleting it may prevent this recipient from decrypting messages to:",
"The certificates are part of a group. Deleting them may prevent these recipients from decrypting messages to:",
selectedKeys.size() + unselectedKeys.size()));
} else {
ui.groupsLB.setText(i18np("The certificate is part of several groups. Deleting it may prevent the recipient from decrypting messages to:",
"The certificates are part of several groups. Deleting them may prevent the recipients from decrypting messages to:",
selectedKeys.size() + unselectedKeys.size()));
}
q->resize({maxRecommendedWidth({ui.selectedList, ui.unselectedList, ui.groupsList}) + 50, q->minimumSizeHint().height()});
}
private:
std::vector<Key> selectedKeys;
std::vector<Key> unselectedKeys;
struct UI {
QLabel selectedLB;
QListWidget *selectedList;
QLabel unselectedLB;
QListWidget *unselectedList;
QLabel groupsLB;
QListWidget *groupsList;
QDialogButtonBox buttonBox;
QVBoxLayout vlay;
explicit UI(DeleteCertificatesDialog *qq)
: selectedLB({}, qq)
, selectedList(new QListWidget)
, unselectedLB({}, qq)
, unselectedList(new QListWidget)
, groupsLB({}, qq)
, groupsList(new QListWidget)
, buttonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel)
, vlay(qq)
{
Q_SET_OBJECT_NAME(selectedLB);
Q_SET_OBJECT_NAME(selectedList);
Q_SET_OBJECT_NAME(unselectedLB);
Q_SET_OBJECT_NAME(unselectedList);
Q_SET_OBJECT_NAME(groupsLB);
Q_SET_OBJECT_NAME(groupsList);
Q_SET_OBJECT_NAME(buttonBox);
Q_SET_OBJECT_NAME(vlay);
+ selectedList->setAccessibleName(i18nc("@label", "Certificates"));
+ unselectedList->setAccessibleName(i18nc("@label", "Certificates"));
+ groupsList->setAccessibleName(i18nc("@label", "Groups"));
+
vlay.addWidget(&selectedLB);
vlay.addWidget(selectedList, 1);
vlay.addWidget(&unselectedLB);
vlay.addWidget(unselectedList, 1);
vlay.addWidget(&groupsLB);
vlay.addWidget(groupsList, 1);
vlay.addWidget(&buttonBox);
unselectedLB.setContextMenuPolicy(Qt::NoContextMenu);
buttonBox.button(QDialogButtonBox::Ok)->setText(i18nc("@action:button", "Delete"));
connect(&unselectedLB, &QLabel::linkActivated, qq, [qq]() {
KMessageBox::information(qq,
xi18nc("@info",
"<para>When you delete CA certificates (both root CAs and intermediate CAs), "
"the certificates issued by them will also be deleted.</para>"
"<para>This can be nicely seen in <application>Kleopatra</application>'s "
"hierarchical view mode: In this mode, if you delete a certificate that has "
"children, those children will also be deleted. Think of CA certificates as "
"folders containing other certificates: When you delete the folder, you "
"delete its contents, too.</para>"));
});
groupsLB.setVisible(false);
unselectedList->setVisible(false);
groupsList->setVisible(false);
connect(&buttonBox, SIGNAL(accepted()), qq, SLOT(accept()));
connect(&buttonBox, &QDialogButtonBox::rejected, qq, &QDialog::reject);
}
} ui;
};
DeleteCertificatesDialog::DeleteCertificatesDialog(QWidget *p)
: QDialog(p)
, d(new Private(this))
{
d->readConfig();
}
DeleteCertificatesDialog::~DeleteCertificatesDialog()
{
d->writeConfig();
}
void DeleteCertificatesDialog::setSelectedKeys(const std::vector<Key> &keys)
{
d->selectedKeys = keys;
for (const auto &key : keys) {
d->ui.selectedList->addItem(Formatting::summaryLine(key));
}
d->ui.selectedLB.setText(
i18np("The following certificate was selected for deletion:", "The following certificates were selected for deletion:", keys.size()));
d->checkGroups(keys);
}
void DeleteCertificatesDialog::setUnselectedKeys(const std::vector<Key> &keys)
{
d->unselectedKeys = keys;
d->ui.unselectedLB.setVisible(!keys.empty());
for (const auto &key : keys) {
d->ui.unselectedList->addItem(Formatting::summaryLine(key));
}
d->ui.unselectedList->setVisible(d->ui.unselectedList->count() > 0);
d->ui.unselectedLB.setText(
i18np("The following certificate will be deleted even though you did <b>not</b> "
"explicitly select it (<a href=\"whatsthis://\">Why?</a>):",
"The following certificates will be deleted even though you did <b>not</b> "
"explicitly select them (<a href=\"whatsthis://\">Why?</a>):",
keys.size()));
d->checkGroups(keys);
}
std::vector<Key> DeleteCertificatesDialog::keys() const
{
const std::vector<Key> sel = d->selectedKeys;
const std::vector<Key> uns = d->unselectedKeys;
std::vector<Key> result;
result.reserve(sel.size() + uns.size());
result.insert(result.end(), sel.begin(), sel.end());
result.insert(result.end(), uns.begin(), uns.end());
return result;
}
void DeleteCertificatesDialog::accept()
{
const std::vector<Key> sel = d->selectedKeys;
const std::vector<Key> uns = d->unselectedKeys;
const uint secret =
std::count_if(sel.cbegin(), sel.cend(), std::mem_fn(&Key::hasSecret)) + std::count_if(uns.cbegin(), uns.cend(), std::mem_fn(&Key::hasSecret));
const uint total = sel.size() + uns.size();
int ret = KMessageBox::Continue;
if (secret)
ret = KMessageBox::warningContinueCancel(this,
secret == total ? i18np("The certificate to be deleted is your own. "
"It contains private key material, "
"which is needed to decrypt past communication "
"encrypted to the certificate, and should therefore "
"not be deleted.",
"All of the certificates to be deleted "
"are your own. "
"They contain private key material, "
"which is needed to decrypt past communication "
"encrypted to the certificate, and should therefore "
"not be deleted.",
secret)
: i18np("One of the certificates to be deleted "
"is your own. "
"It contains private key material, "
"which is needed to decrypt past communication "
"encrypted to the certificate, and should therefore "
"not be deleted.",
"Some of the certificates to be deleted "
"are your own. "
"They contain private key material, "
"which is needed to decrypt past communication "
"encrypted to the certificate, and should therefore "
"not be deleted.",
secret),
i18nc("@title:window", "Secret Key Deletion"),
KStandardGuiItem::guiItem(KStandardGuiItem::Delete),
KStandardGuiItem::cancel(),
QString(),
KMessageBox::Notify | KMessageBox::Dangerous);
if (ret == KMessageBox::Continue) {
QDialog::accept();
} else {
QDialog::reject();
}
}
#include "moc_deletecertificatesdialog.cpp"
diff --git a/src/dialogs/lookupcertificatesdialog.cpp b/src/dialogs/lookupcertificatesdialog.cpp
index 57f1ed25a..83879effe 100644
--- a/src/dialogs/lookupcertificatesdialog.cpp
+++ b/src/dialogs/lookupcertificatesdialog.cpp
@@ -1,521 +1,522 @@
/*
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 <config-kleopatra.h>
#include "lookupcertificatesdialog.h"
#include <view/textoverlay.h>
#include <kleopatra_debug.h>
#include <Libkleo/Formatting>
#include <Libkleo/GnuPG>
#include <Libkleo/KeyList>
#include <Libkleo/TreeWidget>
#include <KConfigGroup>
#include <KLocalizedString>
#include <KSeparator>
#include <KSharedConfig>
#include <KStandardActions>
#include <QClipboard>
#include <QDialogButtonBox>
#include <QGridLayout>
#include <QGuiApplication>
#include <QHeaderView>
#include <QLabel>
#include <QLineEdit>
#include <QMenu>
#include <QPushButton>
#include <QRegularExpression>
#include <QRegularExpressionValidator>
#include <QTreeView>
#include <QVBoxLayout>
#include <gpgme++/key.h>
using namespace Kleo;
using namespace Kleo::Dialogs;
using namespace GpgME;
static const int KeyWithOriginRole = 0x201;
class LookupCertificatesDialog::Private
{
friend class ::Kleo::Dialogs::LookupCertificatesDialog;
LookupCertificatesDialog *const q;
public:
enum Columns {
Name,
Email,
Fingerprint,
ValidFrom,
ValidUntil,
Protocol,
KeyID,
Origin,
};
explicit Private(LookupCertificatesDialog *qq);
~Private();
private:
void slotSelectionChanged()
{
enableDisableWidgets();
}
void slotSearchTextChanged()
{
enableDisableWidgets();
}
void slotSearchClicked()
{
Q_EMIT q->searchTextChanged(searchText());
}
void slotDetailsClicked()
{
if (!q->selectedCertificates().empty()) {
Q_EMIT q->detailsRequested(q->selectedCertificates().front().key);
}
}
void slotSaveAsClicked()
{
std::vector<GpgME::Key> keys;
for (const auto &[key, origin] : q->selectedCertificates()) {
keys.push_back(key);
}
Q_EMIT q->saveAsRequested(keys);
}
void readConfig();
void writeConfig();
void enableDisableWidgets();
QString searchText() const
{
return ui.findED->text().trimmed();
}
std::vector<KeyWithOrigin> selectedCertificates() const
{
const QAbstractItemView *const view = ui.resultTV;
if (!view) {
return {};
}
const auto sm = view->selectionModel();
Q_ASSERT(sm);
std::vector<KeyWithOrigin> keys;
for (const auto &index : sm->selectedRows()) {
const auto key = ui.resultTV->itemFromIndex(index)->data(Private::Name, KeyWithOriginRole).value<KeyWithOrigin>();
Q_ASSERT(!key.key.isNull());
keys.push_back(key);
}
return keys;
}
int numSelectedCertificates() const
{
return ui.resultTV->selectedItems().size();
}
void copySelectedValue()
{
auto clipboardData = ui.resultTV->currentIndex().data(Kleo::ClipboardRole).toString();
if (clipboardData.isEmpty()) {
clipboardData = ui.resultTV->currentIndex().data().toString();
}
QGuiApplication::clipboard()->setText(clipboardData);
}
QValidator *queryValidator();
void updateQueryMode();
private:
QueryMode queryMode = AnyQuery;
bool passive;
QValidator *anyQueryValidator = nullptr;
QValidator *emailQueryValidator = nullptr;
bool initial = false;
struct Ui {
QLabel *guidanceLabel;
QLabel *findLB;
QLineEdit *findED;
QPushButton *findPB;
Kleo::TreeWidget *resultTV;
TextOverlay *overlay;
QPushButton *selectAllPB;
QPushButton *deselectAllPB;
QPushButton *detailsPB;
QPushButton *saveAsPB;
QDialogButtonBox *buttonBox;
void setupUi(LookupCertificatesDialog *dialog)
{
auto verticalLayout = new QVBoxLayout{dialog};
auto gridLayout = new QGridLayout{};
int row = 0;
guidanceLabel = new QLabel{dialog};
gridLayout->addWidget(guidanceLabel, row, 0, 1, 3);
row++;
findLB = new QLabel{i18n("Find:"), dialog};
gridLayout->addWidget(findLB, row, 0, 1, 1);
findED = new QLineEdit{dialog};
findLB->setBuddy(findED);
gridLayout->addWidget(findED, row, 1, 1, 1);
findPB = new QPushButton{i18n("Search"), dialog};
findPB->setAutoDefault(false);
gridLayout->addWidget(findPB, row, 2, 1, 1);
row++;
gridLayout->addWidget(new KSeparator{Qt::Horizontal, dialog}, row, 0, 1, 3);
row++;
resultTV = new Kleo::TreeWidget(dialog);
+ resultTV->setAccessibleName(i18nc("@label", "Results"));
resultTV->setEnabled(true);
resultTV->setMinimumSize(QSize(400, 0));
overlay = new TextOverlay{resultTV, dialog};
overlay->hide();
gridLayout->addWidget(resultTV, row, 0, 1, 2);
auto buttonLayout = new QVBoxLayout{};
selectAllPB = new QPushButton{i18n("Select All"), dialog};
selectAllPB->setEnabled(false);
selectAllPB->setAutoDefault(false);
buttonLayout->addWidget(selectAllPB);
deselectAllPB = new QPushButton{i18n("Deselect All"), dialog};
deselectAllPB->setEnabled(false);
deselectAllPB->setAutoDefault(false);
buttonLayout->addWidget(deselectAllPB);
buttonLayout->addStretch();
detailsPB = new QPushButton{i18n("Details..."), dialog};
detailsPB->setEnabled(false);
detailsPB->setAutoDefault(false);
buttonLayout->addWidget(detailsPB);
saveAsPB = new QPushButton{i18n("Save As..."), dialog};
saveAsPB->setEnabled(false);
saveAsPB->setAutoDefault(false);
buttonLayout->addWidget(saveAsPB);
gridLayout->addLayout(buttonLayout, row, 2, 1, 1);
verticalLayout->addLayout(gridLayout);
buttonBox = new QDialogButtonBox{dialog};
buttonBox->setStandardButtons(QDialogButtonBox::Close | QDialogButtonBox::Save);
verticalLayout->addWidget(buttonBox);
QObject::connect(findED, SIGNAL(returnPressed()), findPB, SLOT(animateClick()));
QObject::connect(buttonBox, SIGNAL(accepted()), dialog, SLOT(accept()));
QObject::connect(buttonBox, SIGNAL(rejected()), dialog, SLOT(reject()));
QObject::connect(findPB, SIGNAL(clicked()), dialog, SLOT(slotSearchClicked()));
QObject::connect(detailsPB, SIGNAL(clicked()), dialog, SLOT(slotDetailsClicked()));
QObject::connect(saveAsPB, SIGNAL(clicked()), dialog, SLOT(slotSaveAsClicked()));
QObject::connect(findED, SIGNAL(textChanged(QString)), dialog, SLOT(slotSearchTextChanged()));
QMetaObject::connectSlotsByName(dialog);
resultTV->setHeaderLabels({
i18nc("@title:column", "Name"),
i18nc("@title:column", "Email"),
i18nc("@title:column", "Fingerprint"),
i18nc("@title:column", "Valid From"),
i18nc("@title:column", "Valid Until"),
i18nc("@title:column", "Protocol"),
i18nc("@title:column", "Key ID"),
i18nc("@title:column", "Origin"),
});
resultTV->setSelectionMode(QAbstractItemView::ExtendedSelection);
resultTV->setContextMenuPolicy(Qt::CustomContextMenu);
connect(resultTV, &QTreeView::customContextMenuRequested, dialog, [this, dialog](const auto &pos) {
auto menu = new QMenu;
menu->setAttribute(Qt::WA_DeleteOnClose, true);
menu->addAction(KStandardActions::copy(
dialog,
[dialog]() {
dialog->d->copySelectedValue();
},
dialog));
menu->popup(resultTV->mapToGlobal(pos));
});
}
explicit Ui(LookupCertificatesDialog *q)
{
q->setWindowTitle(i18nc("@title:window", "Lookup on Server"));
setupUi(q);
saveAsPB->hide(); // ### not yet implemented in LookupCertificatesCommand
findED->setClearButtonEnabled(true);
importPB()->setText(i18n("Import"));
importPB()->setEnabled(false);
connect(resultTV, SIGNAL(doubleClicked(QModelIndex)), q, SLOT(slotDetailsClicked()));
findED->setFocus();
connect(selectAllPB, &QPushButton::clicked, resultTV, &QTreeView::selectAll);
connect(deselectAllPB, &QPushButton::clicked, resultTV, &QTreeView::clearSelection);
}
QPushButton *importPB() const
{
return buttonBox->button(QDialogButtonBox::Save);
}
QPushButton *closePB() const
{
return buttonBox->button(QDialogButtonBox::Close);
}
} ui;
};
LookupCertificatesDialog::Private::Private(LookupCertificatesDialog *qq)
: q(qq)
, passive(false)
, ui(q)
{
connect(ui.resultTV->selectionModel(), SIGNAL(selectionChanged(QItemSelection, QItemSelection)), q, SLOT(slotSelectionChanged()));
updateQueryMode();
}
LookupCertificatesDialog::Private::~Private()
{
}
void LookupCertificatesDialog::Private::readConfig()
{
KConfigGroup configGroup(KSharedConfig::openStateConfig(), QStringLiteral("LookupCertificatesDialog"));
if (!ui.resultTV->restoreColumnLayout(QStringLiteral("LookupCertificatesDialog"))) {
ui.resultTV->setColumnHidden(Private::KeyID, true);
initial = true;
ui.resultTV->resizeToContentsLimited();
}
const QSize size = configGroup.readEntry("Size", QSize(600, 400));
if (size.isValid()) {
q->resize(size);
}
}
void LookupCertificatesDialog::Private::writeConfig()
{
KConfigGroup configGroup(KSharedConfig::openStateConfig(), QStringLiteral("LookupCertificatesDialog"));
configGroup.writeEntry("Size", q->size());
configGroup.sync();
}
static QString guidanceText(LookupCertificatesDialog::QueryMode mode)
{
switch (mode) {
default:
qCWarning(KLEOPATRA_LOG) << __func__ << "Unknown query mode:" << mode;
[[fallthrough]];
case LookupCertificatesDialog::AnyQuery:
return xi18nc("@info", "Enter a search term to search for matching certificates.");
case LookupCertificatesDialog::EmailQuery:
return xi18nc("@info", "Enter an email address to search for matching certificates.");
};
}
QValidator *LookupCertificatesDialog::Private::queryValidator()
{
switch (queryMode) {
default:
qCWarning(KLEOPATRA_LOG) << __func__ << "Unknown query mode:" << queryMode;
[[fallthrough]];
case AnyQuery: {
if (!anyQueryValidator) {
// allow any query with at least one non-whitespace character
anyQueryValidator = new QRegularExpressionValidator{QRegularExpression{QStringLiteral(".*\\S+.*")}, q};
}
return anyQueryValidator;
}
case EmailQuery: {
if (!emailQueryValidator) {
// allow anything that looks remotely like an email address, i.e.
// anything with an '@' surrounded by non-whitespace characters
const QRegularExpression simpleEmailRegex{QStringLiteral(".*\\S+@\\S+.*")};
emailQueryValidator = new QRegularExpressionValidator{simpleEmailRegex, q};
}
return emailQueryValidator;
}
}
}
void LookupCertificatesDialog::Private::updateQueryMode()
{
ui.guidanceLabel->setText(guidanceText(queryMode));
ui.findED->setValidator(queryValidator());
}
LookupCertificatesDialog::LookupCertificatesDialog(QWidget *p, Qt::WindowFlags f)
: QDialog(p, f)
, d(new Private(this))
{
d->ui.findPB->setEnabled(false);
d->readConfig();
}
LookupCertificatesDialog::~LookupCertificatesDialog()
{
d->writeConfig();
}
void LookupCertificatesDialog::setQueryMode(QueryMode mode)
{
d->queryMode = mode;
d->updateQueryMode();
}
LookupCertificatesDialog::QueryMode LookupCertificatesDialog::queryMode() const
{
return d->queryMode;
}
void LookupCertificatesDialog::setCertificates(const std::vector<KeyWithOrigin> &certs)
{
d->ui.resultTV->setFocus();
d->ui.resultTV->clear();
for (const auto &[cert, origin] : certs) {
auto item = new QTreeWidgetItem;
item->setData(Private::Name, Qt::DisplayRole, Formatting::prettyName(cert));
item->setData(Private::Email, Qt::DisplayRole, Formatting::prettyEMail(cert));
item->setData(Private::Fingerprint, Qt::DisplayRole, Formatting::prettyID(cert.primaryFingerprint()));
item->setData(Private::Fingerprint, Qt::AccessibleTextRole, Formatting::accessibleHexID(cert.primaryFingerprint()));
item->setData(Private::Fingerprint, Kleo::ClipboardRole, QString::fromLatin1(cert.primaryFingerprint()));
item->setData(Private::ValidFrom, Qt::DisplayRole, Formatting::creationDateString(cert));
item->setData(Private::ValidFrom, Qt::AccessibleTextRole, Formatting::accessibleCreationDate(cert));
item->setData(Private::ValidUntil, Qt::DisplayRole, Formatting::expirationDateString(cert));
item->setData(Private::ValidUntil, Qt::AccessibleTextRole, Formatting::accessibleExpirationDate(cert));
item->setData(Private::KeyID, Qt::DisplayRole, Formatting::prettyID(cert.keyID()));
item->setData(Private::KeyID, Qt::AccessibleTextRole, Formatting::accessibleHexID(cert.keyID()));
item->setData(Private::KeyID, Kleo::ClipboardRole, QString::fromLatin1(cert.keyID()));
if (cert.protocol() == Protocol::CMS) {
item->setData(Private::Origin, Qt::DisplayRole, i18n("LDAP"));
} else if (origin == GpgME::Key::OriginKS) {
if (keyserver().startsWith(QStringLiteral("ldap:")) || keyserver().startsWith(QStringLiteral("ldaps:"))) {
item->setData(Private::Origin, Qt::DisplayRole, i18n("LDAP"));
} else {
item->setData(Private::Origin, Qt::DisplayRole, i18n("Keyserver"));
}
} else {
item->setData(Private::Origin, Qt::DisplayRole, Formatting::origin(origin));
}
item->setData(Private::Protocol, Qt::DisplayRole, Formatting::displayName(cert.protocol()));
item->setData(Private::Name, KeyWithOriginRole, QVariant::fromValue(KeyWithOrigin{cert, origin}));
d->ui.resultTV->addTopLevelItem(item);
}
if (certs.size() == 1) {
d->ui.resultTV->setCurrentIndex(d->ui.resultTV->model()->index(0, 0));
}
if (d->initial && d->ui.resultTV->model()->rowCount() > 0) {
d->initial = false;
d->ui.resultTV->resizeToContentsLimited();
}
}
std::vector<KeyWithOrigin> LookupCertificatesDialog::selectedCertificates() const
{
return d->selectedCertificates();
}
void LookupCertificatesDialog::setPassive(bool on)
{
if (d->passive == on) {
return;
}
d->passive = on;
d->enableDisableWidgets();
}
bool LookupCertificatesDialog::isPassive() const
{
return d->passive;
}
void LookupCertificatesDialog::setSearchText(const QString &text)
{
d->ui.findED->setText(text);
}
QString LookupCertificatesDialog::searchText() const
{
return d->ui.findED->text();
}
void LookupCertificatesDialog::setOverlayText(const QString &text)
{
if (text.isEmpty()) {
d->ui.overlay->hideOverlay();
} else {
d->ui.overlay->setText(text);
d->ui.overlay->showOverlay();
}
d->ui.selectAllPB->setEnabled(text.isEmpty());
d->ui.deselectAllPB->setEnabled(text.isEmpty());
}
QString LookupCertificatesDialog::overlayText() const
{
return d->ui.overlay->text();
}
void LookupCertificatesDialog::accept()
{
Q_ASSERT(!selectedCertificates().empty());
Q_EMIT importRequested(selectedCertificates());
QDialog::accept();
}
void LookupCertificatesDialog::Private::enableDisableWidgets()
{
// enable/disable everything except 'close', based on passive:
const QList<QObject *> list = q->children();
for (QObject *const o : list) {
if (QWidget *const w = qobject_cast<QWidget *>(o)) {
w->setDisabled(passive && w != ui.closePB() && w != ui.buttonBox);
}
}
if (passive) {
return;
}
q->setOverlayText({});
ui.findPB->setEnabled(ui.findED->hasAcceptableInput());
const int n = q->selectedCertificates().size();
ui.detailsPB->setEnabled(n == 1);
ui.saveAsPB->setEnabled(n == 1);
ui.importPB()->setEnabled(n != 0);
ui.importPB()->setDefault(false); // otherwise Import becomes default button if enabled and return triggers both a search and accept()
}
#include "moc_lookupcertificatesdialog.cpp"
diff --git a/src/dialogs/trustchainwidget.cpp b/src/dialogs/trustchainwidget.cpp
index 69a191561..2a4ae2b0d 100644
--- a/src/dialogs/trustchainwidget.cpp
+++ b/src/dialogs/trustchainwidget.cpp
@@ -1,96 +1,97 @@
/* SPDX-FileCopyrightText: 2016 Klarälvdalens Datakonsult AB
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "trustchainwidget.h"
#include "kleopatra_debug.h"
#include <KLocalizedString>
#include <QDialogButtonBox>
#include <QPushButton>
#include <QTreeWidget>
#include <QTreeWidgetItem>
#include <QVBoxLayout>
#include <gpgme++/key.h>
#include <Libkleo/Formatting>
#include <Libkleo/KeyCache>
class TrustChainWidget::Private
{
TrustChainWidget *const q;
public:
Private(TrustChainWidget *qq)
: q(qq)
, ui{qq}
{
}
GpgME::Key key;
struct UI {
QTreeWidget *treeWidget;
UI(QWidget *widget)
{
auto mainLayout = new QVBoxLayout{widget};
mainLayout->setContentsMargins({});
treeWidget = new QTreeWidget{widget};
+ treeWidget->setAccessibleName(i18nc("@label", "Certificate chain"));
// Breeze draws no frame for scroll areas that are the only widget in a layout...unless we force it
treeWidget->setProperty("_breeze_force_frame", true);
treeWidget->setHeaderHidden(true);
mainLayout->addWidget(treeWidget);
}
} ui;
};
TrustChainWidget::TrustChainWidget(QWidget *parent)
: QWidget(parent)
, d(new Private(this))
{
}
TrustChainWidget::~TrustChainWidget()
{
}
void TrustChainWidget::setKey(const GpgME::Key &key)
{
if (key.protocol() != GpgME::CMS) {
qCDebug(KLEOPATRA_LOG) << "Trust chain is only supported for CMS keys";
return;
}
d->key = key;
d->ui.treeWidget->clear();
const auto chain = Kleo::KeyCache::instance()->findIssuers(key, Kleo::KeyCache::RecursiveSearch | Kleo::KeyCache::IncludeSubject);
if (chain.empty()) {
return;
}
QTreeWidgetItem *last = nullptr;
if (!chain.back().isRoot()) {
last = new QTreeWidgetItem(d->ui.treeWidget);
last->setText(0, i18n("Issuer Certificate Not Found (%1)", Kleo::Formatting::prettyDN(chain.back().issuerName())));
const QBrush &fg = d->ui.treeWidget->palette().brush(QPalette::Disabled, QPalette::WindowText);
last->setForeground(0, fg);
}
for (auto it = chain.rbegin(), end = chain.rend(); it != end; ++it) {
last = last ? new QTreeWidgetItem(last) : new QTreeWidgetItem(d->ui.treeWidget);
last->setText(0, Kleo::Formatting::prettyDN(it->userID(0).id()));
}
d->ui.treeWidget->expandAll();
}
GpgME::Key TrustChainWidget::key() const
{
return d->key;
}
#include "moc_trustchainwidget.cpp"

File Metadata

Mime Type
text/x-diff
Expires
Sat, Feb 7, 5:28 PM (7 h, 37 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
95/ac/aa7fff6438e1aaa6dadabfd54eed

Event Timeline