Libkleo

useridlistmodel.cpp
1/* -*- mode: c++; c-basic-offset:4 -*-
2 models/useridlistmodel.cpp
3
4 This file is part of Kleopatra, the KDE keymanager
5 SPDX-FileCopyrightText: 2007 Klarälvdalens Datakonsult AB
6 SPDX-FileCopyrightText: 2016 Andre Heinecke <aheinecke@gnupg.org>
7 SPDX-FileCopyrightText: 2021 g10 Code GmbH
8 SPDX-FileContributor: Ingo Klöcker <dev@ingo-kloecker.de>
9
10 SPDX-License-Identifier: GPL-2.0-or-later
11*/
12
13#include <config-libkleo.h>
14
15#include "useridlistmodel.h"
16
17#include "keycache.h"
18
19#include <libkleo/formatting.h>
20
21#include <KLocalizedString>
22
23#include <QDate>
24#include <QIcon>
25#include <QVariant>
26
27#include <gpgme++/key.h>
28
29using namespace GpgME;
30using namespace Kleo;
31
32class UIDModelItem
33{
34 // A uid model item can either be a UserID::Signature or a UserID.
35 // you can find out which it is if the uid or the signature return
36 // null values. (Not null but isNull)
37 //
38public:
39 explicit UIDModelItem(const UserID::Signature &sig, UIDModelItem *parentItem, bool showRemarks)
40 : mParentItem{parentItem}
41 , mSig{sig}
42 {
43 const auto name = Formatting::prettyName(sig);
44 const auto email = Formatting::prettyEMail(sig);
45 mItemData = {
46 Formatting::prettyID(sig.signerKeyID()),
47 name,
48 email,
49 Formatting::validityShort(sig),
50 sig.isExportable() ? QStringLiteral("✓") : QString{},
51 Formatting::creationDateString(sig),
52 Formatting::expirationDateString(sig),
53 };
54
55 QString lastNotation;
56 if (showRemarks && parentItem) {
57 for (const auto &notation : sig.notations()) {
58 if (notation.name() && !strcmp(notation.name(), "rem@gnupg.org")) {
59 lastNotation = QString::fromUtf8(notation.value());
60 }
61 }
62 }
63 mItemData.push_back(lastNotation);
64
65 const auto trustSignatureDomain = Formatting::trustSignatureDomain(sig);
66 mItemData.push_back(trustSignatureDomain);
67 mAccessibleText = {
68 Formatting::accessibleHexID(sig.signerKeyID()),
69 name.isEmpty() ? i18nc("text for screen readers for an empty name", "no name") : QVariant{},
70 email.isEmpty() ? i18nc("text for screen readers for an empty email address", "no email") : QVariant{},
71 {}, // display text is always okay
72 sig.isExportable() ? i18nc("yes, is exportable", "yes") : i18nc("no, is not exportable", "no"),
73 Formatting::accessibleDate(Formatting::creationDate(sig)),
74 Formatting::accessibleExpirationDate(sig),
75 lastNotation.isEmpty() ? i18nc("accessible text for empty list of tags", "none") : QVariant{},
76 trustSignatureDomain.isEmpty() ? i18n("not applicable") : QVariant{},
77 };
78 Q_ASSERT(mAccessibleText.size() == mItemData.size());
79 }
80
81 explicit UIDModelItem(const UserID &uid, UIDModelItem *parentItem)
82 : mParentItem{parentItem}
83 , mUid{uid}
84 {
85 mItemData = {Formatting::prettyUserID(uid)};
86 // for the empty cells of the user ID rows we announce "User ID"
87 mAccessibleText = {
88 {}, // use displayed user ID
89 i18n("User ID"),
90 i18n("User ID"),
91 i18n("User ID"),
92 i18n("User ID"),
93 i18n("User ID"),
94 i18n("User ID"),
95 i18n("User ID"),
96 i18n("User ID"),
97 };
98 }
99
100 // The root item
101 UIDModelItem()
102 {
103 mItemData = {
104 i18n("User ID / Certification Key ID"),
105 i18n("Name"),
106 i18n("Email"),
107 i18n("Status"),
108 i18n("Exportable"),
109 i18n("Valid From"),
110 i18n("Valid Until"),
111 i18n("Tags"),
112 i18n("Trust Signature For"),
113 };
114 // mAccessibleText is explicitly left empty
115 }
116
117 ~UIDModelItem()
118 {
119 qDeleteAll(mChildItems);
120 }
121
122 void appendChild(UIDModelItem *child)
123 {
124 mChildItems << child;
125 }
126
127 UIDModelItem *child(int row) const
128 {
129 return mChildItems.value(row);
130 }
131
132 const UIDModelItem *constChild(int row) const
133 {
134 return mChildItems.value(row);
135 }
136
137 int childCount() const
138 {
139 return mChildItems.count();
140 }
141
142 int columnCount() const
143 {
144 if (childCount()) {
145 // We take the value from the first child
146 // as we are likely a UID and our children
147 // are UID Signatures.
148 return constChild(0)->columnCount();
149 }
150 return mItemData.count();
151 }
152
153 QVariant data(int column) const
154 {
155 return mItemData.value(column);
156 }
157
158 QVariant accessibleText(int column) const
159 {
160 return mAccessibleText.value(column);
161 }
162
163 QVariant toolTip(int column) const
164 {
165 if (!mSig.isNull()) {
166 if (column == static_cast<int>(UserIDListModel::Column::TrustSignatureDomain)) {
167 return Formatting::trustSignature(mSig);
168 }
169 }
170 return mItemData.value(column);
171 }
172
173 QVariant icon(int column) const
174 {
175 if (!mSig.isNull() && column == static_cast<int>(UserIDListModel::Column::Status)) {
176 return Formatting::validityIcon(mSig);
177 }
178 return {};
179 }
180
181 int row() const
182 {
183 if (mParentItem) {
184 return mParentItem->mChildItems.indexOf(const_cast<UIDModelItem *>(this));
185 }
186 return 0;
187 }
188
189 UIDModelItem *parentItem() const
190 {
191 return mParentItem;
192 }
193
194 UserID::Signature signature() const
195 {
196 return mSig;
197 }
198
199 UserID uid() const
200 {
201 return mUid;
202 }
203
204 const char *signerKeyId() const
205 {
206 return mSig.signerKeyID();
207 }
208
209private:
210 QList<UIDModelItem *> mChildItems;
211 QList<QVariant> mItemData;
212 QList<QVariant> mAccessibleText;
213 UIDModelItem *mParentItem = nullptr;
214 UserID::Signature mSig;
215 UserID mUid;
216};
217
218UserIDListModel::UserIDListModel(QObject *p)
220{
221}
222
223UserIDListModel::~UserIDListModel() = default;
224
225Key UserIDListModel::key() const
226{
227 return mKey;
228}
229
230static std::vector<UserID::Signature> effectiveSignatures(const UserID &userID)
231{
232 std::vector<UserID::Signature> sigs = userID.signatures();
233 std::sort(sigs.begin(), sigs.end());
234 std::reverse(sigs.begin(), sigs.end());
235 auto last = std::unique(sigs.begin(), sigs.end(), [](const auto &sig1, const auto &sig2) {
236 return !qstricmp(sig1.signerKeyID(), sig2.signerKeyID());
237 });
238 sigs.erase(last, sigs.end());
239 std::reverse(sigs.begin(), sigs.end());
240 return sigs;
241}
242
243void UserIDListModel::setKey(const Key &key)
244{
246 mKey = key;
247
248 mRootItem.reset(new UIDModelItem);
249 for (int i = 0, ids = key.numUserIDs(); i < ids; ++i) {
250 UserID uid = key.userID(i);
251 auto uidItem = new UIDModelItem(uid, mRootItem.get());
252 mRootItem->appendChild(uidItem);
253
254 for (const auto &sig : effectiveSignatures(uid)) {
255 auto sigItem = new UIDModelItem(sig, uidItem, mRemarksEnabled);
256 uidItem->appendChild(sigItem);
257 }
258 }
259
261}
262
263int UserIDListModel::columnCount(const QModelIndex &parent) const
264{
265 if (parent.isValid()) {
266 return static_cast<UIDModelItem *>(parent.internalPointer())->columnCount();
267 }
268
269 if (!mRootItem) {
270 return 0;
271 }
272
273 return mRootItem->columnCount();
274}
275
276int UserIDListModel::rowCount(const QModelIndex &parent) const
277{
278 if (parent.column() > 0 || !mRootItem) {
279 return 0;
280 }
281
282 const UIDModelItem *const parentItem = !parent.isValid() ? mRootItem.get() : static_cast<UIDModelItem *>(parent.internalPointer());
283 return parentItem->childCount();
284}
285
286QModelIndex UserIDListModel::index(int row, int column, const QModelIndex &parent) const
287{
288 if (!hasIndex(row, column, parent)) {
289 return {};
290 }
291
292 const UIDModelItem *const parentItem = !parent.isValid() ? mRootItem.get() : static_cast<UIDModelItem *>(parent.internalPointer());
293 UIDModelItem *const childItem = parentItem->child(row);
294 if (childItem) {
295 return createIndex(row, column, childItem);
296 } else {
297 return QModelIndex();
298 }
299}
300
302{
303 if (!index.isValid()) {
304 return {};
305 }
306 auto childItem = static_cast<UIDModelItem *>(index.internalPointer());
307 UIDModelItem *parentItem = childItem->parentItem();
308
309 if (parentItem == mRootItem.get()) {
310 return QModelIndex();
311 }
312
313 return createIndex(parentItem->row(), 0, parentItem);
314}
315
316QVariant UserIDListModel::headerData(int section, Qt::Orientation o, int role) const
317{
318 if (o == Qt::Horizontal && mRootItem) {
319 if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole) {
320 return mRootItem->data(section);
321 } else if (role == Qt::AccessibleTextRole) {
322 return mRootItem->accessibleText(section);
323 }
324 }
325 return QVariant();
326}
327
328QVariant UserIDListModel::data(const QModelIndex &index, int role) const
329{
330 if (!index.isValid()) {
331 return QVariant();
332 }
333
334 auto item = static_cast<UIDModelItem *>(index.internalPointer());
335
336 switch (role) {
337 case Qt::DisplayRole:
338 case Qt::EditRole:
339 return item->data(index.column());
341 return item->accessibleText(index.column());
342 case Qt::ToolTipRole:
343 return item->toolTip(index.column());
345 return item->icon(index.column());
346 case UserIDListModel::SignerKeyIdRole:
347 return QVariant::fromValue(item->signerKeyId());
348 default:;
349 }
350
351 return {};
352}
353
354UserID UserIDListModel::userID(const QModelIndex &index) const
355{
356 if (!index.isValid()) {
357 return UserID();
358 }
359 UIDModelItem *item = static_cast<UIDModelItem *>(index.internalPointer());
360 return item->uid();
361}
362
363QList<UserID> UserIDListModel::userIDs(const QModelIndexList &indexes) const
364{
366 for (const QModelIndex &idx : indexes) {
367 if (!idx.isValid()) {
368 continue;
369 }
370 auto item = static_cast<UIDModelItem *>(idx.internalPointer());
371 if (!item->uid().isNull()) {
372 ret << item->uid();
373 }
374 }
375 return ret;
376}
377
378UserID::Signature UserIDListModel::signature(const QModelIndex &index) const
379{
380 if (!index.isValid()) {
381 return UserID::Signature();
382 }
383 UIDModelItem *item = static_cast<UIDModelItem *>(index.internalPointer());
384 return item->signature();
385}
386
387QList<UserID::Signature> UserIDListModel::signatures(const QModelIndexList &indexes) const
388{
390 for (const QModelIndex &idx : indexes) {
391 if (!idx.isValid()) {
392 continue;
393 }
394 auto item = static_cast<UIDModelItem *>(idx.internalPointer());
395 if (!item->signature().isNull()) {
396 ret << item->signature();
397 }
398 }
399 return ret;
400}
401
402void UserIDListModel::enableRemarks(bool value)
403{
404 mRemarksEnabled = value;
405}
406
407#include "moc_useridlistmodel.cpp"
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
QModelIndex createIndex(int row, int column, const void *ptr) const const
bool hasIndex(int row, int column, const QModelIndex &parent) const const
qsizetype count() const const
qsizetype indexOf(const AT &value, qsizetype from) const const
void push_back(parameter_type value)
qsizetype size() const const
T value(qsizetype i) const const
int column() const const
void * internalPointer() const const
bool isValid() const const
QObject * parent() const const
QString fromUtf8(QByteArrayView str)
DisplayRole
Orientation
QVariant fromValue(T &&value)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Dec 6 2024 12:02:18 by doxygen 1.12.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.