File indexing completed on 2025-01-05 04:55:46
0001 /* 0002 SPDX-FileCopyrightText: 2024 g10 Code GmbH 0003 SPDX-FileContributor: Tobias Fella <tobias.fella@gnupg.com> 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "useridproxymodel.h" 0008 0009 #include "keylist.h" 0010 #include "keylistmodel.h" 0011 #include "kleo/keyfiltermanager.h" 0012 #include "utils/algorithm.h" 0013 #include "utils/formatting.h" 0014 #include "utils/systeminfo.h" 0015 0016 #include <global.h> 0017 0018 #include <QColor> 0019 0020 #include <variant> 0021 0022 using namespace Kleo; 0023 0024 class UserIDProxyModel::Private 0025 { 0026 public: 0027 Private(UserIDProxyModel *qq); 0028 void loadUserIDs(); 0029 QList<std::variant<GpgME::UserID, KeyGroup>> mIds; 0030 QAbstractItemModel *oldSourceModel = nullptr; 0031 UserIDProxyModel *q; 0032 }; 0033 0034 void UserIDProxyModel::Private::loadUserIDs() 0035 { 0036 q->beginResetModel(); 0037 mIds.clear(); 0038 mIds.reserve(q->sourceModel()->rowCount()); 0039 for (auto i = 0; i < q->sourceModel()->rowCount(); ++i) { 0040 const auto key = q->sourceModel()->index(i, 0).data(KeyList::KeyRole).value<GpgME::Key>(); 0041 QList<GpgME::UserID> ids; 0042 if (key.isNull()) { 0043 mIds += q->sourceModel()->index(i, 0).data(KeyList::GroupRole).value<KeyGroup>(); 0044 } else if (key.protocol() == GpgME::OpenPGP) { 0045 for (const auto &userID : key.userIDs()) { 0046 mIds += userID; 0047 } 0048 } else { 0049 QList<std::variant<GpgME::UserID, KeyGroup>> ids; 0050 for (const auto &userID : key.userIDs()) { 0051 const auto exists = Kleo::contains_if(ids, [userID](const auto &other) { 0052 return !qstrcmp(std::get<GpgME::UserID>(other).email(), userID.email()); 0053 }); 0054 if (!exists && userID.email() && *userID.email()) { 0055 ids += userID; 0056 } 0057 } 0058 if (ids.count() > 0) { 0059 mIds.append(ids); 0060 } else { 0061 mIds.append(key.userID(0)); 0062 } 0063 } 0064 } 0065 q->endResetModel(); 0066 } 0067 0068 UserIDProxyModel::Private::Private(UserIDProxyModel *qq) 0069 : q(qq) 0070 { 0071 connect(q, &UserIDProxyModel::sourceModelChanged, q, [this]() { 0072 if (oldSourceModel) { 0073 disconnect(oldSourceModel, nullptr, q, nullptr); 0074 } 0075 connect(q->sourceModel(), &QAbstractItemModel::dataChanged, q, [this]() { 0076 loadUserIDs(); 0077 }); 0078 connect(q->sourceModel(), &QAbstractItemModel::rowsInserted, q, [this]() { 0079 loadUserIDs(); 0080 }); 0081 connect(q->sourceModel(), &QAbstractItemModel::modelReset, q, [this]() { 0082 loadUserIDs(); 0083 }); 0084 oldSourceModel = q->sourceModel(); 0085 loadUserIDs(); 0086 }); 0087 } 0088 0089 UserIDProxyModel::UserIDProxyModel(QObject *parent) 0090 : AbstractKeyListSortFilterProxyModel(parent) 0091 , d{new Private(this)} 0092 { 0093 } 0094 0095 UserIDProxyModel::~UserIDProxyModel() = default; 0096 0097 static QVariant returnIfValid(const QColor &t) 0098 { 0099 if (t.isValid()) { 0100 return t; 0101 } else { 0102 return QVariant(); 0103 } 0104 } 0105 0106 QModelIndex UserIDProxyModel::mapFromSource(const QModelIndex &sourceIndex) const 0107 { 0108 if (!sourceIndex.isValid()) { 0109 return {}; 0110 } 0111 const auto &sourceKey = sourceIndex.data(KeyList::KeyRole).value<GpgME::Key>(); 0112 if (sourceKey.isNull()) { 0113 const auto &sourceKeyGroup = sourceIndex.data(KeyList::GroupRole).value<KeyGroup>(); 0114 for (int i = 0; i < d->mIds.count(); ++i) { 0115 if (std::holds_alternative<KeyGroup>(d->mIds[i]) && std::get<KeyGroup>(d->mIds[i]).id() == sourceKeyGroup.id()) { 0116 return index(i, sourceIndex.column(), {}); 0117 } 0118 } 0119 } else { 0120 const auto &fingerprint = sourceKey.primaryFingerprint(); 0121 for (int i = 0; i < d->mIds.count(); ++i) { 0122 if (std::holds_alternative<GpgME::UserID>(d->mIds[i]) && !qstrcmp(fingerprint, std::get<GpgME::UserID>(d->mIds[i]).parent().primaryFingerprint())) { 0123 return index(i, sourceIndex.column(), {}); 0124 } 0125 } 0126 } 0127 0128 return {}; 0129 } 0130 0131 QModelIndex UserIDProxyModel::mapToSource(const QModelIndex &proxyIndex) const 0132 { 0133 if (!proxyIndex.isValid()) { 0134 return {}; 0135 } 0136 const auto &entry = d->mIds[proxyIndex.row()]; 0137 0138 if (std::holds_alternative<KeyGroup>(entry)) { 0139 const auto &id = std::get<KeyGroup>(entry).id(); 0140 for (int i = 0; i < sourceModel()->rowCount(); ++i) { 0141 if (sourceModel()->index(i, 0).data(KeyList::GroupRole).value<KeyGroup>().id() == id) { 0142 return sourceModel()->index(i, proxyIndex.column()); 0143 } 0144 } 0145 } else { 0146 const auto &fingerprint = std::get<GpgME::UserID>(entry).parent().primaryFingerprint(); 0147 for (int i = 0; i < sourceModel()->rowCount(); ++i) { 0148 if (!qstrcmp(sourceModel()->index(i, 0).data(KeyList::KeyRole).value<GpgME::Key>().primaryFingerprint(), fingerprint)) { 0149 return sourceModel()->index(i, proxyIndex.column()); 0150 } 0151 } 0152 } 0153 0154 return {}; 0155 } 0156 0157 int UserIDProxyModel::rowCount(const QModelIndex &parent) const 0158 { 0159 if (parent.isValid()) { 0160 return 0; 0161 } 0162 return d->mIds.count(); 0163 } 0164 0165 QModelIndex UserIDProxyModel::index(int row, int column, const QModelIndex &parent) const 0166 { 0167 if (parent.isValid()) { 0168 return {}; 0169 } 0170 return createIndex(row, column, nullptr); 0171 } 0172 0173 QModelIndex UserIDProxyModel::parent(const QModelIndex &) const 0174 { 0175 return {}; 0176 } 0177 0178 int UserIDProxyModel::columnCount(const QModelIndex &index) const 0179 { 0180 if (!sourceModel()) { 0181 return 0; 0182 } 0183 return sourceModel()->columnCount(mapToSource(index)); 0184 } 0185 0186 QVariant UserIDProxyModel::data(const QModelIndex &index, int role) const 0187 { 0188 const auto &entry = d->mIds[index.row()]; 0189 if (std::holds_alternative<KeyGroup>(entry)) { 0190 return AbstractKeyListSortFilterProxyModel::data(index, role); 0191 } 0192 const auto &userId = std::get<GpgME::UserID>(entry); 0193 const auto &key = userId.parent(); 0194 if (role == KeyList::UserIDRole) { 0195 return QVariant::fromValue(userId); 0196 } 0197 if ((role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::AccessibleTextRole)) { 0198 if (index.column() == KeyList::Columns::PrettyName) { 0199 if (key.protocol() == GpgME::OpenPGP) { 0200 return Formatting::prettyName(userId); 0201 } else { 0202 return Formatting::prettyName(key); 0203 } 0204 } 0205 if (index.column() == KeyList::Columns::PrettyEMail) { 0206 return Formatting::prettyEMail(userId); 0207 } 0208 if (index.column() == KeyList::Columns::Validity) { 0209 return Formatting::complianceStringShort(userId); 0210 } 0211 if (index.column() == KeyList::Columns::Summary) { 0212 return Formatting::summaryLine(userId); 0213 } 0214 if (index.column() == KeyList::Columns::Origin) { 0215 return Formatting::origin(userId.origin()); 0216 } 0217 if (index.column() == KeyList::Columns::LastUpdate) { 0218 if (role == Qt::AccessibleTextRole) { 0219 return Formatting::accessibleDate(userId.lastUpdate()); 0220 } else { 0221 return Formatting::dateString(userId.lastUpdate()); 0222 } 0223 } 0224 } 0225 if (role == Qt::BackgroundRole) { 0226 if (!SystemInfo::isHighContrastModeActive()) { 0227 return returnIfValid(KeyFilterManager::instance()->bgColor(userId)); 0228 } 0229 } else if (role == Qt::ForegroundRole) { 0230 if (!SystemInfo::isHighContrastModeActive()) { 0231 return returnIfValid(KeyFilterManager::instance()->fgColor(userId)); 0232 } 0233 } 0234 return AbstractKeyListSortFilterProxyModel::data(index, role); 0235 } 0236 0237 UserIDProxyModel *UserIDProxyModel::clone() const 0238 { 0239 auto model = new UserIDProxyModel(QObject::parent()); 0240 model->setSourceModel(sourceModel()); 0241 return model; 0242 } 0243 0244 QModelIndex UserIDProxyModel::index(const KeyGroup &group) const 0245 { 0246 Q_UNUSED(group); 0247 return {}; 0248 } 0249 0250 QModelIndex UserIDProxyModel::index(const GpgME::Key &key) const 0251 { 0252 Q_UNUSED(key); 0253 return {}; 0254 }