diff options
author | saturneric <[email protected]> | 2025-04-12 14:00:42 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2025-04-12 14:00:42 +0000 |
commit | 1f393a50c60d94a4ae23875b31e0e07ad3d64375 (patch) | |
tree | e4e4597dfca48a38c73779fe577e22e3550ee993 | |
parent | fix: avoid accessing invalid pointers of structs related to gpgme_key (diff) | |
download | GpgFrontend-1f393a50c60d94a4ae23875b31e0e07ad3d64375.tar.gz GpgFrontend-1f393a50c60d94a4ae23875b31e0e07ad3d64375.zip |
fix: issues on adsk operations
-rw-r--r-- | src/core/model/GpgAbstractKey.h | 1 | ||||
-rw-r--r-- | src/core/model/GpgKey.h | 2 | ||||
-rw-r--r-- | src/core/model/GpgKeyTableModel.cpp | 59 | ||||
-rw-r--r-- | src/core/model/GpgKeyTableModel.h | 44 | ||||
-rw-r--r-- | src/core/model/GpgKeyTreeModel.cpp | 23 | ||||
-rw-r--r-- | src/core/model/GpgKeyTreeModel.h | 37 | ||||
-rw-r--r-- | src/core/model/GpgSubKey.h | 2 | ||||
-rw-r--r-- | src/ui/dialog/ADSKsPicker.cpp | 11 | ||||
-rw-r--r-- | src/ui/dialog/ADSKsPicker.h | 5 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp | 9 | ||||
-rw-r--r-- | src/ui/model/GpgKeyTreeProxyModel.cpp | 128 | ||||
-rw-r--r-- | src/ui/model/GpgKeyTreeProxyModel.h | 85 | ||||
-rw-r--r-- | src/ui/widgets/KeyTreeView.cpp | 22 | ||||
-rw-r--r-- | src/ui/widgets/KeyTreeView.h | 5 |
14 files changed, 377 insertions, 56 deletions
diff --git a/src/core/model/GpgAbstractKey.h b/src/core/model/GpgAbstractKey.h index 97171daa..854297bc 100644 --- a/src/core/model/GpgAbstractKey.h +++ b/src/core/model/GpgAbstractKey.h @@ -36,6 +36,7 @@ class GpgAbstractKey { [[nodiscard]] virtual auto Fingerprint() const -> QString = 0; [[nodiscard]] virtual auto IsSubKey() const -> bool = 0; + [[nodiscard]] virtual auto IsPrivateKey() const -> bool = 0; [[nodiscard]] virtual auto IsHasEncrCap() const -> bool = 0; [[nodiscard]] virtual auto IsHasSignCap() const -> bool = 0; [[nodiscard]] virtual auto IsHasCertCap() const -> bool = 0; diff --git a/src/core/model/GpgKey.h b/src/core/model/GpgKey.h index 3457b105..da92de8d 100644 --- a/src/core/model/GpgKey.h +++ b/src/core/model/GpgKey.h @@ -276,7 +276,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKey : public GpgAbstractKey { * @return true * @return false */ - [[nodiscard]] auto IsPrivateKey() const -> bool; + [[nodiscard]] auto IsPrivateKey() const -> bool override; /** * @brief diff --git a/src/core/model/GpgKeyTableModel.cpp b/src/core/model/GpgKeyTableModel.cpp index 1e38cb47..2d01aee8 100644 --- a/src/core/model/GpgKeyTableModel.cpp +++ b/src/core/model/GpgKeyTableModel.cpp @@ -28,7 +28,6 @@ #include "GpgKeyTableModel.h" -#include "core/function/gpg/GpgKeyGetter.h" #include "core/model/GpgKey.h" #include "core/utils/GpgUtils.h" @@ -37,19 +36,24 @@ namespace GpgFrontend { GpgKeyTableModel::GpgKeyTableModel(int channel, GpgKeyList keys, QObject *parent) : QAbstractTableModel(parent), - buffered_keys_(std::move(keys)), column_headers_({tr("Select"), tr("Type"), tr("Name"), tr("Email Address"), tr("Usage"), tr("Trust"), tr("Key ID"), tr("Create Date"), tr("Algorithm"), tr("Subkey(s)"), tr("Comment")}), - gpg_context_channel_(channel), - key_check_state_(buffered_keys_.size()) { - LOG_D() << "init gpg key table module at channel: " << gpg_context_channel_ - << "key list size: " << buffered_keys_.size(); + gpg_context_channel_(channel) { + for (const auto &key : keys) { + cached_items_.push_back(GpgKeyTableItem(key)); + } +} + +auto GpgKeyTableModel::index(int row, int column, + const QModelIndex &parent) const -> QModelIndex { + if (!hasIndex(row, column, parent) || parent.isValid()) return {}; + return createIndex(row, column, &cached_items_[row]); } auto GpgKeyTableModel::rowCount(const QModelIndex & /*parent*/) const -> int { - return static_cast<int>(buffered_keys_.size()); + return static_cast<int>(cached_items_.size()); } auto GpgKeyTableModel::columnCount(const QModelIndex & /*parent*/) const @@ -59,16 +63,21 @@ auto GpgKeyTableModel::columnCount(const QModelIndex & /*parent*/) const auto GpgKeyTableModel::data(const QModelIndex &index, int role) const -> QVariant { - if (!index.isValid() || buffered_keys_.empty()) return {}; + if (!index.isValid() || cached_items_.empty()) return {}; + + auto *i = index.isValid() + ? static_cast<GpgKeyTableItem *>(index.internalPointer()) + : nullptr; + if (i == nullptr) return {}; if (role == Qt::CheckStateRole) { if (index.column() == 0) { - return key_check_state_[index.row()] ? Qt::Checked : Qt::Unchecked; + return i->Checked() ? Qt::Checked : Qt::Unchecked; } } if (role == Qt::DisplayRole) { - const auto &key = buffered_keys_.at(index.row()); + const auto key = i->Key(); switch (index.column()) { case 0: { @@ -155,8 +164,13 @@ auto GpgKeyTableModel::setData(const QModelIndex &index, const QVariant &value, int role) -> bool { if (!index.isValid()) return false; + auto *i = index.isValid() + ? static_cast<GpgKeyTableItem *>(index.internalPointer()) + : nullptr; + if (i == nullptr) return false; + if (index.column() == 0 && role == Qt::CheckStateRole) { - key_check_state_[index.row()] = (value == Qt::Checked); + i->SetChecked(value == Qt::Checked); emit dataChanged(index, index); return true; } @@ -166,24 +180,35 @@ auto GpgKeyTableModel::setData(const QModelIndex &index, const QVariant &value, auto GpgKeyTableModel::GetAllKeyIds() -> KeyIdArgsList { KeyIdArgsList keys; - for (auto &key : buffered_keys_) { - keys.push_back(key.ID()); + for (auto &i : cached_items_) { + keys.push_back(i.Key().ID()); } return keys; } auto GpgKeyTableModel::GetKeyIDByRow(int row) const -> QString { - if (buffered_keys_.size() <= row) return {}; + if (cached_items_.size() <= row) return {}; - return buffered_keys_[row].ID(); + return cached_items_[row].Key().ID(); } auto GpgKeyTableModel::IsPrivateKeyByRow(int row) const -> bool { - if (buffered_keys_.size() <= row) return false; - return buffered_keys_[row].IsPrivateKey(); + if (cached_items_.size() <= row) return false; + return cached_items_[row].Key().IsPrivateKey(); } auto GpgKeyTableModel::GetGpgContextChannel() const -> int { return gpg_context_channel_; } + +GpgKeyTableItem::GpgKeyTableItem(const GpgKey &key) : key_(key) {} + +GpgKeyTableItem::GpgKeyTableItem(const GpgKeyTableItem &) = default; + +auto GpgKeyTableItem::Key() const -> GpgKey { return key_; } + +void GpgKeyTableItem::SetChecked(bool checked) { checked_ = checked; } + +auto GpgKeyTableItem::Checked() const -> bool { return checked_; } + } // namespace GpgFrontend diff --git a/src/core/model/GpgKeyTableModel.h b/src/core/model/GpgKeyTableModel.h index 0529bf7b..69118f56 100644 --- a/src/core/model/GpgKeyTableModel.h +++ b/src/core/model/GpgKeyTableModel.h @@ -110,6 +110,36 @@ inline auto operator&(GpgKeyTableDisplayMode lhs, return (static_cast<T>(lhs) & static_cast<T>(rhs)) != 0; } +class GPGFRONTEND_CORE_EXPORT GpgKeyTableItem { + public: + GpgKeyTableItem() = default; + + explicit GpgKeyTableItem(const GpgKey &key); + + GpgKeyTableItem(const GpgKeyTableItem &); + + [[nodiscard]] auto Key() const -> GpgKey; + + /** + * @brief + * + * @return bool + */ + [[nodiscard]] auto Checked() const -> bool; + + /** + * @brief Set the Checked object + * + * @return true + * @return false + */ + void SetChecked(bool); + + private: + GpgKey key_; + bool checked_; +}; + class GPGFRONTEND_CORE_EXPORT GpgKeyTableModel : public QAbstractTableModel { Q_OBJECT public: @@ -125,6 +155,17 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyTableModel : public QAbstractTableModel { /** * @brief * + * @param row + * @param column + * @param parent + * @return QModelIndex + */ + [[nodiscard]] auto index(int row, int column, const QModelIndex &parent) const + -> QModelIndex override; + + /** + * @brief + * * @param parent * @return int */ @@ -212,11 +253,10 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyTableModel : public QAbstractTableModel { [[nodiscard]] auto GetGpgContextChannel() const -> int; private: - GpgKeyList buffered_keys_; QStringList column_headers_; int gpg_context_channel_; - QContainer<bool> key_check_state_; + QContainer<GpgKeyTableItem> cached_items_; }; } // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/model/GpgKeyTreeModel.cpp b/src/core/model/GpgKeyTreeModel.cpp index 3925444c..cb3ee4ba 100644 --- a/src/core/model/GpgKeyTreeModel.cpp +++ b/src/core/model/GpgKeyTreeModel.cpp @@ -36,21 +36,19 @@ namespace GpgFrontend { GpgKeyTreeModel::GpgKeyTreeModel(int channel, const GpgKeyList &keys, - Detector checkable_detector, - Detector enable_detector, QObject *parent) + Detector checkable_detector, QObject *parent) : QAbstractItemModel(parent), gpg_context_channel_(channel), column_headers_({ tr("Select"), + tr("Type"), tr("Identity"), tr("Key ID"), - tr("Type"), tr("Usage"), tr("Algorithm"), tr("Create Date"), }), - checkable_detector_(std::move(checkable_detector)), - enable_detector_(std::move(enable_detector)) { + checkable_detector_(std::move(checkable_detector)) { setup_model_data(keys); } @@ -203,9 +201,16 @@ auto GpgKeyTreeModel::create_gpg_key_tree_items(const GpgKey &key) -> QSharedPointer<GpgKeyTreeItem> { QVariantList columns; columns << "/"; + + QString type; + type += key.IsPrivateKey() ? "pub/sec" : "pub"; + if (key.IsPrivateKey() && !key.IsHasMasterKey()) type += "#"; + if (key.IsHasCardKey()) type += "^"; + columns << type; + columns << key.UIDs().front().GetUID(); columns << key.ID(); - columns << "C"; + columns << GetUsagesByKey(key); columns << key.PublicKeyAlgo(); columns << key.Algo(); @@ -213,16 +218,16 @@ auto GpgKeyTreeModel::create_gpg_key_tree_items(const GpgKey &key) auto i_key = QSharedPointer<GpgKeyTreeItem>::create( QSharedPointer<GpgKey>::create(key), columns); - i_key->SetEnable(enable_detector_(i_key->Key())); + i_key->SetEnable(true); i_key->SetCheckable(checkable_detector_(i_key->Key())); cached_items_.push_back(i_key); for (const auto &s_key : key.SubKeys()) { QVariantList columns; columns << "/"; + columns << (s_key.IsHasCertCap() ? "primary" : "sub"); columns << key.UIDs().front().GetUID(); columns << s_key.ID(); - columns << (s_key.IsHasCertCap() ? "P" : "S"); columns << GetUsagesBySubkey(s_key); columns << s_key.PublicKeyAlgo(); columns << s_key.Algo(); @@ -230,7 +235,7 @@ auto GpgKeyTreeModel::create_gpg_key_tree_items(const GpgKey &key) auto i_s_key = QSharedPointer<GpgKeyTreeItem>::create( QSharedPointer<GpgSubKey>::create(s_key), columns); - i_s_key->SetEnable(enable_detector_(i_s_key->Key())); + i_s_key->SetEnable(true); i_s_key->SetCheckable(checkable_detector_(i_s_key->Key())); i_key->AppendChild(i_s_key); cached_items_.push_back(i_s_key); diff --git a/src/core/model/GpgKeyTreeModel.h b/src/core/model/GpgKeyTreeModel.h index 42b29055..f56591ea 100644 --- a/src/core/model/GpgKeyTreeModel.h +++ b/src/core/model/GpgKeyTreeModel.h @@ -42,9 +42,9 @@ namespace GpgFrontend { enum class GpgKeyTreeColumn : unsigned int { kNONE = 0, - kIDENTITY = 1 << 1, - kKEY_ID = 1 << 2, - kTYPE = 1 << 3, + kTYPE = 1 << 1, + kIDENTITY = 1 << 2, + kKEY_ID = 1 << 3, kUSAGE = 1 << 4, kALGO = 1 << 5, kCREATE_DATE = 1 << 6, @@ -82,6 +82,33 @@ inline auto operator~(GpgKeyTreeColumn hs) -> GpgKeyTreeColumn { return static_cast<GpgKeyTreeColumn>(~static_cast<T>(hs)); } +enum class GpgKeyTreeDisplayMode : unsigned int { + kNONE = 0, + kPUBLIC_KEY = 1 << 0, + kPRIVATE_KEY = 1 << 1, + kFAVORITES = 1 << 2, + kALL = ~0U +}; + +inline auto operator|(GpgKeyTreeDisplayMode lhs, + GpgKeyTreeDisplayMode rhs) -> GpgKeyTreeDisplayMode { + using T = std::underlying_type_t<GpgKeyTreeDisplayMode>; + return static_cast<GpgKeyTreeDisplayMode>(static_cast<T>(lhs) | + static_cast<T>(rhs)); +} + +inline auto operator|=(GpgKeyTreeDisplayMode &lhs, + GpgKeyTreeDisplayMode rhs) -> GpgKeyTreeDisplayMode & { + lhs = lhs | rhs; + return lhs; +} + +inline auto operator&(GpgKeyTreeDisplayMode lhs, + GpgKeyTreeDisplayMode rhs) -> bool { + using T = std::underlying_type_t<GpgKeyTreeDisplayMode>; + return (static_cast<T>(lhs) & static_cast<T>(rhs)) != 0; +} + class GPGFRONTEND_CORE_EXPORT GpgKeyTreeItem { public: GpgKeyTreeItem() = default; @@ -217,8 +244,7 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyTreeModel : public QAbstractItemModel { * @param parent */ explicit GpgKeyTreeModel(int channel, const GpgKeyList &keys, - Detector checkable, Detector enable, - QObject *parent = nullptr); + Detector checkable, QObject *parent = nullptr); /** * @brief @@ -324,7 +350,6 @@ class GPGFRONTEND_CORE_EXPORT GpgKeyTreeModel : public QAbstractItemModel { int gpg_context_channel_; QVariantList column_headers_; Detector checkable_detector_; - Detector enable_detector_; QSharedPointer<GpgKeyTreeItem> root_; QContainer<QSharedPointer<GpgKeyTreeItem>> cached_items_; diff --git a/src/core/model/GpgSubKey.h b/src/core/model/GpgSubKey.h index dc92ae1f..fe925f8e 100644 --- a/src/core/model/GpgSubKey.h +++ b/src/core/model/GpgSubKey.h @@ -163,7 +163,7 @@ class GPGFRONTEND_CORE_EXPORT GpgSubKey : public GpgAbstractKey { * @return true * @return false */ - [[nodiscard]] auto IsPrivateKey() const -> bool; + [[nodiscard]] auto IsPrivateKey() const -> bool override; /** * @brief diff --git a/src/ui/dialog/ADSKsPicker.cpp b/src/ui/dialog/ADSKsPicker.cpp index a3589a91..f10a936a 100644 --- a/src/ui/dialog/ADSKsPicker.cpp +++ b/src/ui/dialog/ADSKsPicker.cpp @@ -34,15 +34,14 @@ namespace GpgFrontend::UI { ADSKsPicker::ADSKsPicker(int channel, - const GpgKeyTreeModel::Detector& enable_detector, + const GpgKeyTreeProxyModel::KeyFilter& filter, QWidget* parent) : GeneralDialog(typeid(ADSKsPicker).name(), parent), tree_view_(new KeyTreeView( channel, [](GpgAbstractKey* k) { return k->IsSubKey(); }, - [=](GpgAbstractKey* k) { - return (!k->IsSubKey() || (k->IsSubKey() && !k->IsPrimaryKey() && - k->IsHasEncrCap())) && - enable_detector(k); + [=](const GpgAbstractKey* k) { + return (!k->IsSubKey() || (k->IsSubKey() && k->IsHasEncrCap())) && + filter(k); })) { auto* confirm_button = new QPushButton(tr("Confirm")); auto* cancel_button = new QPushButton(tr("Cancel")); @@ -77,7 +76,7 @@ ADSKsPicker::ADSKsPicker(int channel, this->setWindowTitle(tr("ADSKs Picker")); movePosition2CenterOfParent(); - + this->show(); this->raise(); this->activateWindow(); diff --git a/src/ui/dialog/ADSKsPicker.h b/src/ui/dialog/ADSKsPicker.h index 9c431209..e5c08c9c 100644 --- a/src/ui/dialog/ADSKsPicker.h +++ b/src/ui/dialog/ADSKsPicker.h @@ -29,9 +29,9 @@ #pragma once #include "GpgFrontendUI.h" -#include "core/model/GpgKeyTreeModel.h" #include "core/typedef/GpgTypedef.h" #include "ui/dialog//GeneralDialog.h" +#include "ui/model/GpgKeyTreeProxyModel.h" namespace GpgFrontend::UI { @@ -50,7 +50,8 @@ class ADSKsPicker : public GeneralDialog { * * @param parent */ - explicit ADSKsPicker(int channel, const GpgKeyTreeModel::Detector& enable, + explicit ADSKsPicker(int channel, + const GpgKeyTreeProxyModel::KeyFilter& filter, QWidget* parent = nullptr); /** diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp index ea175f3e..79a28176 100644 --- a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp +++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp @@ -294,7 +294,9 @@ void KeyPairSubkeyTab::slot_add_adsk() { auto* dialog = new ADSKsPicker( current_gpg_context_channel_, - [=](GpgAbstractKey* key) { return !except_key_ids.contains(key->ID()); }, + [=](const GpgAbstractKey* key) { + return !except_key_ids.contains(key->ID()); + }, this); dialog->exec(); @@ -474,8 +476,9 @@ void KeyPairSubkeyTab::contextMenuEvent(QContextMenuEvent* event) { export_subkey_act_->setDisabled(!s_key.IsSecretKey()); edit_subkey_act_->setDisabled(!s_key.IsSecretKey()); - delete_subkey_act_->setDisabled(!s_key.IsSecretKey()); - revoke_subkey_act_->setDisabled(!s_key.IsSecretKey() || s_key.IsRevoked()); + delete_subkey_act_->setDisabled(!s_key.IsSecretKey() && !s_key.IsADSK()); + revoke_subkey_act_->setDisabled((!s_key.IsSecretKey() && !s_key.IsADSK()) || + s_key.IsRevoked()); subkey_opera_menu_->exec(event->globalPos()); } diff --git a/src/ui/model/GpgKeyTreeProxyModel.cpp b/src/ui/model/GpgKeyTreeProxyModel.cpp new file mode 100644 index 00000000..e58cee88 --- /dev/null +++ b/src/ui/model/GpgKeyTreeProxyModel.cpp @@ -0,0 +1,128 @@ +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend 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 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend 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 GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#include "GpgKeyTreeProxyModel.h" + +#include "core/function/gpg/GpgKeyGetter.h" +#include "core/model/CacheObject.h" +#include "core/model/GpgKey.h" +#include "core/model/GpgKeyTreeModel.h" +#include "core/struct/cache_object/AllFavoriteKeyPairsCO.h" +#include "core/utils/GpgUtils.h" + +namespace GpgFrontend::UI { + +GpgKeyTreeProxyModel::GpgKeyTreeProxyModel( + QSharedPointer<GpgKeyTreeModel> model, GpgKeyTreeDisplayMode display_mode, + KeyFilter filter, QObject *parent) + : QSortFilterProxyModel(parent), + model_(std::move(model)), + display_mode_(display_mode), + custom_filter_(std::move(filter)) { + setSourceModel(model_.get()); + + connect(this, &GpgKeyTreeProxyModel::SignalFavoritesChanged, this, + &GpgKeyTreeProxyModel::slot_update_favorites); + + emit SignalFavoritesChanged(); +} + +auto GpgKeyTreeProxyModel::filterAcceptsRow( + int source_row, const QModelIndex &sourceParent) const -> bool { + auto index = sourceModel()->index(source_row, 0, sourceParent); + auto *i = index.isValid() + ? static_cast<GpgKeyTreeItem *>(index.internalPointer()) + : nullptr; + + const auto *key = i->Key(); + LOG_D() << "get key: " << key->ID() + << "from channel: " << model_->GetGpgContextChannel(); + assert(key->IsGood()); + + if (!(display_mode_ & GpgKeyTreeDisplayMode::kPRIVATE_KEY) && + key->IsPrivateKey()) { + return false; + } + + if (!(display_mode_ & GpgKeyTreeDisplayMode::kPUBLIC_KEY) && + !key->IsPrivateKey()) { + return false; + } + + if (!custom_filter_(key)) return false; + + if (filter_keywords_.isEmpty()) return true; + + QStringList infos; + for (int column = 0; column < sourceModel()->columnCount(); ++column) { + auto index = sourceModel()->index(source_row, column, sourceParent); + infos << sourceModel()->data(index).toString(); + + if (!key->IsSubKey()) { + for (const auto &uid : dynamic_cast<const GpgKey *>(key)->UIDs()) { + infos << uid.GetUID(); + } + } + } + + return std::any_of(infos.cbegin(), infos.cend(), [&](const QString &info) { + return info.contains(filter_keywords_, Qt::CaseInsensitive); + }); + + return false; +} + +void GpgKeyTreeProxyModel::SetSearchKeywords(const QString &keywords) { + this->filter_keywords_ = keywords; + invalidateFilter(); +} + +void GpgKeyTreeProxyModel::slot_update_favorites() { + slot_update_favorites_cache(); + invalidateFilter(); +} + +void GpgKeyTreeProxyModel::ResetGpgKeyTableModel( + QSharedPointer<GpgKeyTreeModel> model) { + model_ = std::move(model); + slot_update_favorites_cache(); + setSourceModel(model_.get()); +} + +void GpgKeyTreeProxyModel::slot_update_favorites_cache() { + auto json_data = CacheObject("all_favorite_key_pairs"); + auto cache_obj = AllFavoriteKeyPairsCO(json_data.object()); + + auto key_db_name = GetGpgKeyDatabaseName(model_->GetGpgContextChannel()); + + if (cache_obj.key_dbs.contains(key_db_name)) { + favorite_key_ids_ = cache_obj.key_dbs[key_db_name].key_ids; + } +} + +} // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/model/GpgKeyTreeProxyModel.h b/src/ui/model/GpgKeyTreeProxyModel.h new file mode 100644 index 00000000..5e75dd84 --- /dev/null +++ b/src/ui/model/GpgKeyTreeProxyModel.h @@ -0,0 +1,85 @@ +/** + * Copyright (C) 2021-2024 Saturneric <[email protected]> + * + * This file is part of GpgFrontend. + * + * GpgFrontend 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 3 of the License, or + * (at your option) any later version. + * + * GpgFrontend 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 GpgFrontend. If not, see <https://www.gnu.org/licenses/>. + * + * The initial version of the source code is inherited from + * the gpg4usb project, which is under GPL-3.0-or-later. + * + * All the source code of GpgFrontend was modified and released by + * Saturneric <[email protected]> starting on May 12, 2021. + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +#pragma once + +#include <QFont> +#include <QFontMetrics> + +#include "core/model/GpgKeyTreeModel.h" + +namespace GpgFrontend::UI { + +class GpgKeyTreeProxyModel : public QSortFilterProxyModel { + Q_OBJECT + public: + using KeyFilter = std::function<bool(const GpgAbstractKey *)>; + + explicit GpgKeyTreeProxyModel(QSharedPointer<GpgKeyTreeModel> model, + GpgKeyTreeDisplayMode display_mode, + KeyFilter filter, QObject *parent); + + void SetSearchKeywords(const QString &keywords); + + void ResetGpgKeyTableModel(QSharedPointer<GpgKeyTreeModel> model); + + protected: + [[nodiscard]] auto filterAcceptsRow( + int sourceRow, const QModelIndex &sourceParent) const -> bool override; + + signals: + + /** + * @brief + * + */ + void SignalFavoritesChanged(); + + private slots: + + /** + * @brief + * + */ + void slot_update_favorites(); + + /** + * @brief + * + */ + void slot_update_favorites_cache(); + + private: + QSharedPointer<GpgKeyTreeModel> model_; + GpgKeyTreeDisplayMode display_mode_; + QString filter_keywords_; + QStringList favorite_key_ids_; + KeyFilter custom_filter_; +}; + +} // namespace GpgFrontend::UI
\ No newline at end of file diff --git a/src/ui/widgets/KeyTreeView.cpp b/src/ui/widgets/KeyTreeView.cpp index 0e8ab859..3dde069a 100644 --- a/src/ui/widgets/KeyTreeView.cpp +++ b/src/ui/widgets/KeyTreeView.cpp @@ -28,20 +28,25 @@ #include "ui/widgets/KeyTreeView.h" +#include <utility> + #include "core/function/gpg/GpgKeyGetter.h" +#include "model/GpgKeyTreeProxyModel.h" namespace GpgFrontend::UI { KeyTreeView::KeyTreeView(int channel, GpgKeyTreeModel::Detector checkable_detector, - GpgKeyTreeModel::Detector enable_detector, + GpgKeyTreeProxyModel::KeyFilter filter, QWidget* parent) - : QTreeView(parent), channel_(channel) { - model_ = QSharedPointer<GpgKeyTreeModel>::create( - channel, GpgKeyGetter::GetInstance(channel_).FetchKey(), - checkable_detector, enable_detector, this); - - setModel(model_.get()); + : QTreeView(parent), + channel_(channel), + model_(QSharedPointer<GpgKeyTreeModel>::create( + channel, GpgKeyGetter::GetInstance(channel_).FetchKey(), + checkable_detector, this)), + proxy_model_(model_, GpgKeyTreeDisplayMode::kALL, std::move(filter), + this) { + setModel(&proxy_model_); sortByColumn(2, Qt::AscendingOrder); setSelectionBehavior(QAbstractItemView::SelectRows); @@ -50,7 +55,10 @@ KeyTreeView::KeyTreeView(int channel, setEditTriggers(QAbstractItemView::NoEditTriggers); header()->resizeSections(QHeaderView::Interactive); header()->setDefaultAlignment(Qt::AlignCenter); + header()->setMinimumHeight(20); + setUniformRowHeights(true); + setExpandsOnDoubleClick(true); setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); setFocusPolicy(Qt::NoFocus); diff --git a/src/ui/widgets/KeyTreeView.h b/src/ui/widgets/KeyTreeView.h index ba0f09a4..a362a8a1 100644 --- a/src/ui/widgets/KeyTreeView.h +++ b/src/ui/widgets/KeyTreeView.h @@ -29,7 +29,7 @@ #pragma once #include "core/model/GpgKey.h" -#include "core/model/GpgKeyTreeModel.h" +#include "model/GpgKeyTreeProxyModel.h" namespace GpgFrontend::UI { @@ -50,7 +50,7 @@ class KeyTreeView : public QTreeView { */ explicit KeyTreeView(int channel, GpgKeyTreeModel::Detector checkable_detector, - GpgKeyTreeModel::Detector enable_detector, + GpgKeyTreeProxyModel::KeyFilter filter, QWidget* parent = nullptr); /** @@ -78,6 +78,7 @@ class KeyTreeView : public QTreeView { bool init_; int channel_; QSharedPointer<GpgKeyTreeModel> model_; ///< + GpgKeyTreeProxyModel proxy_model_; void slot_adjust_column_widths(); }; |