diff --git a/src/commands/editgroupcommand.cpp b/src/commands/editgroupcommand.cpp index c77c8ed4e..18138ed2b 100644 --- a/src/commands/editgroupcommand.cpp +++ b/src/commands/editgroupcommand.cpp @@ -1,129 +1,130 @@ /* -*- mode: c++; c-basic-offset:4 -*- commands/editgroupcommand.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "editgroupcommand.h" #include "command_p.h" #include "dialogs/editgroupdialog.h" #include #include #include using namespace Kleo; using namespace Kleo::Commands; using namespace Kleo::Dialogs; class EditGroupCommand::Private : public Command::Private { friend class ::Kleo::Commands::EditGroupCommand; EditGroupCommand *q_func() const { return static_cast(q); } public: explicit Private(EditGroupCommand *qq, const KeyGroup &group, QWidget *parent); ~Private(); private: void start(); void slotDialogAccepted(); void slotDialogRejected(); void ensureDialogCreated(); private: KeyGroup group; QPointer dialog; }; EditGroupCommand::Private *EditGroupCommand::d_func() { return static_cast(d.get()); } const EditGroupCommand::Private *EditGroupCommand::d_func() const { return static_cast(d.get()); } #define d d_func() #define q q_func() EditGroupCommand::Private::Private(EditGroupCommand *qq, const KeyGroup &group, QWidget *parent) : Command::Private(qq, parent) , group(group) { } EditGroupCommand::Private::~Private() { } void EditGroupCommand::Private::start() { ensureDialogCreated(); dialog->setWindowTitle(i18nc("@title:window", "Edit Group")); dialog->setGroupName(group.name()); const KeyGroup::Keys &keys = group.keys(); dialog->setGroupKeys(std::vector(keys.cbegin(), keys.cend())); dialog->show(); } void EditGroupCommand::Private::slotDialogAccepted() { + group.setName(dialog->groupName()); group.setKeys(dialog->groupKeys()); KeyCache::mutableInstance()->update(group); finished(); } void EditGroupCommand::Private::slotDialogRejected() { canceled(); } void EditGroupCommand::Private::ensureDialogCreated() { if (dialog) { return; } dialog = new EditGroupDialog; applyWindowID(dialog); dialog->setAttribute(Qt::WA_DeleteOnClose); connect(dialog, &QDialog::accepted, q, [this] () { slotDialogAccepted(); }); connect(dialog, &QDialog::rejected, q, [this] () { slotDialogRejected(); }); } EditGroupCommand::EditGroupCommand(const KeyGroup &group, QWidget *parent) : Command(new Private(this, group, parent)) { } EditGroupCommand::~EditGroupCommand() { } void EditGroupCommand::doStart() { d->start(); } void EditGroupCommand::doCancel() { } #undef d #undef q diff --git a/src/dialogs/editgroupdialog.cpp b/src/dialogs/editgroupdialog.cpp index 4ef988ce9..0665abead 100644 --- a/src/dialogs/editgroupdialog.cpp +++ b/src/dialogs/editgroupdialog.cpp @@ -1,298 +1,311 @@ /* dialogs/editgroupdialog.cpp This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #include "editgroupdialog.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kleopatra_debug.h" using namespace Kleo; using namespace Kleo::Dialogs; using namespace GpgME; Q_DECLARE_METATYPE(GpgME::Key) namespace { class ProxyModel : public AbstractKeyListSortFilterProxyModel { Q_OBJECT public: ProxyModel(QObject *parent = nullptr) : AbstractKeyListSortFilterProxyModel(parent) { } ~ProxyModel() override = default; ProxyModel *clone() const override { // compiler-generated copy ctor is fine! return new ProxyModel(*this); } }; } class EditGroupDialog::Private { friend class ::Kleo::Dialogs::EditGroupDialog; EditGroupDialog *const q; struct { - QLabel *groupNameLabel = nullptr; + QLineEdit *groupNameEdit = nullptr; QLineEdit *availableKeysFilter = nullptr; QListView *availableKeysList = nullptr; QLineEdit *groupKeysFilter = nullptr; QListView *groupKeysList = nullptr; QDialogButtonBox *buttonBox = nullptr; } ui; AbstractKeyListModel *availableKeysModel = nullptr; ProxyModel *availableKeysFilterModel = nullptr; AbstractKeyListModel *groupKeysModel = nullptr; ProxyModel *groupKeysFilterModel = nullptr; public: Private(EditGroupDialog *qq) : q(qq) { auto mainLayout = new QVBoxLayout(q); - ui.groupNameLabel = new QLabel(); - ui.groupNameLabel->setWordWrap(true); - mainLayout->addWidget(ui.groupNameLabel); + auto groupNameLayout = new QHBoxLayout(); + groupNameLayout->addWidget(new QLabel(i18nc("Name of a group of keys", "Name:"))); + ui.groupNameEdit = new QLineEdit(); + groupNameLayout->addWidget(ui.groupNameEdit); + mainLayout->addLayout(groupNameLayout); mainLayout->addWidget(new KSeparator(Qt::Horizontal)); auto centerLayout = new QHBoxLayout(); auto availableKeysLayout = new QVBoxLayout(); availableKeysLayout->addWidget(new QLabel(i18n("Available keys:"))); ui.availableKeysFilter = new QLineEdit(); ui.availableKeysFilter->setClearButtonEnabled(true); ui.availableKeysFilter->setPlaceholderText(i18nc("Placeholder text", "Search...")); availableKeysLayout->addWidget(ui.availableKeysFilter); availableKeysModel = AbstractKeyListModel::createFlatKeyListModel(q); availableKeysModel->useKeyCache(true, KeyList::AllKeys); availableKeysFilterModel = new ProxyModel(q); availableKeysFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); availableKeysFilterModel->setFilterKeyColumn(KeyList::Summary); availableKeysFilterModel->setSortCaseSensitivity(Qt::CaseInsensitive); availableKeysFilterModel->setSourceModel(availableKeysModel); availableKeysFilterModel->sort(KeyList::Summary, Qt::AscendingOrder); ui.availableKeysList = new QListView(); ui.availableKeysList->setModel(availableKeysFilterModel); ui.availableKeysList->setModelColumn(KeyList::Summary); ui.availableKeysList->setSelectionBehavior(QAbstractItemView::SelectRows); ui.availableKeysList->setSelectionMode(QAbstractItemView::ExtendedSelection); availableKeysLayout->addWidget(ui.availableKeysList, /*stretch=*/ 1); centerLayout->addLayout(availableKeysLayout, /*stretch=*/ 1); auto buttonsLayout = new QVBoxLayout(); buttonsLayout->addStretch(1); auto addButton = new QPushButton(); addButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-right"))); addButton->setToolTip(i18n("Add the selected keys to the group")); addButton->setEnabled(false); buttonsLayout->addWidget(addButton); auto removeButton = new QPushButton(); removeButton->setIcon(QIcon::fromTheme(QStringLiteral("arrow-left"))); removeButton->setToolTip(i18n("Remove the selected keys from the group")); removeButton->setEnabled(false); buttonsLayout->addWidget(removeButton); buttonsLayout->addStretch(1); centerLayout->addLayout(buttonsLayout); auto groupKeysLayout = new QVBoxLayout(); groupKeysLayout->addWidget(new QLabel(i18n("Group keys:"))); ui.groupKeysFilter = new QLineEdit(); ui.groupKeysFilter->setClearButtonEnabled(true); ui.groupKeysFilter->setPlaceholderText(i18nc("Placeholder text", "Search...")); groupKeysLayout->addWidget(ui.groupKeysFilter); groupKeysModel = AbstractKeyListModel::createFlatKeyListModel(q); groupKeysFilterModel = new ProxyModel(q); groupKeysFilterModel->setFilterCaseSensitivity(Qt::CaseInsensitive); groupKeysFilterModel->setFilterKeyColumn(KeyList::Summary); groupKeysFilterModel->setSortCaseSensitivity(Qt::CaseInsensitive); groupKeysFilterModel->setSourceModel(groupKeysModel); groupKeysFilterModel->sort(KeyList::Summary, Qt::AscendingOrder); ui.groupKeysList = new QListView(); ui.groupKeysList->setModel(groupKeysFilterModel); ui.groupKeysList->setModelColumn(KeyList::Summary); ui.groupKeysList->setSelectionBehavior(QAbstractItemView::SelectRows); ui.groupKeysList->setSelectionMode(QAbstractItemView::ExtendedSelection); groupKeysLayout->addWidget(ui.groupKeysList, /*stretch=*/ 1); centerLayout->addLayout(groupKeysLayout, /*stretch=*/ 1); mainLayout->addLayout(centerLayout); mainLayout->addWidget(new KSeparator(Qt::Horizontal)); ui.buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel); - KGuiItem::assign(ui.buttonBox->button(QDialogButtonBox::Ok), KStandardGuiItem::ok()); + QPushButton *okButton = ui.buttonBox->button(QDialogButtonBox::Ok); + KGuiItem::assign(okButton, KStandardGuiItem::ok()); KGuiItem::assign(ui.buttonBox->button(QDialogButtonBox::Cancel), KStandardGuiItem::cancel()); + okButton->setEnabled(false); mainLayout->addWidget(ui.buttonBox); + connect(ui.groupNameEdit, &QLineEdit::textChanged, + q, [okButton] (const QString &text) { + okButton->setEnabled(!text.trimmed().isEmpty()); + }); connect(ui.availableKeysFilter, &QLineEdit::textChanged, availableKeysFilterModel, &QSortFilterProxyModel::setFilterFixedString); connect(ui.availableKeysList->selectionModel(), &QItemSelectionModel::selectionChanged, q, [addButton] (const QItemSelection &selected, const QItemSelection &) { addButton->setEnabled(!selected.isEmpty()); }); connect(ui.groupKeysFilter, &QLineEdit::textChanged, groupKeysFilterModel, &QSortFilterProxyModel::setFilterFixedString); connect(ui.groupKeysList->selectionModel(), &QItemSelectionModel::selectionChanged, q, [removeButton] (const QItemSelection &selected, const QItemSelection &) { removeButton->setEnabled(!selected.isEmpty()); }); connect(addButton, &QPushButton::clicked, q, [this] () { addKeysToGroup(); }); connect(removeButton, &QPushButton::clicked, q, [this] () { removeKeysFromGroup(); }); connect(ui.buttonBox, &QDialogButtonBox::accepted, q, &EditGroupDialog::accept); connect(ui.buttonBox, &QDialogButtonBox::rejected, q, &EditGroupDialog::reject); // calculate default size with enough space for the key list const auto fm = q->fontMetrics(); const QSize sizeHint = q->sizeHint(); const QSize defaultSize = QSize(qMax(sizeHint.width(), 150 * fm.horizontalAdvance(QLatin1Char('x'))), sizeHint.height() + 12 * fm.lineSpacing()); restoreLayout(defaultSize); } ~Private() { saveLayout(); } private: void saveLayout() { KConfigGroup configGroup(KSharedConfig::openConfig(), "EditGroupDialog"); configGroup.writeEntry("Size", q->size()); configGroup.sync(); } void restoreLayout(const QSize &defaultSize) { const KConfigGroup configGroup(KSharedConfig::openConfig(), "EditGroupDialog"); const QSize size = configGroup.readEntry("Size", defaultSize); if (size.isValid()) { q->resize(size); } } void addKeysToGroup(); void removeKeysFromGroup(); }; namespace { std::vector getSelectedKeys(const QListView *view) { const QModelIndexList selectedRows = view->selectionModel()->selectedRows(); if (selectedRows.isEmpty()) { return std::vector(); } const KeyListModelInterface *const keyListModel = dynamic_cast(view->model()); Q_ASSERT(keyListModel); return keyListModel->keys(selectedRows); } void setSelectedKeys(const QListView *view, const std::vector &keys) { const KeyListModelInterface *const keyListModel = dynamic_cast(view->model()); Q_ASSERT(keyListModel); const QModelIndexList indexes = keyListModel->indexes(keys); for (const QModelIndex &idx : indexes) { if (idx.isValid()) { view->selectionModel()->select(idx, QItemSelectionModel::Select | QItemSelectionModel::Rows); } } } } void EditGroupDialog::Private::addKeysToGroup() { const std::vector selectedGroupKeys = getSelectedKeys(ui.groupKeysList); const std::vector selectedKeys = getSelectedKeys(ui.availableKeysList); groupKeysModel->addKeys(selectedKeys); setSelectedKeys(ui.groupKeysList, selectedGroupKeys); } void EditGroupDialog::Private::removeKeysFromGroup() { const std::vector selectedKeys = getSelectedKeys(ui.groupKeysList); for (const Key &key : selectedKeys) { groupKeysModel->removeKey(key); } } EditGroupDialog::EditGroupDialog(QWidget *parent) : QDialog(parent) , d(new Private(this)) { setWindowTitle(i18nc("@title:window", "Edit Group")); } EditGroupDialog::~EditGroupDialog() { } void EditGroupDialog::setGroupName(const QString &name) { - d->ui.groupNameLabel->setText(name); + d->ui.groupNameEdit->setText(name); +} + +QString EditGroupDialog::groupName() const +{ + return d->ui.groupNameEdit->text().trimmed(); } void EditGroupDialog::setGroupKeys(const std::vector &keys) { d->groupKeysModel->setKeys(keys); } std::vector EditGroupDialog::groupKeys() const { std::vector keys; keys.reserve(d->groupKeysModel->rowCount()); for (int row = 0; row < d->groupKeysModel->rowCount(); ++row) { const QModelIndex index = d->groupKeysModel->index(row, 0); keys.push_back(d->groupKeysModel->key(index)); } return keys; } #include "editgroupdialog.moc" diff --git a/src/dialogs/editgroupdialog.h b/src/dialogs/editgroupdialog.h index 34f382d77..163e1bd59 100644 --- a/src/dialogs/editgroupdialog.h +++ b/src/dialogs/editgroupdialog.h @@ -1,48 +1,49 @@ /* dialogs/editgroupdialog.h This file is part of Kleopatra, the KDE keymanager SPDX-FileCopyrightText: 2021 g10 Code GmbH SPDX-FileContributor: Ingo Klöcker SPDX-License-Identifier: GPL-2.0-or-later */ #ifndef __KLEOPATRA_DIALOGS_EDITGROUPDIALOG_H__ #define __KLEOPATRA_DIALOGS_EDITGROUPDIALOG_H__ #include #include namespace GpgME { class Key; } namespace Kleo { namespace Dialogs { class EditGroupDialog : public QDialog { Q_OBJECT public: explicit EditGroupDialog(QWidget *parent = nullptr); ~EditGroupDialog() override; void setGroupName(const QString &name); + QString groupName() const; void setGroupKeys(const std::vector &keys); std::vector groupKeys() const; private: class Private; const std::unique_ptr d; }; } } #endif // __KLEOPATRA_DIALOGS_EDITGROUPDIALOG_H__