aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsaturneric <[email protected]>2025-04-12 14:00:42 +0000
committersaturneric <[email protected]>2025-04-12 14:00:42 +0000
commit1f393a50c60d94a4ae23875b31e0e07ad3d64375 (patch)
treee4e4597dfca48a38c73779fe577e22e3550ee993 /src
parentfix: avoid accessing invalid pointers of structs related to gpgme_key (diff)
downloadGpgFrontend-1f393a50c60d94a4ae23875b31e0e07ad3d64375.tar.gz
GpgFrontend-1f393a50c60d94a4ae23875b31e0e07ad3d64375.zip
fix: issues on adsk operations
Diffstat (limited to 'src')
-rw-r--r--src/core/model/GpgAbstractKey.h1
-rw-r--r--src/core/model/GpgKey.h2
-rw-r--r--src/core/model/GpgKeyTableModel.cpp59
-rw-r--r--src/core/model/GpgKeyTableModel.h44
-rw-r--r--src/core/model/GpgKeyTreeModel.cpp23
-rw-r--r--src/core/model/GpgKeyTreeModel.h37
-rw-r--r--src/core/model/GpgSubKey.h2
-rw-r--r--src/ui/dialog/ADSKsPicker.cpp11
-rw-r--r--src/ui/dialog/ADSKsPicker.h5
-rw-r--r--src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp9
-rw-r--r--src/ui/model/GpgKeyTreeProxyModel.cpp128
-rw-r--r--src/ui/model/GpgKeyTreeProxyModel.h85
-rw-r--r--src/ui/widgets/KeyTreeView.cpp22
-rw-r--r--src/ui/widgets/KeyTreeView.h5
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();
};