From a518f4306da9c56009231a004bb5dfdc8fb0963b Mon Sep 17 00:00:00 2001 From: Aetf Date: Sun, 1 Dec 2019 16:49:36 -0500 Subject: [PATCH] FdoSecrets: UI improvements - Use proper model for database and session in settings page - Fix button text (unlock/lock) not changed according to the database locking status - Fix button icons not present on icon themes other than Breeze - Fix the disconnect button may got clipped when new session opens --- COPYING | 5 + .../scalable/actions/object-locked.svg | 14 + .../scalable/actions/object-unlocked.svg | 15 + src/fdosecrets/CMakeLists.txt | 1 + src/fdosecrets/FdoSecretsPlugin.cpp | 9 + src/fdosecrets/FdoSecretsPlugin.h | 7 + src/fdosecrets/widgets/SettingsModels.cpp | 396 ++++++++++++++ src/fdosecrets/widgets/SettingsModels.h | 96 ++++ .../widgets/SettingsWidgetFdoSecrets.cpp | 498 +++++++++--------- .../widgets/SettingsWidgetFdoSecrets.h | 24 +- .../widgets/SettingsWidgetFdoSecrets.ui | 29 +- 11 files changed, 791 insertions(+), 303 deletions(-) create mode 100644 share/icons/application/scalable/actions/object-locked.svg create mode 100644 share/icons/application/scalable/actions/object-unlocked.svg create mode 100644 src/fdosecrets/widgets/SettingsModels.cpp create mode 100644 src/fdosecrets/widgets/SettingsModels.h diff --git a/COPYING b/COPYING index 9bfd33539..fe7d02f3e 100644 --- a/COPYING +++ b/COPYING @@ -248,3 +248,8 @@ Comment: from Freedesktop.org website Files: share/icons/application/32x32/actions/statistics.png Copyright: Icon made by Freepik from https://www.flaticon.com/free-icon/bars-chart_265733 + +Files: share/icons/application/scalable/actions/object-locked.svg + share/icons/application/scalable/actions/object-unlocked.svg +License: LGPL-3 +Comment: from Breeze icon theme (https://github.com/KDE/breeze-icons) diff --git a/share/icons/application/scalable/actions/object-locked.svg b/share/icons/application/scalable/actions/object-locked.svg new file mode 100644 index 000000000..090e038c0 --- /dev/null +++ b/share/icons/application/scalable/actions/object-locked.svg @@ -0,0 +1,14 @@ + + + + + + diff --git a/share/icons/application/scalable/actions/object-unlocked.svg b/share/icons/application/scalable/actions/object-unlocked.svg new file mode 100644 index 000000000..f6c53e581 --- /dev/null +++ b/share/icons/application/scalable/actions/object-unlocked.svg @@ -0,0 +1,15 @@ + + + + + + diff --git a/src/fdosecrets/CMakeLists.txt b/src/fdosecrets/CMakeLists.txt index 9d3fcb6a9..a9750bc2d 100644 --- a/src/fdosecrets/CMakeLists.txt +++ b/src/fdosecrets/CMakeLists.txt @@ -4,6 +4,7 @@ if(WITH_XC_FDOSECRETS) add_library(fdosecrets STATIC # app settings page FdoSecretsPlugin.cpp + widgets/SettingsModels.cpp widgets/SettingsWidgetFdoSecrets.cpp # per database settings page diff --git a/src/fdosecrets/FdoSecretsPlugin.cpp b/src/fdosecrets/FdoSecretsPlugin.cpp index 668b5fb04..646f85301 100644 --- a/src/fdosecrets/FdoSecretsPlugin.cpp +++ b/src/fdosecrets/FdoSecretsPlugin.cpp @@ -60,11 +60,15 @@ void FdoSecretsPlugin::updateServiceState() }); if (!m_secretService->initialize()) { m_secretService.reset(); + FdoSecrets::settings()->setEnabled(false); + return; } + emit secretServiceStarted(); } } else { if (m_secretService) { m_secretService.reset(); + emit secretServiceStopped(); } } } @@ -74,6 +78,11 @@ Service* FdoSecretsPlugin::serviceInstance() const return m_secretService.data(); } +DatabaseTabWidget* FdoSecretsPlugin::dbTabs() const +{ + return m_dbTabs; +} + void FdoSecretsPlugin::emitRequestSwitchToDatabases() { emit requestSwitchToDatabases(); diff --git a/src/fdosecrets/FdoSecretsPlugin.h b/src/fdosecrets/FdoSecretsPlugin.h index 2a57ea0db..828c0bd76 100644 --- a/src/fdosecrets/FdoSecretsPlugin.h +++ b/src/fdosecrets/FdoSecretsPlugin.h @@ -59,6 +59,11 @@ public: */ FdoSecrets::Service* serviceInstance() const; + /** + * @return The db tabs widget, containing opened databases. Can be nullptr. + */ + DatabaseTabWidget* dbTabs() const; + public slots: void emitRequestSwitchToDatabases(); void emitRequestShowNotification(const QString& msg, const QString& title = {}); @@ -67,6 +72,8 @@ signals: void error(const QString& msg); void requestSwitchToDatabases(); void requestShowNotification(const QString& msg, const QString& title, int msTimeoutHint); + void secretServiceStarted(); + void secretServiceStopped(); private: QPointer m_dbTabs; diff --git a/src/fdosecrets/widgets/SettingsModels.cpp b/src/fdosecrets/widgets/SettingsModels.cpp new file mode 100644 index 000000000..edcb275c8 --- /dev/null +++ b/src/fdosecrets/widgets/SettingsModels.cpp @@ -0,0 +1,396 @@ +/* + * Copyright (C) 2019 Aetf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#include "SettingsModels.h" + +#include "fdosecrets/FdoSecretsPlugin.h" +#include "fdosecrets/FdoSecretsSettings.h" +#include "fdosecrets/objects/Service.h" +#include "fdosecrets/objects/Session.h" + +#include "core/Database.h" +#include "core/DatabaseIcons.h" +#include "core/FilePath.h" +#include "gui/DatabaseTabWidget.h" +#include "gui/DatabaseWidget.h" + +#include + +namespace FdoSecrets +{ + + SettingsDatabaseModel::SettingsDatabaseModel(DatabaseTabWidget* dbTabs, QObject* parent) + : QAbstractTableModel(parent) + , m_dbTabs(nullptr) + { + setTabWidget(dbTabs); + } + + void SettingsDatabaseModel::setTabWidget(DatabaseTabWidget* dbTabs) + { + auto old = m_dbTabs; + m_dbTabs = dbTabs; + if (old != m_dbTabs) { + populateModel(); + } + } + + int SettingsDatabaseModel::rowCount(const QModelIndex& parent) const + { + if (parent.isValid()) { + return 0; + } + return m_dbs.size(); + } + + int SettingsDatabaseModel::columnCount(const QModelIndex& parent) const + { + if (parent.isValid()) { + return 0; + } + return 3; + } + + QVariant SettingsDatabaseModel::headerData(int section, Qt::Orientation orientation, int role) const + { + if (orientation != Qt::Horizontal) { + return {}; + } + + if (role != Qt::DisplayRole) { + return {}; + } + + switch (section) { + case 0: + return tr("File Name"); + case 1: + return tr("Group"); + case 2: + return tr("Manage"); + default: + return {}; + } + } + + QVariant SettingsDatabaseModel::data(const QModelIndex& index, int role) const + { + if (!index.isValid()) { + return {}; + } + const auto& dbWidget = m_dbs[index.row()]; + if (!dbWidget) { + return {}; + } + + switch (index.column()) { + case 0: + return dataForName(dbWidget, role); + case 1: + return dataForExposedGroup(dbWidget, role); + case 2: + return dataForManage(dbWidget, role); + default: + return {}; + } + } + + QVariant SettingsDatabaseModel::dataForName(DatabaseWidget* db, int role) const + { + switch (role) { + case Qt::DisplayRole: { + QFileInfo fi(db->database()->filePath()); + return fi.fileName(); + } + case Qt::ToolTipRole: + return db->database()->filePath(); + default: + return {}; + } + } + + QVariant SettingsDatabaseModel::dataForExposedGroup(DatabaseWidget* dbWidget, int role) + { + if (dbWidget->isLocked()) { + switch (role) { + case Qt::DisplayRole: + return tr("Unlock to show"); + case Qt::DecorationRole: + return filePath()->icon(QStringLiteral("apps"), QStringLiteral("object-locked"), true); + case Qt::FontRole: { + QFont font; + font.setItalic(true); + return font; + } + default: + return {}; + } + } + auto db = dbWidget->database(); + auto group = db->rootGroup()->findGroupByUuid(FdoSecrets::settings()->exposedGroup(db)); + if (group) { + switch (role) { + case Qt::DisplayRole: + return group->name(); + case Qt::DecorationRole: + return group->isExpired() ? databaseIcons()->iconPixmap(DatabaseIcons::ExpiredIconIndex) + : group->iconScaledPixmap(); + case Qt::FontRole: + if (group->isExpired()) { + QFont font; + font.setStrikeOut(true); + return font; + } else { + return {}; + } + default: + return {}; + } + } else { + switch (role) { + case Qt::DisplayRole: + return tr("None"); + case Qt::DecorationRole: + return filePath()->icon(QStringLiteral("apps"), QStringLiteral("paint-none"), true); + default: + return {}; + } + } + } + + QVariant SettingsDatabaseModel::dataForManage(DatabaseWidget* db, int role) const + { + switch (role) { + case Qt::EditRole: + return QVariant::fromValue(db); + default: + return {}; + } + } + + void SettingsDatabaseModel::populateModel() + { + beginResetModel(); + + m_dbs.clear(); + + if (m_dbTabs) { + // Add existing database tabs + for (int idx = 0; idx != m_dbTabs->count(); ++idx) { + auto dbWidget = m_dbTabs->databaseWidgetFromIndex(idx); + databaseAdded(dbWidget, false); + } + // connect signals + connect(m_dbTabs, &DatabaseTabWidget::databaseOpened, this, [this](DatabaseWidget* db) { + databaseAdded(db, true); + }); + connect(m_dbTabs, &DatabaseTabWidget::databaseClosed, this, &SettingsDatabaseModel::databaseRemoved); + } + + endResetModel(); + } + + void SettingsDatabaseModel::databaseAdded(DatabaseWidget* db, bool emitSignals) + { + int row = m_dbs.size(); + if (emitSignals) { + beginInsertRows({}, row, row); + } + + m_dbs.append(db); + connect(db, &DatabaseWidget::databaseLocked, this, [row, this]() { + emit dataChanged(index(row, 1), index(row, 2)); + }); + connect(db, &DatabaseWidget::databaseUnlocked, this, [row, this]() { + emit dataChanged(index(row, 1), index(row, 2)); + }); + connect(db, &DatabaseWidget::databaseModified, this, [row, this]() { + emit dataChanged(index(row, 0), index(row, 2)); + }); + connect(db, &DatabaseWidget::databaseFilePathChanged, this, [row, this]() { + emit dataChanged(index(row, 0), index(row, 2)); + }); + + if (emitSignals) { + endInsertRows(); + } + } + + void SettingsDatabaseModel::databaseRemoved(const QString& filePath) + { + for (int i = 0; i != m_dbs.size(); i++) { + if (m_dbs[i] && m_dbs[i]->database()->filePath() == filePath) { + beginRemoveRows({}, i, i); + + m_dbs[i]->disconnect(this); + m_dbs.removeAt(i); + + endRemoveRows(); + break; + } + } + } + + SettingsSessionModel::SettingsSessionModel(FdoSecretsPlugin* plugin, QObject* parent) + : QAbstractTableModel(parent) + , m_service(nullptr) + { + setService(plugin->serviceInstance()); + connect(plugin, &FdoSecretsPlugin::secretServiceStarted, this, [plugin, this]() { + setService(plugin->serviceInstance()); + }); + connect(plugin, &FdoSecretsPlugin::secretServiceStopped, this, [this]() { setService(nullptr); }); + } + + void SettingsSessionModel::setService(Service* service) + { + auto old = m_service; + m_service = service; + if (old != m_service) { + populateModel(); + } + } + + int SettingsSessionModel::rowCount(const QModelIndex& parent) const + { + if (parent.isValid()) { + return 0; + } + return m_sessions.size(); + } + + int SettingsSessionModel::columnCount(const QModelIndex& parent) const + { + if (parent.isValid()) { + return 0; + } + return 2; + } + + QVariant SettingsSessionModel::headerData(int section, Qt::Orientation orientation, int role) const + { + if (orientation != Qt::Horizontal) { + return {}; + } + + if (role != Qt::DisplayRole) { + return {}; + } + + switch (section) { + case 0: + return tr("Application"); + case 1: + return tr("Manage"); + default: + return {}; + } + } + + QVariant SettingsSessionModel::data(const QModelIndex& index, int role) const + { + if (!index.isValid()) { + return {}; + } + const auto& sess = m_sessions[index.row()]; + if (!sess) { + return {}; + } + + switch (index.column()) { + case 0: + return dataForApplication(sess, role); + case 1: + return dataForManage(sess, role); + default: + return {}; + } + } + + QVariant SettingsSessionModel::dataForApplication(Session* sess, int role) const + { + switch (role) { + case Qt::DisplayRole: + return sess->peer(); + default: + return {}; + } + } + + QVariant SettingsSessionModel::dataForManage(Session* sess, int role) const + { + switch (role) { + case Qt::EditRole: { + auto v = QVariant::fromValue(sess); + qDebug() << v << v.type() << v.userType(); + return v; + } + default: + return {}; + } + } + + void SettingsSessionModel::populateModel() + { + beginResetModel(); + + m_sessions.clear(); + + if (m_service) { + // Add existing database tabs + for (const auto& sess : m_service->sessions()) { + sessionAdded(sess, false); + } + + // connect signals + connect(m_service, &Service::sessionOpened, this, [this](Session* sess) { sessionAdded(sess, true); }); + connect(m_service, &Service::sessionClosed, this, &SettingsSessionModel::sessionRemoved); + } + + endResetModel(); + } + + void SettingsSessionModel::sessionAdded(Session* sess, bool emitSignals) + { + int row = m_sessions.size(); + if (emitSignals) { + beginInsertRows({}, row, row); + } + + m_sessions.append(sess); + + if (emitSignals) { + endInsertRows(); + } + } + + void SettingsSessionModel::sessionRemoved(Session* sess) + { + for (int i = 0; i != m_sessions.size(); i++) { + if (m_sessions[i] == sess) { + beginRemoveRows({}, i, i); + + m_sessions[i]->disconnect(this); + m_sessions.removeAt(i); + + endRemoveRows(); + break; + } + } + } + +} // namespace FdoSecrets diff --git a/src/fdosecrets/widgets/SettingsModels.h b/src/fdosecrets/widgets/SettingsModels.h new file mode 100644 index 000000000..b07bb1637 --- /dev/null +++ b/src/fdosecrets/widgets/SettingsModels.h @@ -0,0 +1,96 @@ +/* + * Copyright (C) 2019 Aetf + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +#ifndef KEEPASSXC_FDOSECRETS_SETTINGSMODELS_H +#define KEEPASSXC_FDOSECRETS_SETTINGSMODELS_H + +#include +#include + +class DatabaseTabWidget; +class DatabaseWidget; +class FdoSecretsPlugin; + +namespace FdoSecrets +{ + class SettingsDatabaseModel : public QAbstractTableModel + { + Q_OBJECT + public: + explicit SettingsDatabaseModel(DatabaseTabWidget* dbTabs, QObject* parent = nullptr); + + void setTabWidget(DatabaseTabWidget* dbTabs); + + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; + QVariant data(const QModelIndex& index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + private: + QVariant dataForName(DatabaseWidget* db, int role) const; + static QVariant dataForExposedGroup(DatabaseWidget* db, int role); + QVariant dataForManage(DatabaseWidget* db, int role) const; + + private slots: + void populateModel(); + void databaseAdded(DatabaseWidget* db, bool emitSignals); + void databaseRemoved(const QString& filePath); + + private: + // source + QPointer m_dbTabs; + + // internal store + QList> m_dbs; + }; + + class Service; + class Session; + + class SettingsSessionModel : public QAbstractTableModel + { + Q_OBJECT + public: + explicit SettingsSessionModel(FdoSecretsPlugin* plugin, QObject* parent = nullptr); + + int rowCount(const QModelIndex& parent) const override; + int columnCount(const QModelIndex& parent) const override; + QVariant data(const QModelIndex& index, int role) const override; + QVariant headerData(int section, Qt::Orientation orientation, int role) const override; + + private: + void setService(Service* service); + + QVariant dataForApplication(Session* sess, int role) const; + QVariant dataForManage(Session* sess, int role) const; + + private slots: + void populateModel(); + void sessionAdded(Session* sess, bool emitSignals); + void sessionRemoved(Session* sess); + + private: + // source + QPointer m_service; + + // internal copy, so we can emit with changed index + QList m_sessions; + }; + +} // namespace FdoSecrets + +#endif // KEEPASSXC_FDOSECRETS_SETTINGSMODELS_H diff --git a/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.cpp b/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.cpp index 920b603d9..59399cdec 100644 --- a/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.cpp +++ b/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.cpp @@ -20,241 +20,273 @@ #include "fdosecrets/FdoSecretsPlugin.h" #include "fdosecrets/FdoSecretsSettings.h" -#include "fdosecrets/objects/Collection.h" -#include "fdosecrets/objects/Prompt.h" #include "fdosecrets/objects/Session.h" +#include "fdosecrets/widgets/SettingsModels.h" -#include "core/DatabaseIcons.h" #include "core/FilePath.h" #include "gui/DatabaseWidget.h" #include -#include -#include #include -#include -#include +#include +#include #include #include -using FdoSecrets::Collection; -using FdoSecrets::Service; using FdoSecrets::Session; +using FdoSecrets::SettingsDatabaseModel; +using FdoSecrets::SettingsSessionModel; + +namespace +{ + class ManageDatabase : public QToolBar + { + Q_OBJECT + + Q_PROPERTY(DatabaseWidget* dbWidget READ dbWidget WRITE setDbWidget USER true) + + public: + explicit ManageDatabase(FdoSecretsPlugin* plugin, QWidget* parent = nullptr) + : QToolBar(parent) + , m_plugin(plugin) + { + setFloatable(false); + setMovable(false); + + // use a dummy widget to center the buttons + auto spacer = new QWidget(this); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + spacer->setVisible(true); + addWidget(spacer); + + // db settings + m_dbSettingsAct = new QAction(tr("Database settings"), this); + m_dbSettingsAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("document-edit"))); + m_dbSettingsAct->setToolTip(tr("Edit database settings")); + m_dbSettingsAct->setEnabled(false); + connect(m_dbSettingsAct, &QAction::triggered, this, [this]() { + if (!m_dbWidget) { + return; + } + auto db = m_dbWidget; + m_plugin->serviceInstance()->doSwitchToChangeDatabaseSettings(m_dbWidget); + }); + addAction(m_dbSettingsAct); + + // unlock/lock + m_lockAct = new QAction(tr("Unlock database"), this); + m_lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-locked"), false)); + m_lockAct->setToolTip(tr("Unlock database to show more information")); + connect(m_lockAct, &QAction::triggered, this, [this]() { + if (!m_dbWidget) { + return; + } + if (m_dbWidget->isLocked()) { + m_plugin->serviceInstance()->doUnlockDatabaseInDialog(m_dbWidget); + } else { + m_dbWidget->lock(); + } + }); + + addAction(m_lockAct); + + // use a dummy widget to center the buttons + spacer = new QWidget(this); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + spacer->setVisible(true); + addWidget(spacer); + } + + DatabaseWidget* dbWidget() const + { + return m_dbWidget; + } + + void setDbWidget(DatabaseWidget* dbWidget) + { + if (m_dbWidget == dbWidget) { + return; + } + + if (m_dbWidget) { + disconnect(); + } + + m_dbWidget = dbWidget; + + reconnect(); + } + + private: + void disconnect() + { + if (!m_dbWidget) { + return; + } + m_dbWidget->disconnect(this); + } + + void reconnect() + { + if (!m_dbWidget) { + return; + } + connect(m_dbWidget, &DatabaseWidget::databaseLocked, this, [this]() { + m_lockAct->setText(tr("Unlock database")); + m_lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-locked"), false)); + m_lockAct->setToolTip(tr("Unlock database to show more information")); + m_dbSettingsAct->setEnabled(false); + }); + connect(m_dbWidget, &DatabaseWidget::databaseUnlocked, this, [this]() { + m_lockAct->setText(tr("Lock database")); + m_lockAct->setIcon( + filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-unlocked"), false)); + m_lockAct->setToolTip(tr("Lock database")); + m_dbSettingsAct->setEnabled(true); + }); + } + + private: + FdoSecretsPlugin* m_plugin = nullptr; + QPointer m_dbWidget = nullptr; + QAction* m_dbSettingsAct = nullptr; + QAction* m_lockAct = nullptr; + }; + + class ManageSession : public QToolBar + { + Q_OBJECT + + Q_PROPERTY(Session* session READ session WRITE setSession USER true) + + public: + explicit ManageSession(FdoSecretsPlugin*, QWidget* parent = nullptr) + : QToolBar(parent) + { + setFloatable(false); + setMovable(false); + + // use a dummy widget to center the buttons + auto spacer = new QWidget(this); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + spacer->setVisible(true); + addWidget(spacer); + + m_disconnectAct = new QAction(tr("Disconnect"), this); + m_disconnectAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("dialog-close"))); + m_disconnectAct->setToolTip(tr("Disconnect this application")); + connect(m_disconnectAct, &QAction::triggered, this, [this]() { + if (m_session) { + m_session->close(); + } + }); + addAction(m_disconnectAct); + + // use a dummy widget to center the buttons + spacer = new QWidget(this); + spacer->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred); + spacer->setVisible(true); + addWidget(spacer); + } + + Session* session() + { + return m_session; + } + + void setSession(Session* sess) + { + m_session = sess; + } + + private: + Session* m_session = nullptr; + QAction* m_disconnectAct = nullptr; + }; + + template class Creator : public QItemEditorCreatorBase + { + public: + inline explicit Creator(FdoSecretsPlugin* plugin) + : QItemEditorCreatorBase() + , m_plugin(plugin) + , m_propertyName(T::staticMetaObject.userProperty().name()) + { + } + + inline QWidget* createWidget(QWidget* parent) const override + { + return new T(m_plugin, parent); + } + + inline QByteArray valuePropertyName() const override + { + return m_propertyName; + } + + private: + FdoSecretsPlugin* m_plugin; + QByteArray m_propertyName; + }; +} // namespace SettingsWidgetFdoSecrets::SettingsWidgetFdoSecrets(FdoSecretsPlugin* plugin, QWidget* parent) : QWidget(parent) , m_ui(new Ui::SettingsWidgetFdoSecrets()) + , m_factory(new QItemEditorFactory) , m_plugin(plugin) { m_ui->setupUi(this); - auto sessHeader = m_ui->tableSessions->horizontalHeader(); - sessHeader->setSelectionMode(QAbstractItemView::NoSelection); - sessHeader->setSectionsClickable(false); - sessHeader->setSectionResizeMode(0, QHeaderView::Stretch); // application - sessHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents); // disconnect button + auto sessModel = new SettingsSessionModel(plugin, this); + m_ui->tableSessions->setModel(sessModel); + setupView(m_ui->tableSessions, 1, qMetaTypeId(), new Creator(m_plugin)); - auto dbHeader = m_ui->tableDatabases->horizontalHeader(); - dbHeader->setSelectionMode(QAbstractItemView::NoSelection); - dbHeader->setSectionsClickable(false); - dbHeader->setSectionResizeMode(0, QHeaderView::Stretch); // file name - dbHeader->setSectionResizeMode(1, QHeaderView::Stretch); // group - dbHeader->setSectionResizeMode(2, QHeaderView::ResizeToContents); // manage button + // config header after setting model, otherwise the header doesn't have enough sections + auto sessViewHeader = m_ui->tableSessions->horizontalHeader(); + sessViewHeader->setSelectionMode(QAbstractItemView::NoSelection); + sessViewHeader->setSectionsClickable(false); + sessViewHeader->setSectionResizeMode(0, QHeaderView::Stretch); // application + sessViewHeader->setSectionResizeMode(1, QHeaderView::ResizeToContents); // disconnect button + + auto dbModel = new SettingsDatabaseModel(plugin->dbTabs(), this); + m_ui->tableDatabases->setModel(dbModel); + setupView(m_ui->tableDatabases, 2, qMetaTypeId(), new Creator(m_plugin)); + + // config header after setting model, otherwise the header doesn't have enough sections + auto dbViewHeader = m_ui->tableDatabases->horizontalHeader(); + dbViewHeader->setSelectionMode(QAbstractItemView::NoSelection); + dbViewHeader->setSectionsClickable(false); + dbViewHeader->setSectionResizeMode(0, QHeaderView::Stretch); // file name + dbViewHeader->setSectionResizeMode(1, QHeaderView::Stretch); // group + dbViewHeader->setSectionResizeMode(2, QHeaderView::ResizeToContents); // manage button m_ui->tabWidget->setEnabled(m_ui->enableFdoSecretService->isChecked()); connect(m_ui->enableFdoSecretService, &QCheckBox::toggled, m_ui->tabWidget, &QTabWidget::setEnabled); } +void SettingsWidgetFdoSecrets::setupView(QAbstractItemView* view, + int manageColumn, + int editorTypeId, + QItemEditorCreatorBase* creator) +{ + auto manageButtonDelegate = new QStyledItemDelegate(this); + m_factory->registerEditor(editorTypeId, creator); + manageButtonDelegate->setItemEditorFactory(m_factory.data()); + view->setItemDelegateForColumn(manageColumn, manageButtonDelegate); + connect(view->model(), + &QAbstractItemModel::rowsInserted, + this, + [view, manageColumn](const QModelIndex&, int first, int last) { + for (int i = first; i <= last; ++i) { + auto idx = view->model()->index(i, manageColumn); + view->openPersistentEditor(idx); + } + }); +} + SettingsWidgetFdoSecrets::~SettingsWidgetFdoSecrets() = default; -void SettingsWidgetFdoSecrets::populateSessions(bool enabled) -{ - m_ui->tableSessions->setRowCount(0); - - auto service = m_plugin->serviceInstance(); - if (!service || !enabled) { - return; - } - - for (const auto& sess : service->sessions()) { - addSessionRow(sess); - } -} - -void SettingsWidgetFdoSecrets::addSessionRow(Session* sess) -{ - auto row = m_ui->tableSessions->rowCount(); - m_ui->tableSessions->insertRow(row); - - // column 0: application name - auto item = new QTableWidgetItem(sess->peer()); - item->setData(Qt::UserRole, QVariant::fromValue(sess)); - m_ui->tableSessions->setItem(row, 0, item); - - // column 1: disconnect button - auto btn = new QPushButton(tr("Disconnect")); - connect(btn, &QPushButton::clicked, sess, &Session::close); - m_ui->tableSessions->setCellWidget(row, 1, btn); - - // column 2: hidden uuid - m_ui->tableSessions->setItem(row, 2, new QTableWidgetItem(sess->id())); -} - -void SettingsWidgetFdoSecrets::removeSessionRow(Session* sess) -{ - int row = 0; - while (row != m_ui->tableSessions->rowCount()) { - auto item = m_ui->tableSessions->item(row, 0); - const auto itemSess = item->data(Qt::UserRole).value(); - if (itemSess == sess) { - break; - } - ++row; - } - if (row == m_ui->tableSessions->rowCount()) { - qWarning() << "Unknown Fdo Secret Service session" << sess->id() << "while removing collection from table"; - return; - } - - m_ui->tableSessions->removeRow(row); -} - -void SettingsWidgetFdoSecrets::populateDatabases(bool enabled) -{ - m_ui->tableDatabases->setRowCount(0); - - auto service = m_plugin->serviceInstance(); - if (!service || !enabled) { - return; - } - - auto ret = service->collections(); - if (ret.isError()) { - return; - } - for (const auto& coll : ret.value()) { - addDatabaseRow(coll); - } -} - -void SettingsWidgetFdoSecrets::addDatabaseRow(Collection* coll) -{ - auto row = m_ui->tableDatabases->rowCount(); - m_ui->tableDatabases->insertRow(row); - - // column 0: File name - QFileInfo fi(coll->backend()->database()->filePath()); - auto item = new QTableWidgetItem(fi.fileName()); - item->setData(Qt::UserRole, QVariant::fromValue(coll)); - m_ui->tableDatabases->setItem(row, 0, item); - - // column 2: manage button: hboxlayout: unlock/lock settings - // create this first so we have a widget to bind connection to, - // which can then be auto deleted when the row is deleted. - auto widget = createManageButtons(coll); - m_ui->tableDatabases->setCellWidget(row, 2, widget); - - // column 1: Group name - auto itemGroupName = new QTableWidgetItem(); - updateExposedGroupItem(itemGroupName, coll); - - connect(coll, &Collection::collectionLockChanged, widget, [this, itemGroupName, coll](bool) { - updateExposedGroupItem(itemGroupName, coll); - }); - - m_ui->tableDatabases->setItem(row, 1, itemGroupName); -} - -QWidget* SettingsWidgetFdoSecrets::createManageButtons(Collection* coll) -{ - auto toolbar = new QToolBar; - toolbar->setFloatable(false); - toolbar->setMovable(false); - - // db settings - auto dbSettingsAct = new QAction(tr("Database settings"), toolbar); - dbSettingsAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("document-edit"))); - dbSettingsAct->setToolTip(tr("Edit database settings")); - dbSettingsAct->setEnabled(!coll->locked().value()); - connect(dbSettingsAct, &QAction::triggered, this, [this, coll]() { - auto db = coll->backend(); - m_plugin->serviceInstance()->doSwitchToChangeDatabaseSettings(db); - }); - toolbar->addAction(dbSettingsAct); - - // unlock/lock - auto lockAct = new QAction(tr("Unlock database"), toolbar); - lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-locked"), true)); - lockAct->setToolTip(tr("Unlock database to show more information")); - connect(coll, &Collection::collectionLockChanged, lockAct, [lockAct, dbSettingsAct](bool locked) { - if (locked) { - lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-locked"), true)); - lockAct->setToolTip(tr("Unlock database to show more information")); - } else { - lockAct->setIcon(filePath()->icon(QStringLiteral("actions"), QStringLiteral("object-unlocked"), true)); - lockAct->setToolTip(tr("Lock database")); - } - dbSettingsAct->setEnabled(!locked); - }); - connect(lockAct, &QAction::triggered, this, [coll]() { - if (coll->locked().value()) { - coll->doUnlock(); - } else { - coll->doLock(); - } - }); - toolbar->addAction(lockAct); - - return toolbar; -} - -void SettingsWidgetFdoSecrets::updateExposedGroupItem(QTableWidgetItem* item, Collection* coll) -{ - if (coll->locked().value()) { - item->setText(tr("Unlock to show")); - item->setIcon(filePath()->icon(QStringLiteral("apps"), QStringLiteral("object-locked"), true)); - QFont font; - font.setItalic(true); - item->setFont(font); - return; - } - - auto db = coll->backend()->database(); - auto group = db->rootGroup()->findGroupByUuid(FdoSecrets::settings()->exposedGroup(db)); - if (group) { - item->setText(group->name()); - item->setIcon(group->isExpired() ? databaseIcons()->iconPixmap(DatabaseIcons::ExpiredIconIndex) - : group->iconScaledPixmap()); - if (group->isExpired()) { - QFont font; - font.setStrikeOut(true); - item->setFont(font); - } - } else { - item->setText(tr("None")); - item->setIcon(filePath()->icon(QStringLiteral("apps"), QStringLiteral("paint-none"), true)); - } -} - -void SettingsWidgetFdoSecrets::removeDatabaseRow(Collection* coll) -{ - int row = 0; - while (row != m_ui->tableDatabases->rowCount()) { - auto item = m_ui->tableDatabases->item(row, 0); - const auto itemColl = item->data(Qt::UserRole).value(); - if (itemColl == coll) { - break; - } - ++row; - } - if (row == m_ui->tableDatabases->rowCount()) { - qWarning() << "Unknown Fdo Secret Service collection" << coll->name() << "while removing collection from table"; - return; - } - - m_ui->tableDatabases->removeRow(row); -} - void SettingsWidgetFdoSecrets::loadSettings() { m_ui->enableFdoSecretService->setChecked(FdoSecrets::settings()->isEnabled()); @@ -269,52 +301,4 @@ void SettingsWidgetFdoSecrets::saveSettings() FdoSecrets::settings()->setNoConfirmDeleteItem(m_ui->noConfirmDeleteItem->isChecked()); } -void SettingsWidgetFdoSecrets::showEvent(QShowEvent* event) -{ - QWidget::showEvent(event); - - QMetaObject::invokeMethod(this, "updateTables", Qt::QueuedConnection, Q_ARG(bool, true)); -} - -void SettingsWidgetFdoSecrets::hideEvent(QHideEvent* event) -{ - QWidget::hideEvent(event); - - QMetaObject::invokeMethod(this, "updateTables", Qt::QueuedConnection, Q_ARG(bool, false)); -} - -void SettingsWidgetFdoSecrets::updateTables(bool enabled) -{ - if (enabled) { - // update the table - populateDatabases(m_ui->enableFdoSecretService->isChecked()); - populateSessions(m_ui->enableFdoSecretService->isChecked()); - - // re-layout the widget to adjust the table cell size - adjustSize(); - - connect(m_ui->enableFdoSecretService, &QCheckBox::toggled, this, &SettingsWidgetFdoSecrets::populateSessions); - connect(m_ui->enableFdoSecretService, &QCheckBox::toggled, this, &SettingsWidgetFdoSecrets::populateDatabases); - - auto service = m_plugin->serviceInstance(); - if (service) { - connect(service, &Service::sessionOpened, this, &SettingsWidgetFdoSecrets::addSessionRow); - connect(service, &Service::sessionClosed, this, &SettingsWidgetFdoSecrets::removeSessionRow); - connect(service, &Service::collectionCreated, this, &SettingsWidgetFdoSecrets::addDatabaseRow); - connect(service, &Service::collectionDeleted, this, &SettingsWidgetFdoSecrets::removeDatabaseRow); - } - } else { - disconnect( - m_ui->enableFdoSecretService, &QCheckBox::toggled, this, &SettingsWidgetFdoSecrets::populateSessions); - disconnect( - m_ui->enableFdoSecretService, &QCheckBox::toggled, this, &SettingsWidgetFdoSecrets::populateDatabases); - - auto service = m_plugin->serviceInstance(); - if (service) { - disconnect(service, &Service::sessionOpened, this, &SettingsWidgetFdoSecrets::addSessionRow); - disconnect(service, &Service::sessionClosed, this, &SettingsWidgetFdoSecrets::removeSessionRow); - disconnect(service, &Service::collectionCreated, this, &SettingsWidgetFdoSecrets::addDatabaseRow); - disconnect(service, &Service::collectionDeleted, this, &SettingsWidgetFdoSecrets::removeDatabaseRow); - } - } -} +#include "SettingsWidgetFdoSecrets.moc" diff --git a/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.h b/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.h index eac1f1e3c..2bf58f826 100644 --- a/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.h +++ b/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.h @@ -21,7 +21,9 @@ #include #include -class QTableWidgetItem; +class QAbstractItemView; +class QItemEditorCreatorBase; +class QItemEditorFactory; namespace FdoSecrets { @@ -48,28 +50,12 @@ public slots: void loadSettings(); void saveSettings(); -private slots: - void populateSessions(bool enabled); - void populateDatabases(bool enabled); - void addSessionRow(FdoSecrets::Session* sess); - void removeSessionRow(FdoSecrets::Session* sess); - void addDatabaseRow(FdoSecrets::Collection* coll); - void removeDatabaseRow(FdoSecrets::Collection* coll); - - void updateTables(bool enabled); - -protected: - void showEvent(QShowEvent* event) override; - - void hideEvent(QHideEvent* event) override; - private: - QWidget* createManageButtons(FdoSecrets::Collection* coll); - - void updateExposedGroupItem(QTableWidgetItem* item, FdoSecrets::Collection* coll); + void setupView(QAbstractItemView* view, int manageColumn, int editorTypeId, QItemEditorCreatorBase* creator); private: QScopedPointer m_ui; + QScopedPointer m_factory; FdoSecretsPlugin* m_plugin; }; diff --git a/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.ui b/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.ui index b77e086c9..660181f5d 100644 --- a/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.ui +++ b/src/fdosecrets/widgets/SettingsWidgetFdoSecrets.ui @@ -75,7 +75,7 @@ - + Qt::NoFocus @@ -91,21 +91,6 @@ false - - - File Name - - - - - Group - - - - - Manage - - @@ -123,7 +108,7 @@ - + Qt::NoFocus @@ -139,16 +124,6 @@ false - - - Application - - - - - Manage - -