aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorsaturneric <[email protected]>2025-04-12 10:15:20 +0000
committersaturneric <[email protected]>2025-04-12 10:15:20 +0000
commitb91a96e1bd50923100887efe2d8f262102447b39 (patch)
treed58d71c23dcf5d8ae528c9fa54c1a6236101dc99 /src
parentfeat: support adsk (diff)
downloadGpgFrontend-b91a96e1bd50923100887efe2d8f262102447b39.tar.gz
GpgFrontend-b91a96e1bd50923100887efe2d8f262102447b39.zip
feat: add ui support for adsk features
Diffstat (limited to 'src')
-rw-r--r--src/core/function/gpg/GpgKeyOpera.cpp2
-rw-r--r--src/core/model/GpgAbstractKey.h59
-rw-r--r--src/core/model/GpgKey.cpp3
-rw-r--r--src/core/model/GpgKey.h13
-rw-r--r--src/core/model/GpgKeyTableModel.cpp8
-rw-r--r--src/core/model/GpgKeyTreeModel.cpp309
-rw-r--r--src/core/model/GpgKeyTreeModel.h424
-rw-r--r--src/core/model/GpgSubKey.cpp5
-rw-r--r--src/core/model/GpgSubKey.h23
-rw-r--r--src/core/utils/GpgUtils.cpp20
-rw-r--r--src/core/utils/GpgUtils.h16
-rw-r--r--src/ui/dialog/SubKeysPicker.cpp82
-rw-r--r--src/ui/dialog/SubKeysPicker.h74
-rw-r--r--src/ui/dialog/keypair_details/KeyPairSubkeyTab.cpp92
-rw-r--r--src/ui/dialog/keypair_details/KeyPairSubkeyTab.h8
-rw-r--r--src/ui/widgets/KeyTreeView.cpp84
-rw-r--r--src/ui/widgets/KeyTreeView.h85
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