Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F34601787
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
19 KB
Subscribers
None
View Options
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 3bd51c65..5671af94 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,98 +1,98 @@
cmake_minimum_required(VERSION 3.0)
set(PIM_VERSION "5.8.42")
project(libkleo VERSION ${PIM_VERSION})
set(KF5_VERSION "5.44.0")
find_package(ECM ${KF5_VERSION} CONFIG REQUIRED)
set(CMAKE_MODULE_PATH ${ECM_MODULE_PATH})
set(LIBRARY_NAMELINK)
include(GenerateExportHeader)
include(ECMSetupVersion)
include(ECMGenerateHeaders)
include(ECMGeneratePriFile)
include(CMakePackageConfigHelpers)
include(ECMSetupVersion)
include(FeatureSummary)
include(KDEInstallDirs)
include(KDECMakeSettings)
include(KDEFrameworkCompilerSettings NO_POLICY_SCOPE)
include(ECMQtDeclareLoggingCategory)
include(ECMAddTests)
include(ECMCoverageOption)
set(LIBKLEO_LIB_VERSION ${PIM_VERSION})
set(QT_REQUIRED_VERSION "5.8.0")
-set(KDEPIMTEXTEDIT_VERSION "5.7.80")
+set(KDEPIMTEXTEDIT_VERSION "5.7.90")
find_package(Qt5 ${QT_REQUIRED_VERSION} CONFIG REQUIRED Widgets)
find_package(KF5I18n ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5Config ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5WidgetsAddons ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5Completion ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5WindowSystem ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5CoreAddons ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5Codecs ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5ItemModels ${KF5_VERSION} CONFIG REQUIRED)
find_package(KF5PimTextEdit ${KDEPIMTEXTEDIT_VERSION} CONFIG)
set(GPGMEPP_LIB_VERSION "1.8.0")
find_package(Gpgmepp ${GPGMEPP_LIB_VERSION})
set_package_properties(Gpgmepp PROPERTIES DESCRIPTION "GpgME++ Library" URL "http://www.gnupg.org" TYPE REQUIRED PURPOSE "GpgME++ is required for OpenPGP support")
find_package(QGpgme ${GPGMEPP_LIB_VERSION} CONFIG REQUIRED)
find_package(Boost 1.34.0)
set_package_properties(Boost PROPERTIES DESCRIPTION "Boost C++ Libraries" URL "http://www.boost.org" TYPE REQUIRED PURPOSE "Boost is required for building most KDEPIM applications")
set_package_properties(KF5PimTextEdit PROPERTIES DESCRIPTION
"A textedit with PIM-specific features."
URL "https://projects.kde.org/projects/kde/pim/kpimtextedit"
TYPE OPTIONAL PURPOSE "Improved audit log viewer.")
ecm_setup_version(PROJECT VARIABLE_PREFIX LIBKLEO
VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/libkleo_version.h"
PACKAGE_VERSION_FILE "${CMAKE_CURRENT_BINARY_DIR}/KF5LibkleoConfigVersion.cmake"
SOVERSION 5
)
########### Targets ###########
add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0)
#add_definitions(-DQT_DISABLE_DEPRECATED_BEFORE=0x060000)
remove_definitions( -DQT_NO_CAST_FROM_ASCII )
add_definitions(-DQT_NO_NARROWING_CONVERSIONS_IN_CONNECT)
add_definitions(-DQT_NO_URL_CAST_FROM_STRING)
add_definitions(-DQT_USE_QSTRINGBUILDER)
########### CMake Config Files ###########
set(CMAKECONFIG_INSTALL_DIR "${KDE_INSTALL_CMAKEPACKAGEDIR}/KF5Libkleo")
configure_package_config_file(
"${CMAKE_CURRENT_SOURCE_DIR}/KF5LibkleoConfig.cmake.in"
"${CMAKE_CURRENT_BINARY_DIR}/KF5LibkleoConfig.cmake"
INSTALL_DESTINATION ${CMAKECONFIG_INSTALL_DIR}
)
install(FILES
"${CMAKE_CURRENT_BINARY_DIR}/KF5LibkleoConfig.cmake"
"${CMAKE_CURRENT_BINARY_DIR}/KF5LibkleoConfigVersion.cmake"
DESTINATION "${CMAKECONFIG_INSTALL_DIR}"
COMPONENT Devel
)
install(EXPORT KF5LibkleoTargets DESTINATION "${CMAKECONFIG_INSTALL_DIR}" FILE KF5LibkleoTargets.cmake NAMESPACE KF5::)
install(FILES
${CMAKE_CURRENT_BINARY_DIR}/libkleo_version.h
DESTINATION ${KDE_INSTALL_INCLUDEDIR_KF5} COMPONENT Devel
)
add_subdirectory(src)
install( FILES libkleo.renamecategories libkleo.categories DESTINATION ${KDE_INSTALL_CONFDIR} )
feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES)
diff --git a/src/ui/keyselectioncombo.cpp b/src/ui/keyselectioncombo.cpp
index e8fd4c39..bedd0cd5 100644
--- a/src/ui/keyselectioncombo.cpp
+++ b/src/ui/keyselectioncombo.cpp
@@ -1,447 +1,461 @@
/* This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2016 Klarälvdalens Datakonsult AB
Kleopatra 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 2 of the License, or
(at your option) any later version.
Kleopatra 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 this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "keyselectioncombo.h"
#include <kleo_ui_debug.h>
#include "kleo/dn.h"
#include "models/keylistmodel.h"
#include "models/keylistsortfilterproxymodel.h"
#include "models/keycache.h"
#include "utils/formatting.h"
#include "progressbar.h"
#include <gpgme++/key.h>
#include <QSortFilterProxyModel>
#include <QVector>
#include <QTimer>
#include <KLocalizedString>
Q_DECLARE_METATYPE(GpgME::Key)
namespace
{
class ProxyModel : public QSortFilterProxyModel
{
Q_OBJECT
private:
struct CustomItem {
QIcon icon;
QString text;
QVariant data;
};
public:
ProxyModel(QObject *parent = nullptr)
: QSortFilterProxyModel(parent)
{
}
~ProxyModel() override
{
qDeleteAll(mFrontItems);
qDeleteAll(mBackItems);
}
bool lessThan(const QModelIndex &left, const QModelIndex &right) const override
{
const auto leftKey = sourceModel()->data(left, Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
const auto rightKey = sourceModel()->data(right, Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
if (leftKey.isNull()) {
return false;
}
if (rightKey.isNull()) {
return true;
}
// As we display UID(0) this is ok. We probably need a get Best UID at some point.
const auto lUid = leftKey.userID(0);
const auto rUid = rightKey.userID(0);
if (lUid.isNull()) {
return false;
}
if (rUid.isNull()) {
return true;
}
int cmp = strcmp (lUid.id(), rUid.id());
if (cmp) {
return cmp < 0;
}
if (lUid.validity() == rUid.validity()) {
/* Both are the same check which one is newer. */
time_t oldTime = 0;
for (const GpgME::Subkey &s: leftKey.subkeys()) {
if (s.isRevoked() || s.isInvalid() || s.isDisabled()) {
continue;
}
if (s.creationTime() > oldTime) {
oldTime= s.creationTime();
}
}
time_t newTime = 0;
for (const GpgME::Subkey &s: rightKey.subkeys()) {
if (s.isRevoked() || s.isInvalid() || s.isDisabled()) {
continue;
}
if (s.creationTime() > newTime) {
newTime = s.creationTime();
}
}
return newTime < oldTime;
}
return lUid.validity() > rUid.validity();
}
bool isCustomItem(const int row) const
{
return row < mFrontItems.count() || row >= mFrontItems.count() + QSortFilterProxyModel::rowCount();
}
void prependItem(const QIcon &icon, const QString &text, const QVariant &data)
{
beginInsertRows(QModelIndex(), 0, 0);
mFrontItems.push_front(new CustomItem{ icon, text, data });
endInsertRows();
}
void appendItem(const QIcon &icon, const QString &text, const QVariant &data)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
mBackItems.push_back(new CustomItem{ icon, text, data });
endInsertRows();
}
void removeCustomItem(const QVariant &data)
{
for (int i = 0; i < mFrontItems.count(); ++i) {
if (mFrontItems[i]->data == data) {
beginRemoveRows(QModelIndex(), i, i);
delete mFrontItems.takeAt(i);
endRemoveRows();
return;
}
}
for (int i = 0; i < mBackItems.count(); ++i) {
if (mBackItems[i]->data == data) {
const int index = mFrontItems.count() + QSortFilterProxyModel::rowCount() + i;
beginRemoveRows(QModelIndex(), index, index);
delete mBackItems.takeAt(i);
endRemoveRows();
return;
}
}
}
int rowCount(const QModelIndex &parent = QModelIndex()) const override
{
return mFrontItems.count() + QSortFilterProxyModel::rowCount(parent) + mBackItems.count();
}
QModelIndex mapToSource(const QModelIndex &index) const override
{
if (!isCustomItem(index.row())) {
const int row = index.row() - mFrontItems.count();
return sourceModel()->index(row, index.column());
} else {
return QModelIndex();
}
}
QModelIndex mapFromSource(const QModelIndex &source_index) const override
{
const QModelIndex idx = QSortFilterProxyModel::mapFromSource(source_index);
return createIndex(mFrontItems.count() + idx.row(), idx.column(), idx.internalPointer());
}
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override
{
if (row < 0 || row >= rowCount()) {
return QModelIndex();
}
if (row < mFrontItems.count()) {
return createIndex(row, column, mFrontItems[row]);
} else if (row >= mFrontItems.count() + QSortFilterProxyModel::rowCount()) {
return createIndex(row, column, mBackItems[row - mFrontItems.count() - QSortFilterProxyModel::rowCount()]);
} else {
const QModelIndex mi = QSortFilterProxyModel::index(row - mFrontItems.count(), column, parent);
return createIndex(row, column, mi.internalPointer());
}
}
Qt::ItemFlags flags(const QModelIndex &index) const override
{
Q_UNUSED(index);
return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemNeverHasChildren;
}
QModelIndex parent(const QModelIndex &) const override
{
// Flat list
return QModelIndex();
}
QVariant data(const QModelIndex &index, int role) const override
{
if (!index.isValid()) {
return QVariant();
}
if (isCustomItem(index.row())) {
Q_ASSERT(!mFrontItems.isEmpty() || !mBackItems.isEmpty());
CustomItem *ci = static_cast<CustomItem*>(index.internalPointer());
switch (role) {
case Qt::DisplayRole:
return ci->text;
case Qt::DecorationRole:
return ci->icon;
case Qt::UserRole:
return ci->data;
default:
return QVariant();
}
}
const auto key = QSortFilterProxyModel::data(index, Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
Q_ASSERT(!key.isNull());
if (key.isNull()) {
return QVariant();
}
switch (role) {
case Qt::DisplayRole: {
const auto userID = key.userID(0);
QString name, email;
if (key.protocol() == GpgME::OpenPGP) {
name = QString::fromUtf8(userID.name());
email = QString::fromUtf8(userID.email());
} else {
const Kleo::DN dn(userID.id());
name = dn[QStringLiteral("CN")];
email = dn[QStringLiteral("EMAIL")];
}
return i18nc("Name <email> (validity, type, created: date)", "%1 (%2, %3 created: %4)",
email.isEmpty() ? name : name.isEmpty() ? email : i18nc("Name <email>", "%1 <%2>", name, email),
Kleo::Formatting::complianceStringShort(key),
Kleo::KeyCache::instance()->pgpOnly() ? QString() :
key.protocol() == GpgME::OpenPGP ? i18n("OpenPGP") + QLatin1Char(',') : i18n("S/MIME") + QLatin1Char(','),
Kleo::Formatting::creationDateString(key));
}
case Qt::ToolTipRole:
return Kleo::Formatting::toolTip(key, Kleo::Formatting::Validity |
Kleo::Formatting::Issuer |
Kleo::Formatting::Subject |
Kleo::Formatting::Fingerprint |
Kleo::Formatting::ExpiryDates |
Kleo::Formatting::UserIDs);
case Qt::DecorationRole:
return Kleo::Formatting::iconForUid(key.userID(0));
default:
return QSortFilterProxyModel::data(index, role);
}
}
private:
QVector<CustomItem*> mFrontItems;
QVector<CustomItem*> mBackItems;
};
} // anonymous namespace
namespace Kleo
{
class KeySelectionComboPrivate
{
public:
KeySelectionComboPrivate(KeySelectionCombo *parent)
: wasEnabled(true)
, q(parent)
{
}
Kleo::AbstractKeyListModel *model = nullptr;
Kleo::KeyListSortFilterProxyModel *sortFilterProxy = nullptr;
ProxyModel *proxyModel = nullptr;
std::shared_ptr<Kleo::KeyCache> cache;
QString defaultKey;
bool wasEnabled = false;
+ bool useWasEnabled = false;
bool secretOnly;
private:
KeySelectionCombo * const q;
};
}
using namespace Kleo;
KeySelectionCombo::KeySelectionCombo(QWidget* parent)
: KeySelectionCombo(true, parent)
{}
KeySelectionCombo::KeySelectionCombo(bool secretOnly, QWidget* parent)
: QComboBox(parent)
, d(new KeySelectionComboPrivate(this))
{
d->model = Kleo::AbstractKeyListModel::createFlatKeyListModel(this);
d->secretOnly = secretOnly;
d->sortFilterProxy = new Kleo::KeyListSortFilterProxyModel(this);
d->sortFilterProxy->setSourceModel(d->model);
d->proxyModel = new ProxyModel(this);
d->proxyModel->setSourceModel(d->sortFilterProxy);
setModel(d->proxyModel);
connect(this, static_cast<void(KeySelectionCombo::*)(int)>(&KeySelectionCombo::currentIndexChanged),
this, [this](int row) {
if (row >= 0 && row < d->proxyModel->rowCount()) {
if (d->proxyModel->isCustomItem(row)) {
Q_EMIT customItemSelected(d->proxyModel->index(row, 0).data(Qt::UserRole));
} else {
Q_EMIT currentKeyChanged(currentKey());
}
}
});
d->cache = Kleo::KeyCache::mutableInstance();
QTimer::singleShot(0, this, &KeySelectionCombo::init);
}
KeySelectionCombo::~KeySelectionCombo()
{
delete d;
}
void KeySelectionCombo::init()
{
connect(d->cache.get(), &Kleo::KeyCache::keyListingDone,
this, [this]() {
// Set useKeyCache ensures that the cache is populated
// so this can be a blocking call if the cache is not initalized
d->model->useKeyCache(true, d->secretOnly);
d->proxyModel->removeCustomItem(QStringLiteral("-libkleo-loading-keys"));
- setEnabled(d->wasEnabled);
+
+ // We use the useWasEnabled state variable to decide if we should
+ // change the enable / disable state based on the keylist done signal.
+ // If we triggered the refresh useWasEnabled is true and we want to
+ // enable / disable again after our refresh, as the refresh disabled it.
+ //
+ // But if a keyListingDone signal comes from just a generic refresh
+ // triggered by someone else we don't want to change the enable / disable
+ // state.
+ if (d->useWasEnabled) {
+ setEnabled(d->wasEnabled);
+ d->useWasEnabled = false;
+ }
Q_EMIT keyListingFinished();
});
connect(this, &KeySelectionCombo::keyListingFinished, this, [this]() { setCurrentKey(d->defaultKey); });
if (!d->cache->initialized()) {
refreshKeys();
} else {
d->model->useKeyCache(true, d->secretOnly);
Q_EMIT keyListingFinished();
}
connect(this, QOverload<int>::of(&QComboBox::currentIndexChanged), this, [this] () {
setToolTip(currentData(Qt::ToolTipRole).toString());
});
}
void KeySelectionCombo::setKeyFilter(const std::shared_ptr<const KeyFilter> &kf)
{
d->sortFilterProxy->setKeyFilter(kf);
d->proxyModel->sort(0);
setCurrentKey(d->defaultKey);
}
std::shared_ptr<const KeyFilter> KeySelectionCombo::keyFilter() const
{
return d->sortFilterProxy->keyFilter();
}
void KeySelectionCombo::setIdFilter(const QString &id)
{
d->sortFilterProxy->setFilterRegExp(id);
setCurrentKey(d->defaultKey);
}
QString KeySelectionCombo::idFilter() const
{
return d->sortFilterProxy->filterRegExp().pattern();
}
GpgME::Key Kleo::KeySelectionCombo::currentKey() const
{
return currentData(Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
}
void Kleo::KeySelectionCombo::setCurrentKey(const GpgME::Key &key)
{
const int idx = findData(QVariant::fromValue(key), Kleo::KeyListModelInterface::KeyRole, Qt::MatchExactly);
if (idx > -1) {
setCurrentIndex(idx);
}
setToolTip(currentData(Qt::ToolTipRole).toString());
}
void Kleo::KeySelectionCombo::setCurrentKey(const QString &fingerprint)
{
for (int i = 0; i < d->proxyModel->rowCount(); ++i) {
const auto idx = d->proxyModel->index(i, 0, QModelIndex());
const auto key = d->proxyModel->data(idx, Kleo::KeyListModelInterface::KeyRole).value<GpgME::Key>();
if (!key.isNull() && fingerprint == QString::fromLatin1(key.primaryFingerprint())) {
setCurrentIndex(i);
setToolTip(currentData(Qt::ToolTipRole).toString());
return;
}
}
setCurrentIndex(0);
setToolTip(currentData(Qt::ToolTipRole).toString());
}
void KeySelectionCombo::refreshKeys()
{
d->wasEnabled = isEnabled();
+ d->useWasEnabled = true;
setEnabled(false);
const bool wasBlocked = blockSignals(true);
prependCustomItem(QIcon(), i18n("Loading keys ..."), QStringLiteral("-libkleo-loading-keys"));
setCurrentIndex(0);
blockSignals(wasBlocked);
d->cache->startKeyListing();
}
void KeySelectionCombo::appendCustomItem(const QIcon &icon, const QString &text, const QVariant &data)
{
d->proxyModel->appendItem(icon, text, data);
}
void KeySelectionCombo::prependCustomItem(const QIcon &icon, const QString &text, const QVariant &data)
{
d->proxyModel->prependItem(icon, text, data);
}
void Kleo::KeySelectionCombo::setDefaultKey(const QString &fingerprint)
{
d->defaultKey = fingerprint;
setCurrentKey(d->defaultKey);
}
QString Kleo::KeySelectionCombo::defaultKey() const
{
return d->defaultKey;
}
#include "keyselectioncombo.moc"
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Sun, Jan 18, 4:04 PM (2 h, 3 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
4d/06/622fd196d6595ac48b75b8ff9689
Attached To
rLIBKLEO Libkleo
Event Timeline
Log In to Comment