diff options
author | saturneric <[email protected]> | 2025-04-12 10:15:20 +0000 |
---|---|---|
committer | saturneric <[email protected]> | 2025-04-12 10:15:20 +0000 |
commit | b91a96e1bd50923100887efe2d8f262102447b39 (patch) | |
tree | d58d71c23dcf5d8ae528c9fa54c1a6236101dc99 | |
parent | feat: support adsk (diff) | |
download | GpgFrontend-b91a96e1bd50923100887efe2d8f262102447b39.tar.gz GpgFrontend-b91a96e1bd50923100887efe2d8f262102447b39.zip |
feat: add ui support for adsk features
-rw-r--r-- | src/core/function/gpg/GpgKeyOpera.cpp | 2 | ||||
-rw-r--r-- | src/core/model/GpgAbstractKey.h | 59 | ||||
-rw-r--r-- | src/core/model/GpgKey.cpp | 3 | ||||
-rw-r--r-- | src/core/model/GpgKey.h | 13 | ||||
-rw-r--r-- | src/core/model/GpgKeyTableModel.cpp | 8 | ||||
-rw-r--r-- | src/core/model/GpgKeyTreeModel.cpp | 309 | ||||
-rw-r--r-- | src/core/model/GpgKeyTreeModel.h | 424 | ||||
-rw-r--r-- | src/core/model/GpgSubKey.cpp | 5 | ||||
-rw-r--r-- | src/core/model/GpgSubKey.h | 23 | ||||
-rw-r--r-- | src/core/utils/GpgUtils.cpp | 20 | ||||
-rw-r--r-- | src/core/utils/GpgUtils.h | 16 | ||||
-rw-r--r-- | src/ui/dialog/SubKeysPicker.cpp | 82 | ||||
-rw-r--r-- | src/ui/dialog/SubKeysPicker.h | 74 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp | 92 | ||||
-rw-r--r-- | src/ui/dialog/keypair_details/KeyPairSubkeyTab.h | 8 | ||||
-rw-r--r-- | src/ui/widgets/KeyTreeView.cpp | 84 | ||||
-rw-r--r-- | src/ui/widgets/KeyTreeView.h | 85 |
17 files changed, 1278 insertions, 29 deletions
diff --git a/src/core/function/gpg/GpgKeyOpera.cpp b/src/core/function/gpg/GpgKeyOpera.cpp index 931eae72..2378b518 100644 --- a/src/core/function/gpg/GpgKeyOpera.cpp +++ b/src/core/function/gpg/GpgKeyOpera.cpp @@ -371,7 +371,7 @@ auto AddADSKImpl(GpgContext& ctx, const GpgKey& key, const GpgSubKey& adsk, unsigned int flags = GPGME_CREATE_ADSK; LOG_D() << "add adsk args: " << key.GetId() << algo; - + auto err = gpgme_op_createsubkey(ctx.DefaultContext(), static_cast<gpgme_key_t>(key), algo.toLatin1(), 0, 0, flags); diff --git a/src/core/model/GpgAbstractKey.h b/src/core/model/GpgAbstractKey.h new file mode 100644 index 00000000..b34643cd --- /dev/null +++ b/src/core/model/GpgAbstractKey.h @@ -0,0 +1,59 @@ +/** + * 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 + +namespace GpgFrontend { + +class GpgAbstractKey { + public: + [[nodiscard]] virtual auto ID() const -> QString = 0; + [[nodiscard]] virtual auto Fingerprint() const -> QString = 0; + [[nodiscard]] virtual auto IsSubKey() const -> bool = 0; + [[nodiscard]] virtual auto CreationTime() const -> QDateTime = 0; + [[nodiscard]] virtual auto IsHasEncrCap() const -> bool = 0; + [[nodiscard]] virtual auto IsHasSignCap() const -> bool = 0; + [[nodiscard]] virtual auto IsHasCertCap() const -> bool = 0; + [[nodiscard]] virtual auto IsHasAuthCap() const -> bool = 0; + + // + + [[nodiscard]] auto IsPrimaryKey() const -> bool { return IsHasCertCap(); } + + auto operator==(const GpgAbstractKey& o) const -> bool { + return ID() == o.ID(); + } + + auto operator<(const GpgAbstractKey& o) const -> bool { + return ID() < o.ID(); + } + + virtual ~GpgAbstractKey() = default; +}; + +} // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/model/GpgKey.cpp b/src/core/model/GpgKey.cpp index 2bf15f3a..ce6f1c10 100644 --- a/src/core/model/GpgKey.cpp +++ b/src/core/model/GpgKey.cpp @@ -241,4 +241,7 @@ void GpgKey::KeyRefDeleter::operator()(gpgme_key_t _key) { if (_key != nullptr) gpgme_key_unref(_key); } +auto GpgKey::GetPrimaryKey() const -> GpgSubKey { + return GpgSubKey(key_ref_->subkeys); +} } // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/model/GpgKey.h b/src/core/model/GpgKey.h index a3fe8396..e2743670 100644 --- a/src/core/model/GpgKey.h +++ b/src/core/model/GpgKey.h @@ -123,9 +123,9 @@ class GPGFRONTEND_CORE_EXPORT GpgKey { auto operator<=(const GpgKey&) const -> bool; /** - * @brief - * - * @return gpgme_key_t + * @brief + * + * @return gpgme_key_t */ // NOLINTNEXTLINE(google-explicit-constructor) operator gpgme_key_t() const; @@ -364,6 +364,13 @@ class GPGFRONTEND_CORE_EXPORT GpgKey { */ [[nodiscard]] auto GetUIDs() const -> std::unique_ptr<QContainer<GpgUID>>; + /** + * @brief Get the Primary Key object + * + * @return GpgSubKey + */ + [[nodiscard]] auto GetPrimaryKey() const -> GpgSubKey; + private: /** * @brief diff --git a/src/core/model/GpgKeyTableModel.cpp b/src/core/model/GpgKeyTableModel.cpp index b9d03bb6..53f5d1b1 100644 --- a/src/core/model/GpgKeyTableModel.cpp +++ b/src/core/model/GpgKeyTableModel.cpp @@ -30,6 +30,7 @@ #include "core/function/gpg/GpgKeyGetter.h" #include "core/model/GpgKey.h" +#include "core/utils/GpgUtils.h" namespace GpgFrontend { @@ -87,12 +88,7 @@ auto GpgKeyTableModel::data(const QModelIndex &index, return key.GetEmail(); } case 4: { - QString usage_sym; - if (key.IsHasActualCertCap()) usage_sym += "C"; - if (key.IsHasActualEncrCap()) usage_sym += "E"; - if (key.IsHasActualSignCap()) usage_sym += "S"; - if (key.IsHasActualAuthCap()) usage_sym += "A"; - return usage_sym; + return GetUsagesByKey(key); } case 5: { return key.GetOwnerTrust(); diff --git a/src/core/model/GpgKeyTreeModel.cpp b/src/core/model/GpgKeyTreeModel.cpp new file mode 100644 index 00000000..ce43d2d1 --- /dev/null +++ b/src/core/model/GpgKeyTreeModel.cpp @@ -0,0 +1,309 @@ +/** + * 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 "GpgKeyTreeModel.h" + +#include <utility> + +#include "core/model/GpgKey.h" +#include "core/utils/GpgUtils.h" + +namespace GpgFrontend { + +GpgKeyTreeModel::GpgKeyTreeModel(int channel, const GpgKeyList &keys, + Detector checkable_detector, + Detector enable_detector, QObject *parent) + : QAbstractItemModel(parent), + gpg_context_channel_(channel), + column_headers_({ + tr("Select"), + 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)) { + setup_model_data(keys); +} + +auto GpgKeyTreeModel::index(int row, int column, + const QModelIndex &parent) const -> QModelIndex { + if (!hasIndex(row, column, parent)) return {}; + + GpgKeyTreeItem *i_parent = + parent.isValid() ? static_cast<GpgKeyTreeItem *>(parent.internalPointer()) + : root_.get(); + + auto *i_child = i_parent->Child(row); + if (i_child != nullptr) { + return createIndex(row, column, i_child); + } + + return {}; +} + +auto GpgKeyTreeModel::rowCount(const QModelIndex &parent) const -> int { + if (parent.column() > 0) return 0; + + const GpgKeyTreeItem *i_parent = + parent.isValid() + ? static_cast<const GpgKeyTreeItem *>(parent.internalPointer()) + : root_.get(); + + return static_cast<int>(i_parent->ChildCount()); +} + +auto GpgKeyTreeModel::columnCount(const QModelIndex &parent) const -> int { + if (parent.isValid()) { + return static_cast<int>( + static_cast<GpgKeyTreeItem *>(parent.internalPointer())->ColumnCount()); + } + return static_cast<int>(root_->ColumnCount()); +} + +auto GpgKeyTreeModel::data(const QModelIndex &index, + int role) const -> QVariant { + if (!index.isValid()) return {}; + + const auto *item = + static_cast<const GpgKeyTreeItem *>(index.internalPointer()); + + if (role == Qt::CheckStateRole) { + if (index.column() == 0 && item->Checkable()) { + return item->Checked() ? Qt::Checked : Qt::Unchecked; + } + } + + if (role == Qt::DisplayRole) { + if (index.column() == 0) return item->Row(); + return item->Data(index.column()); + } + + if (role == Qt::TextAlignmentRole) { + switch (index.column()) { + case 1: + case 2: + case 3: + case 4: + case 5: + case 6: + return Qt::AlignCenter; + default: + return {}; + } + } + + return {}; +} + +auto GpgKeyTreeModel::headerData(int section, Qt::Orientation orientation, + int role) const -> QVariant { + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + return root_->Data(section); + } + return {}; +} + +auto GpgKeyTreeModel::flags(const QModelIndex &index) const -> Qt::ItemFlags { + if (!index.isValid()) return Qt::NoItemFlags; + + const auto *item = + static_cast<const GpgKeyTreeItem *>(index.internalPointer()); + + if (index.column() == 0) { + return (item->Checkable() ? Qt::ItemIsUserCheckable : Qt::ItemFlags{0}) | + Qt::ItemIsSelectable | + (item->Enable() ? Qt::ItemIsEnabled : Qt::ItemFlags{0}); + } + + return Qt::ItemIsSelectable | + (item->Enable() ? Qt::ItemIsEnabled : Qt::ItemFlags{0}); +} + +auto GpgKeyTreeModel::setData(const QModelIndex &index, const QVariant &value, + int role) -> bool { + if (!index.isValid()) return false; + + auto *item = static_cast<GpgKeyTreeItem *>(index.internalPointer()); + + if (index.column() == 0 && role == Qt::CheckStateRole) { + item->SetChecked(value == Qt::Checked); + emit dataChanged(index, index); + return true; + } + + return false; +} + +auto GpgKeyTreeModel::GetGpgContextChannel() const -> int { + return gpg_context_channel_; +} + +void GpgKeyTreeModel::setup_model_data(const GpgKeyList &keys) { + auto root = QSharedPointer<GpgKeyTreeItem>::create(nullptr, column_headers_); + cached_items_.clear(); + + for (const auto &key : keys) { + auto pi_key = create_gpg_key_tree_items(key); + root->AppendChild(pi_key); + } + + std::swap(root_, root); +} + +auto GpgKeyTreeModel::parent(const QModelIndex &index) const -> QModelIndex { + if (!index.isValid()) return {}; + + auto *i_child = static_cast<GpgKeyTreeItem *>(index.internalPointer()); + GpgKeyTreeItem *i_parent = i_child->ParentItem(); + + return i_parent != root_.get() + ? createIndex(static_cast<int>(i_parent->Row()), 0, i_parent) + : QModelIndex{}; +} + +auto GpgKeyTreeModel::GetAllCheckedKeyIds() -> KeyIdArgsList { + auto ret = KeyIdArgsList{}; + for (const auto &item : cached_items_) { + if (!item->Checkable() || !item->Checked()) continue; + ret.push_back(item->Key()->ID()); + } + return ret; +} + +auto GpgKeyTreeModel::create_gpg_key_tree_items(const GpgKey &key) + -> QSharedPointer<GpgKeyTreeItem> { + QVariantList columns; + columns << "/"; + columns << key.GetUIDs()->front().GetUID(); + columns << key.GetId(); + columns << "C"; + columns << GetUsagesByKey(key); + columns << key.GetPublicKeyAlgo(); + columns << key.GetKeyAlgo(); + columns << QLocale().toString(key.GetCreateTime(), "yyyy-MM-dd"); + + auto i_key = QSharedPointer<GpgKeyTreeItem>::create( + QSharedPointer<GpgKeyAdapter>::create(key), columns); + i_key->SetEnable(enable_detector_(i_key->Key())); + i_key->SetCheckable(checkable_detector_(i_key->Key())); + cached_items_.push_back(i_key); + + auto s_keys = key.GetSubKeys(); + for (const auto &s_key : *s_keys) { + QVariantList columns; + columns << "/"; + columns << key.GetUIDs()->front().GetUID(); + columns << s_key.GetID(); + columns << (s_key.IsHasCertCap() ? "P" : "S"); + columns << GetUsagesBySubkey(s_key); + columns << s_key.GetPubkeyAlgo(); + columns << s_key.GetKeyAlgo(); + columns << QLocale().toString(s_key.GetCreateTime(), "yyyy-MM-dd"); + + auto i_s_key = QSharedPointer<GpgKeyTreeItem>::create( + QSharedPointer<GpgSubKeyAdapter>::create(s_key), columns); + i_s_key->SetEnable(enable_detector_(i_s_key->Key())); + i_s_key->SetCheckable(checkable_detector_(i_s_key->Key())); + i_key->AppendChild(i_s_key); + cached_items_.push_back(i_s_key); + } + + return i_key; +} + +auto GpgKeyTreeModel::GetAllCheckedSubKey() -> QContainer<GpgSubKey> { + QContainer<GpgSubKey> ret; + for (const auto &i : cached_items_) { + if (!i->Key()->IsSubKey() || !i->Checkable() || !i->Checked()) continue; + + auto *adaptor = dynamic_cast<GpgSubKeyAdapter *>(i->Key()); + if (adaptor == nullptr) continue; + + ret.push_back(adaptor->GetRawKey()); + } + return ret; +} + +GpgKeyTreeItem::GpgKeyTreeItem(QSharedPointer<GpgAbstractKey> key, + QVariantList data) + : data_(std::move(data)), key_(std::move(key)) {} + +void GpgKeyTreeItem::AppendChild(const QSharedPointer<GpgKeyTreeItem> &child) { + child->parent_ = this; + children_.append(child); +} + +auto GpgKeyTreeItem::Child(int row) -> GpgKeyTreeItem * { + return row >= 0 && row < ChildCount() ? children_.at(row).get() : nullptr; +} + +auto GpgKeyTreeItem::ChildCount() const -> qsizetype { + return children_.size(); +} + +auto GpgKeyTreeItem::ColumnCount() const -> qsizetype { return data_.count(); } + +auto GpgKeyTreeItem::Data(qsizetype column) const -> QVariant { + return data_.value(column); +} + +auto GpgKeyTreeItem::Row() const -> qsizetype { + if (parent_ == nullptr) return 0; + const auto it = + std::find_if(parent_->children_.cbegin(), parent_->children_.cend(), + [this](const auto &item) { return item.get() == this; }); + + if (it != parent_->children_.cend()) { + return std::distance(parent_->children_.cbegin(), it); + } + + Q_ASSERT(false); + return -1; +} + +auto GpgKeyTreeItem::ParentItem() -> GpgKeyTreeItem * { return parent_; } + +auto GpgKeyTreeItem::Checked() const -> bool { return checked_; } + +auto GpgKeyTreeItem::Checkable() const -> bool { return checkable_; } + +void GpgKeyTreeItem::SetChecked(bool checked) { checked_ = checked; } + +void GpgKeyTreeItem::SetCheckable(bool checkable) { checkable_ = checkable; } + +auto GpgKeyTreeItem::Key() const -> GpgAbstractKey * { return key_.get(); } + +auto GpgKeyTreeItem::Enable() const -> bool { return enable_; } + +void GpgKeyTreeItem::SetEnable(bool enable) { enable_ = enable; } + +} // namespace GpgFrontend diff --git a/src/core/model/GpgKeyTreeModel.h b/src/core/model/GpgKeyTreeModel.h new file mode 100644 index 00000000..96b78400 --- /dev/null +++ b/src/core/model/GpgKeyTreeModel.h @@ -0,0 +1,424 @@ +/** + * 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 + +/** + * @brief + * + */ +#include <utility> + +#include "core/model/GpgAbstractKey.h" +#include "core/model/GpgKey.h" +#include "core/typedef/GpgTypedef.h" + +namespace GpgFrontend { + +enum class GpgKeyTreeColumn : unsigned int { + kNONE = 0, + kIDENTITY = 1 << 1, + kKEY_ID = 1 << 2, + kTYPE = 1 << 3, + kUSAGE = 1 << 4, + kALGO = 1 << 5, + kCREATE_DATE = 1 << 6, + kALL = ~0U +}; + +inline auto operator|(GpgKeyTreeColumn lhs, + GpgKeyTreeColumn rhs) -> GpgKeyTreeColumn { + using T = std::underlying_type_t<GpgKeyTreeColumn>; + return static_cast<GpgKeyTreeColumn>(static_cast<T>(lhs) | + static_cast<T>(rhs)); +} + +inline auto operator|=(GpgKeyTreeColumn &lhs, + GpgKeyTreeColumn rhs) -> GpgKeyTreeColumn & { + lhs = lhs | rhs; + return lhs; +} + +inline auto operator&(GpgKeyTreeColumn lhs, + GpgKeyTreeColumn rhs) -> GpgKeyTreeColumn { + using T = std::underlying_type_t<GpgKeyTreeColumn>; + return static_cast<GpgKeyTreeColumn>(static_cast<T>(lhs) & + static_cast<T>(rhs)); +} + +inline auto operator&=(GpgKeyTreeColumn &lhs, + GpgKeyTreeColumn rhs) -> GpgKeyTreeColumn & { + lhs = lhs & rhs; + return lhs; +} + +inline auto operator~(GpgKeyTreeColumn hs) -> GpgKeyTreeColumn { + using T = std::underlying_type_t<GpgKeyTreeColumn>; + return static_cast<GpgKeyTreeColumn>(~static_cast<T>(hs)); +} + +class GPGFRONTEND_CORE_EXPORT GpgKeyAdapter : public GpgAbstractKey { + public: + [[nodiscard]] auto ID() const -> QString override { return key_.GetId(); } + + [[nodiscard]] auto Fingerprint() const -> QString override { + return key_.GetFingerprint(); + } + + [[nodiscard]] auto IsSubKey() const -> bool override { return false; } + + [[nodiscard]] auto CreationTime() const -> QDateTime override { + return key_.GetCreateTime(); + } + + [[nodiscard]] auto IsHasEncrCap() const -> bool override { + return key_.IsHasActualEncrCap(); + } + + [[nodiscard]] auto IsHasSignCap() const -> bool override { + return key_.IsHasActualSignCap(); + } + + [[nodiscard]] auto IsHasCertCap() const -> bool override { + return key_.IsHasActualCertCap(); + } + + [[nodiscard]] auto IsHasAuthCap() const -> bool override { + return key_.IsHasActualAuthCap(); + } + + [[nodiscard]] auto GetRawKey() const -> const GpgKey & { return key_; } + + explicit GpgKeyAdapter(GpgKey key) : key_(std::move(key)) {} + + private: + GpgKey key_; +}; + +class GPGFRONTEND_CORE_EXPORT GpgSubKeyAdapter : public GpgAbstractKey { + public: + [[nodiscard]] auto ID() const -> QString override { return key_.GetID(); } + + [[nodiscard]] auto Fingerprint() const -> QString override { + return key_.GetFingerprint(); + } + + [[nodiscard]] auto IsSubKey() const -> bool override { return true; } + + [[nodiscard]] auto CreationTime() const -> QDateTime override { + return key_.GetCreateTime(); + } + + [[nodiscard]] auto IsHasEncrCap() const -> bool override { + return key_.IsHasEncrCap(); + } + + [[nodiscard]] auto IsHasSignCap() const -> bool override { + return key_.IsHasSignCap(); + } + + [[nodiscard]] auto IsHasCertCap() const -> bool override { + return key_.IsHasCertCap(); + } + + [[nodiscard]] auto IsHasAuthCap() const -> bool override { + return key_.IsHasAuthCap(); + } + + [[nodiscard]] auto GetRawKey() const -> const GpgSubKey & { return key_; } + + explicit GpgSubKeyAdapter(const GpgSubKey &key) : key_(key) {} + + private: + GpgSubKey key_; +}; + +class GPGFRONTEND_CORE_EXPORT GpgKeyTreeItem { + public: + GpgKeyTreeItem() = default; + + /** + * @brief Construct a new Gpg Key Tree Item object + * + * @param headers + */ + explicit GpgKeyTreeItem(QSharedPointer<GpgAbstractKey> key, + QVariantList data); + + /** + * @brief + * + * @param child + */ + void AppendChild(const QSharedPointer<GpgKeyTreeItem> &child); + + /** + * @brief + * + * @param row + * @return GpgKeyTreeItem* + */ + auto Child(int row) -> GpgKeyTreeItem *; + + /** + * @brief + * + * @return qsizetype + */ + [[nodiscard]] auto ChildCount() const -> qsizetype; + + /** + * @brief + * + * @return qsizetype + */ + [[nodiscard]] auto ColumnCount() const -> qsizetype; + + /** + * @brief + * + * @param column + * @return QVariant + */ + [[nodiscard]] auto Data(qsizetype column) const -> QVariant; + + /** + * @brief + * + * @return qsizetype + */ + [[nodiscard]] auto Row() const -> qsizetype; + + /** + * @brief + * + * @return GpgKeyTreeItem* + */ + auto ParentItem() -> GpgKeyTreeItem *; + + /** + * @brief + * + * @return bool + */ + [[nodiscard]] auto Checked() const -> bool; + + /** + * @brief Set the Checked object + * + * @return true + * @return false + */ + void SetChecked(bool); + + /** + * @brief + * + * @return bool + */ + [[nodiscard]] auto Checkable() const -> bool; + + /** + * @brief + * + * @return bool + */ + void SetCheckable(bool); + + /** + * @brief + * + * @return bool + */ + [[nodiscard]] auto Enable() const -> bool; + + /** + * @brief + * + * @return bool + */ + void SetEnable(bool); + + /** + * @brief + * + * @return bool + */ + [[nodiscard]] auto Key() const -> GpgAbstractKey *; + + private: + QContainer<QSharedPointer<GpgKeyTreeItem>> children_; + QVariantList data_; + bool checked_; + bool checkable_; + bool enable_; + QSharedPointer<GpgAbstractKey> key_; + GpgKeyTreeItem *parent_ = nullptr; +}; + +class GPGFRONTEND_CORE_EXPORT GpgKeyTreeModel : public QAbstractItemModel { + Q_OBJECT + public: + using Detector = std::function<bool(GpgAbstractKey *)>; + + /** + * @brief Construct a new Gpg Key Table Model object + * + * @param keys + * @param parent + */ + explicit GpgKeyTreeModel(int channel, const GpgKeyList &keys, + Detector checkable, Detector enable, + QObject *parent = nullptr); + + /** + * @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 + */ + [[nodiscard]] auto rowCount(const QModelIndex &parent) const -> int override; + + /** + * @brief + * + * @param parent + * @return int + */ + [[nodiscard]] auto columnCount(const QModelIndex &parent) const + -> int override; + + /** + * @brief + * + * @param index + * @param role + * @return QVariant + */ + [[nodiscard]] auto data(const QModelIndex &index, + int role) const -> QVariant override; + + /** + * @brief + * + * @param section + * @param orientation + * @param role + * @return QVariant + */ + [[nodiscard]] auto headerData(int section, Qt::Orientation orientation, + int role) const -> QVariant override; + + /** + * @brief Set the Data object + * + * @param index + * @param value + * @param role + * @return true + * @return false + */ + auto setData(const QModelIndex &index, const QVariant &value, + int role) -> bool override; + + /** + * @brief + * + * @param index + * @return Qt::ItemFlags + */ + [[nodiscard]] auto flags(const QModelIndex &index) const + -> Qt::ItemFlags override; + + /** + * @brief + * + * @param index + * @return QModelIndex + */ + [[nodiscard]] auto parent(const QModelIndex &index) const + -> QModelIndex override; + + /** + * @brief + * + * @return int + */ + [[nodiscard]] auto GetGpgContextChannel() const -> int; + + /** + * @brief Get the All Checked Key Ids object + * + * @return KeyIdArgsList + */ + auto GetAllCheckedKeyIds() -> KeyIdArgsList; + + /** + * @brief Get the All Checked Sub Key object + * + * @return QContainer<GpgSubKey> + */ + auto GetAllCheckedSubKey() -> QContainer<GpgSubKey>; + + private: + int gpg_context_channel_; + QVariantList column_headers_; + Detector checkable_detector_; + Detector enable_detector_; + + QSharedPointer<GpgKeyTreeItem> root_; + QContainer<QSharedPointer<GpgKeyTreeItem>> cached_items_; + + /** + * @brief + * + */ + void setup_model_data(const GpgKeyList &keys); + + /** + * @brief Create a gpg key tree items object + * + * @param key + * @return QSharedPointer<GpgKeyTreeItem> + */ + auto create_gpg_key_tree_items(const GpgKey &key) + -> QSharedPointer<GpgKeyTreeItem>; +}; + +} // namespace GpgFrontend
\ No newline at end of file diff --git a/src/core/model/GpgSubKey.cpp b/src/core/model/GpgSubKey.cpp index 42fc476a..dbabf085 100644 --- a/src/core/model/GpgSubKey.cpp +++ b/src/core/model/GpgSubKey.cpp @@ -41,6 +41,10 @@ auto GpgSubKey::operator==(const GpgSubKey& o) const -> bool { return GetFingerprint() == o.GetFingerprint(); } +auto GpgSubKey::operator<(const GpgSubKey& o) const -> bool { + return GetID() < o.GetID(); +} + auto GpgSubKey::GetID() const -> QString { return subkey_ref_->keyid; } auto GpgSubKey::GetFingerprint() const -> QString { return subkey_ref_->fpr; } @@ -99,4 +103,5 @@ auto GpgSubKey::IsADSK() const -> bool { return subkey_ref_->can_renc; } auto GpgSubKey::SmartCardSerialNumber() -> QString { return subkey_ref_->card_number; } + } // namespace GpgFrontend diff --git a/src/core/model/GpgSubKey.h b/src/core/model/GpgSubKey.h index e5a8a21b..6577ff5a 100644 --- a/src/core/model/GpgSubKey.h +++ b/src/core/model/GpgSubKey.h @@ -78,6 +78,15 @@ class GPGFRONTEND_CORE_EXPORT GpgSubKey { /** * @brief * + * @param o + * @return true + * @return false + */ + auto operator<(const GpgSubKey& o) const -> bool; + + /** + * @brief + * * @return QString */ [[nodiscard]] auto GetID() const -> QString; @@ -205,17 +214,17 @@ class GPGFRONTEND_CORE_EXPORT GpgSubKey { [[nodiscard]] auto GetExpireTime() const -> QDateTime; /** - * @brief - * - * @return true - * @return false + * @brief + * + * @return true + * @return false */ [[nodiscard]] auto IsADSK() const -> bool; /** - * @brief - * - * @return QString + * @brief + * + * @return QString */ [[nodiscard]] auto SmartCardSerialNumber() -> QString; diff --git a/src/core/utils/GpgUtils.cpp b/src/core/utils/GpgUtils.cpp index c96f523b..c7040cc2 100644 --- a/src/core/utils/GpgUtils.cpp +++ b/src/core/utils/GpgUtils.cpp @@ -333,4 +333,24 @@ auto GPGFRONTEND_CORE_EXPORT Convert2RawGpgMEKeyList( return recipients; } + +auto GPGFRONTEND_CORE_EXPORT GetUsagesByKey(const GpgKey& key) -> QString { + QString usages; + if (key.IsHasActualCertCap()) usages += "C"; + if (key.IsHasActualEncrCap()) usages += "E"; + if (key.IsHasActualSignCap()) usages += "S"; + if (key.IsHasActualAuthCap()) usages += "A"; + return usages; +} + +auto GPGFRONTEND_CORE_EXPORT GetUsagesBySubkey(const GpgSubKey& key) + -> QString { + QString usages; + if (key.IsHasCertCap()) usages += "C"; + if (key.IsHasEncrCap()) usages += "E"; + if (key.IsHasSignCap()) usages += "S"; + if (key.IsHasAuthCap()) usages += "A"; + if (key.IsADSK()) usages += "R"; + return usages; +} } // namespace GpgFrontend diff --git a/src/core/utils/GpgUtils.h b/src/core/utils/GpgUtils.h index 7710f56e..b453cc0a 100644 --- a/src/core/utils/GpgUtils.h +++ b/src/core/utils/GpgUtils.h @@ -157,4 +157,20 @@ auto GPGFRONTEND_CORE_EXPORT GetGpgKeyDatabaseName(int channel) -> QString; auto GPGFRONTEND_CORE_EXPORT Convert2RawGpgMEKeyList( const QContainer<GpgKey>& keys) -> QContainer<gpgme_key_t>; +/** + * @brief + * + * @param key + * @return QString + */ +auto GPGFRONTEND_CORE_EXPORT GetUsagesByKey(const GpgKey& key) -> QString; + +/** + * @brief + * + * @param key + * @return QString + */ +auto GPGFRONTEND_CORE_EXPORT GetUsagesBySubkey(const GpgSubKey& key) -> QString; + } // namespace GpgFrontend
\ No newline at end of file diff --git a/src/ui/dialog/SubKeysPicker.cpp b/src/ui/dialog/SubKeysPicker.cpp new file mode 100644 index 00000000..af9c5c6d --- /dev/null +++ b/src/ui/dialog/SubKeysPicker.cpp @@ -0,0 +1,82 @@ +/** + * 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 "SubKeysPicker.h" + +#include "core/GpgModel.h" +#include "ui/widgets/KeyTreeView.h" + +namespace GpgFrontend::UI { + +SubKeysPicker::SubKeysPicker(int channel, + const GpgKeyTreeModel::Detector& enable_detector, + QWidget* parent) + : GeneralDialog(typeid(SubKeysPicker).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); + })) { + auto* confirm_button = new QPushButton(tr("Confirm")); + auto* cancel_button = new QPushButton(tr("Cancel")); + + connect(confirm_button, &QPushButton::clicked, + [=]() { this->accepted_ = true; }); + connect(confirm_button, &QPushButton::clicked, this, &QDialog::accept); + connect(cancel_button, &QPushButton::clicked, this, &QDialog::reject); + + auto* vbox2 = new QVBoxLayout(); + vbox2->addWidget(new QLabel(tr("Select Subkey(s)") + ": ")); + vbox2->addWidget(tree_view_); + vbox2->addWidget(new QLabel( + tr("Please select one or more subkeys you use for operation."))); + vbox2->addWidget(confirm_button); + vbox2->addWidget(cancel_button); + setLayout(vbox2); + + tree_view_->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + this->setWindowFlags(Qt::Window | Qt::WindowTitleHint | + Qt::CustomizeWindowHint); + + this->setModal(true); + this->setWindowTitle(tr("Subkeys Picker")); + + movePosition2CenterOfParent(); + this->show(); +} + +auto SubKeysPicker::GetCheckedSubkeys() -> QContainer<GpgSubKey> { + return tree_view_->GetAllCheckedSubKey(); +} + +auto SubKeysPicker::GetStatus() const -> bool { return this->accepted_; } + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/SubKeysPicker.h b/src/ui/dialog/SubKeysPicker.h new file mode 100644 index 00000000..a7a1b200 --- /dev/null +++ b/src/ui/dialog/SubKeysPicker.h @@ -0,0 +1,74 @@ +/** + * 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 "GpgFrontendUI.h" +#include "core/model/GpgKeyTreeModel.h" +#include "core/typedef/GpgTypedef.h" +#include "ui/dialog//GeneralDialog.h" + +namespace GpgFrontend::UI { + +class KeyTreeView; + +/** + * @brief + * + */ +class SubKeysPicker : public GeneralDialog { + Q_OBJECT + + public: + /** + * @brief Construct a new Signers Picker object + * + * @param parent + */ + explicit SubKeysPicker(int channel, const GpgKeyTreeModel::Detector& enable, + QWidget* parent = nullptr); + + /** + * @brief Get the Checked Signers object + * + * @return GpgFrontend::KeyIdArgsListPtr + */ + auto GetCheckedSubkeys() -> QContainer<GpgSubKey>; + + /** + * + * @return + */ + [[nodiscard]] auto GetStatus() const -> bool; + + private: + KeyTreeView* tree_view_; ///< + bool accepted_ = false; +}; + +} // namespace GpgFrontend::UI diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp index 548b3473..526b9dd5 100644 --- a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp +++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp @@ -32,12 +32,14 @@ #include "core/function/gpg/GpgKeyGetter.h" #include "core/function/gpg/GpgKeyImportExporter.h" #include "core/function/gpg/GpgKeyManager.h" +#include "core/function/gpg/GpgKeyOpera.h" #include "core/utils/CommonUtils.h" #include "core/utils/GpgUtils.h" #include "core/utils/IOUtils.h" #include "ui/UISignalStation.h" #include "ui/UserInterfaceUtils.h" #include "ui/dialog/RevocationOptionsDialog.h" +#include "ui/dialog/SubKeysPicker.h" namespace GpgFrontend::UI { @@ -58,12 +60,17 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(int channel, const QString& key_id, auto* uid_buttons_layout = new QGridLayout(); auto* add_subkey_button = new QPushButton(tr("Generate A New Subkey")); + auto* add_adsk_button = new QPushButton(tr("Add ADSK(s)")); if (!key_.IsPrivateKey() || !key_.IsHasMasterKey()) { add_subkey_button->setDisabled(true); - setHidden(add_subkey_button); + add_subkey_button->hide(); + + add_adsk_button->setDisabled(true); + add_adsk_button->hide(); } uid_buttons_layout->addWidget(add_subkey_button, 0, 1); + uid_buttons_layout->addWidget(add_adsk_button, 1, 1); auto* base_layout = new QVBoxLayout(); @@ -137,6 +144,8 @@ KeyPairSubkeyTab::KeyPairSubkeyTab(int channel, const QString& key_id, connect(add_subkey_button, &QPushButton::clicked, this, &KeyPairSubkeyTab::slot_add_subkey); + connect(add_adsk_button, &QPushButton::clicked, this, + &KeyPairSubkeyTab::slot_add_adsk); connect(subkey_list_, &QTableWidget::itemSelectionChanged, this, &KeyPairSubkeyTab::slot_refresh_subkey_detail); @@ -205,8 +214,10 @@ void KeyPairSubkeyTab::slot_refresh_subkey_list() { tmp0->setTextAlignment(Qt::AlignCenter); subkey_list_->setItem(row, 0, tmp0); - auto* tmp1 = new QTableWidgetItem(subkey.IsHasCertCap() ? tr("Primary Key") - : tr("Subkey")); + auto type = subkey.IsHasCertCap() ? tr("Primary Key") : tr("Subkey"); + if (subkey.IsADSK()) type = tr("ADSK"); + + auto* tmp1 = new QTableWidgetItem(type); tmp1->setTextAlignment(Qt::AlignCenter); subkey_list_->setItem(row, 1, tmp1); @@ -276,6 +287,72 @@ void KeyPairSubkeyTab::slot_add_subkey() { dialog->show(); } +void KeyPairSubkeyTab::slot_add_adsk() { + QStringList except_key_ids; + except_key_ids.append(key_.GetId()); + auto except_sub_keys = key_.GetSubKeys(); + for (const auto& sub_key : *except_sub_keys) { + except_key_ids.append(sub_key.GetID()); + } + + auto* dialog = new SubKeysPicker( + current_gpg_context_channel_, + [=](GpgAbstractKey* key) { return !except_key_ids.contains(key->ID()); }, + this); + dialog->exec(); + + if (!dialog->GetStatus()) { + dialog->deleteLater(); + return; + } + + auto sub_keys = dialog->GetCheckedSubkeys(); + dialog->deleteLater(); + + if (sub_keys.isEmpty()) { + QMessageBox::information(this, tr("No Subkeys Selected"), + tr("Please select at least one subkey.")); + return; + } + + QContainer<GpgSubKey> err_sub_keys; + for (const auto& sub_key : sub_keys) { + auto [err, data_object] = + GpgKeyOpera::GetInstance(current_gpg_context_channel_) + .AddADSKSync(key_, sub_key); + if (CheckGpgError(err) == GPG_ERR_NO_ERROR) continue; + + err_sub_keys.append(sub_key); + } + + if (err_sub_keys.isEmpty()) { + QMessageBox::information( + this, tr("Success"), + tr("All selected subkeys were successfully added as ADSKs.")); + } else { + QStringList failed_info; + for (const auto& sub_key : err_sub_keys) { + QString key_id = sub_key.GetID(); + failed_info << tr("Key ID: %1").arg(key_id); + } + + QString details = failed_info.join("\n\n"); + + QMessageBox msg_box(this); + msg_box.setIcon(QMessageBox::Warning); + msg_box.setWindowTitle(err_sub_keys.size() == sub_keys.size() + ? tr("Failed") + : tr("Partially Failed")); + msg_box.setText(err_sub_keys.size() == sub_keys.size() + ? tr("Failed to add all selected subkeys.") + : tr("Some subkeys failed to be added as ADSKs.")); + msg_box.setDetailedText(details); + msg_box.exec(); + } + + emit SignalKeyDatabaseRefresh(); +} + void KeyPairSubkeyTab::slot_refresh_subkey_detail() { const auto& subkey = get_selected_subkey(); @@ -306,14 +383,7 @@ void KeyPairSubkeyTab::slot_refresh_subkey_detail() { QString buffer; QTextStream usage_steam(&buffer); - if (subkey.IsHasCertCap()) { - usage_steam << tr("Certificate") << " "; - } - if (subkey.IsHasEncrCap()) usage_steam << tr("Encrypt") << " "; - if (subkey.IsHasSignCap()) usage_steam << tr("Sign") << " "; - if (subkey.IsHasAuthCap()) usage_steam << tr("Auth") << " "; - - usage_var_label_->setText(usage_steam.readAll()); + usage_var_label_->setText(GetUsagesBySubkey(subkey)); // Show the situation that secret key not exists. master_key_exist_var_label_->setText(subkey.IsSecretKey() ? tr("Exists") diff --git a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h index b4aa9a00..d06ee032 100644 --- a/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h +++ b/src/ui/dialog/keypair_details/KeyPairSubkeyTab.h @@ -65,7 +65,7 @@ class KeyPairSubkeyTab : public QWidget { * * @return const GpgSubKey& */ - const GpgSubKey& get_selected_subkey(); + auto get_selected_subkey() -> const GpgSubKey&; int current_gpg_context_channel_; GpgKey key_; ///< @@ -147,6 +147,12 @@ class KeyPairSubkeyTab : public QWidget { */ void slot_delete_subkey(); + /** + * @brief + * + */ + void slot_add_adsk(); + signals: /** diff --git a/src/ui/widgets/KeyTreeView.cpp b/src/ui/widgets/KeyTreeView.cpp new file mode 100644 index 00000000..0e8ab859 --- /dev/null +++ b/src/ui/widgets/KeyTreeView.cpp @@ -0,0 +1,84 @@ +/** + * 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 "ui/widgets/KeyTreeView.h" + +#include "core/function/gpg/GpgKeyGetter.h" + +namespace GpgFrontend::UI { + +KeyTreeView::KeyTreeView(int channel, + GpgKeyTreeModel::Detector checkable_detector, + GpgKeyTreeModel::Detector enable_detector, + QWidget* parent) + : QTreeView(parent), channel_(channel) { + model_ = QSharedPointer<GpgKeyTreeModel>::create( + channel, GpgKeyGetter::GetInstance(channel_).FetchKey(), + checkable_detector, enable_detector, this); + + setModel(model_.get()); + + sortByColumn(2, Qt::AscendingOrder); + setSelectionBehavior(QAbstractItemView::SelectRows); + setSelectionMode(QAbstractItemView::SingleSelection); + + setEditTriggers(QAbstractItemView::NoEditTriggers); + header()->resizeSections(QHeaderView::Interactive); + header()->setDefaultAlignment(Qt::AlignCenter); + + setSizeAdjustPolicy(QAbstractScrollArea::AdjustToContents); + + setFocusPolicy(Qt::NoFocus); + setAlternatingRowColors(true); + setSortingEnabled(true); +} + +void KeyTreeView::paintEvent(QPaintEvent* event) { + QTreeView::paintEvent(event); + + if (!init_) { + slot_adjust_column_widths(); + init_ = true; + } +} + +void KeyTreeView::slot_adjust_column_widths() { + for (int i = 1; i < model_->columnCount({}); ++i) { + this->resizeColumnToContents(i); + } +} + +auto KeyTreeView::GetAllCheckedKeyIds() -> KeyIdArgsList { + return model_->GetAllCheckedKeyIds(); +} + +auto KeyTreeView::GetAllCheckedSubKey() -> QContainer<GpgSubKey> { + return model_->GetAllCheckedSubKey(); +} + +} // namespace GpgFrontend::UI diff --git a/src/ui/widgets/KeyTreeView.h b/src/ui/widgets/KeyTreeView.h new file mode 100644 index 00000000..ba0f09a4 --- /dev/null +++ b/src/ui/widgets/KeyTreeView.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 "core/model/GpgKey.h" +#include "core/model/GpgKeyTreeModel.h" + +namespace GpgFrontend::UI { + +/** + * @brief + * + */ +class KeyTreeView : public QTreeView { + Q_OBJECT + public: + /** + * @brief Construct a new Key Table object + * + * @param _key_list + * @param _select_type + * @param _info_type + * @param _filter + */ + explicit KeyTreeView(int channel, + GpgKeyTreeModel::Detector checkable_detector, + GpgKeyTreeModel::Detector enable_detector, + QWidget* parent = nullptr); + + /** + * @brief Get the All Checked Key Ids object + * + * @return KeyIdArgsList + */ + auto GetAllCheckedKeyIds() -> KeyIdArgsList; + + /** + * @brief Get the All Checked Sub Key object + * + * @return QContainer<GpgSubKey> + */ + auto GetAllCheckedSubKey() -> QContainer<GpgSubKey>; + + protected: + /** + * @brief + * + */ + void paintEvent(QPaintEvent* event) override; + + private: + bool init_; + int channel_; + QSharedPointer<GpgKeyTreeModel> model_; ///< + + void slot_adjust_column_widths(); +}; + +} // namespace GpgFrontend::UI
\ No newline at end of file |