Page Menu
Home
GnuPG
Search
Configure Global Search
Log In
Files
F25703670
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Size
57 KB
Subscribers
None
View Options
diff --git a/kleopatraapplication.cpp b/kleopatraapplication.cpp
index aa1adc103..0d722e895 100644
--- a/kleopatraapplication.cpp
+++ b/kleopatraapplication.cpp
@@ -1,541 +1,541 @@
/*
kleopatraapplication.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2008 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
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "kleopatraapplication.h"
#include "mainwindow.h"
#include "kleopatra_options.h"
#include "systrayicon.h"
#include <smartcard/readerstatus.h>
#include <conf/configuredialog.h>
#include <utils/gnupg-helper.h>
#include <utils/filesystemwatcher.h>
#include <utils/kdpipeiodevice.h>
#include <utils/log.h>
#include <utils/getpid.h>
#include <gpgme++/key.h>
#include <models/keycache.h>
#ifdef HAVE_USABLE_ASSUAN
# include <uiserver/uiserver.h>
#endif
#include <commands/signencryptfilescommand.h>
#include <commands/decryptverifyfilescommand.h>
#include <commands/lookupcertificatescommand.h>
#include <commands/detailscommand.h>
#include <KIconLoader>
#include <KLocalizedString>
#include "kleopatra_debug.h"
#include <KMessageBox>
#include <KWindowSystem>
#include <QUrl>
#include <QFile>
#include <QDir>
#include <QPointer>
#include <QCommandLineOption>
#include <boost/shared_ptr.hpp>
#include <memory>
#include <KSharedConfig>
using namespace Kleo;
using namespace Kleo::Commands;
using namespace boost;
static void add_resources()
{
KIconLoader::global()->addAppDir(QStringLiteral("libkleopatra"));
KIconLoader::global()->addAppDir(QStringLiteral("kwatchgnupg"));
}
static QList<QByteArray> default_logging_options()
{
QList<QByteArray> result;
result.push_back("io");
return result;
}
class KleopatraApplication::Private
{
friend class ::KleopatraApplication;
KleopatraApplication *const q;
public:
explicit Private(KleopatraApplication *qq)
: q(qq),
ignoreNewInstance(true)
{
KDAB_SET_OBJECT_NAME(readerStatus);
#ifndef QT_NO_SYSTEMTRAYICON
KDAB_SET_OBJECT_NAME(sysTray);
sysTray.setAnyCardHasNullPin(readerStatus.anyCardHasNullPin());
sysTray.setAnyCardCanLearnKeys(readerStatus.anyCardCanLearnKeys());
connect(&readerStatus, &SmartCard::ReaderStatus::anyCardHasNullPinChanged,
&sysTray, &SysTrayIcon::setAnyCardHasNullPin);
connect(&readerStatus, &SmartCard::ReaderStatus::anyCardCanLearnKeysChanged,
&sysTray, &SysTrayIcon::setAnyCardCanLearnKeys);
#endif
}
private:
void connectConfigureDialog()
{
if (configureDialog && q->mainWindow()) {
connect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted()));
}
}
void disconnectConfigureDialog()
{
if (configureDialog && q->mainWindow()) {
disconnect(configureDialog, SIGNAL(configCommitted()), q->mainWindow(), SLOT(slotConfigCommitted()));
}
}
public:
bool ignoreNewInstance;
QPointer<ConfigureDialog> configureDialog;
QPointer<MainWindow> mainWindow;
SmartCard::ReaderStatus readerStatus;
#ifndef QT_NO_SYSTEMTRAYICON
SysTrayIcon sysTray;
#endif
shared_ptr<KeyCache> keyCache;
shared_ptr<Log> log;
shared_ptr<FileSystemWatcher> watcher;
public:
void setupKeyCache()
{
keyCache = KeyCache::mutableInstance();
watcher.reset(new FileSystemWatcher);
watcher->blacklistFiles(gnupgFileBlacklist());
watcher->addPath(gnupgHomeDirectory());
watcher->setDelay(1000);
keyCache->addFileSystemWatcher(watcher);
}
void setupLogging()
{
log = Log::mutableInstance();
const QByteArray envOptions = qgetenv("KLEOPATRA_LOGOPTIONS");
const bool logAll = envOptions.trimmed() == "all";
const QList<QByteArray> options = envOptions.isEmpty() ? default_logging_options() : envOptions.split(',') ;
const QByteArray dirNative = qgetenv("KLEOPATRA_LOGDIR");
if (dirNative.isEmpty()) {
return;
}
const QString dir = QFile::decodeName(dirNative);
const QString logFileName = QDir(dir).absoluteFilePath(QStringLiteral("kleopatra.log.%1").arg(mygetpid()));
std::unique_ptr<QFile> logFile(new QFile(logFileName));
if (!logFile->open(QIODevice::WriteOnly | QIODevice::Append)) {
qCDebug(KLEOPATRA_LOG) << "Could not open file for logging: " << logFileName << "\nLogging disabled";
return;
}
log->setOutputDirectory(dir);
if (logAll || options.contains("io")) {
log->setIOLoggingEnabled(true);
}
qInstallMsgHandler(Log::messageHandler);
#ifdef HAVE_USABLE_ASSUAN
if (logAll || options.contains("pipeio")) {
KDPipeIODevice::setDebugLevel(KDPipeIODevice::Debug);
}
UiServer::setLogStream(log->logFile());
#endif
}
};
KleopatraApplication::KleopatraApplication(int &argc, char *argv[])
: QApplication(argc, argv), d(new Private(this))
{
add_resources();
d->setupKeyCache();
d->setupLogging();
#ifndef QT_NO_SYSTEMTRAYICON
d->sysTray.show();
#endif
setQuitOnLastWindowClosed(false);
KWindowSystem::allowExternalProcessWindowActivation();
}
KleopatraApplication::~KleopatraApplication()
{
// work around kdelibs bug https://bugs.kde.org/show_bug.cgi?id=162514
KSharedConfig::openConfig()->sync();
}
namespace
{
typedef void (KleopatraApplication::*Func)(const QStringList &, GpgME::Protocol);
}
void KleopatraApplication::slotActivateRequested(const QStringList& arguments,
const QString &workingDirectory)
{
QCommandLineParser parser;
kleopatra_options(&parser);
QString err;
if (!arguments.isEmpty() && !parser.parse(arguments)) {
err = parser.errorText();
} else if (arguments.isEmpty()) {
// KDBusServices omits the application name if no other
// arguments are provided. In that case the parser prints
// a warning.
parser.parse(QStringList() << QCoreApplication::applicationFilePath());
}
if (err.isEmpty()) {
err = newInstance(parser, workingDirectory);
}
if (!err.isEmpty()) {
KMessageBox::sorry(NULL, err.toHtmlEscaped(), i18n("Failed to execute command"));
emit setExitValue(1);
}
}
QString KleopatraApplication::newInstance(const QCommandLineParser& parser,
const QString &workingDirectory)
{
if (d->ignoreNewInstance) {
qCDebug(KLEOPATRA_LOG) << "New instance ignored because of ignoreNewInstance";
return QString();
}
QStringList files;
const QDir cwd = QDir(workingDirectory);
Q_FOREACH (const QString& file, parser.positionalArguments()) {
// We do not check that file exists here. Better handle
// these errors in the UI.
if (QFileInfo(file).isAbsolute()) {
files << file;
} else {
files << cwd.absoluteFilePath(file);
}
}
GpgME::Protocol protocol = GpgME::UnknownProtocol;
if (parser.isSet(QStringLiteral("openpgp"))) {
qCDebug(KLEOPATRA_LOG) << "found OpenPGP";
protocol = GpgME::OpenPGP;
}
if (parser.isSet(QStringLiteral("cms"))) {
qCDebug(KLEOPATRA_LOG) << "found CMS";
if (protocol == GpgME::OpenPGP) {
- return i18n("Ambigious protocol: --openpgp and --cms");
+ return i18n("Ambiguous protocol: --openpgp and --cms");
}
protocol = GpgME::CMS;
}
// Check for --query command
if (parser.isSet(QStringLiteral("query"))) {
const QString fingerPrint = parser.value(QStringLiteral("query"));
if (fingerPrint.isEmpty()) {
return i18n("No fingerprint argument specified for --query");
}
// Check for Parent Window id
WId parentId = 0;
if (parser.isSet(QStringLiteral("parent-windowid"))) {
#ifdef Q_OS_WIN
// WId is not a portable type as it is a pointer type on Windows.
// casting it from an integer is ok though as the values are guranteed to
// be compatible in the documentation.
parentId = reinterpret_cast<WId>(parser.value(QStringLiteral("parent-windowid")).toUInt());
#else
parentId = parser.value(QStringLiteral("parent-windowid")).toUInt();
#endif
}
// Search for local keys
const GpgME::Key &key = Kleo::KeyCache::instance()->findByKeyIDOrFingerprint(fingerPrint.toLocal8Bit().data());
if (key.isNull()) {
// Show external search dialog
LookupCertificatesCommand *const cmd = new LookupCertificatesCommand(fingerPrint, 0);
if (parentId != 0) {
cmd->setParentWId(parentId);
};
cmd->start();
} else {
// show local detail
DetailsCommand *const cmd = new DetailsCommand(key, 0);
if (parentId != 0) {
cmd->setParentWId(parentId);
};
cmd->start();
}
return QString();
}
static const QMap<QString, Func> funcMap {
{ QStringLiteral("import-certificate"), &KleopatraApplication::importCertificatesFromFile },
{ QStringLiteral("encrypt"), &KleopatraApplication::encryptFiles },
{ QStringLiteral("sign"), &KleopatraApplication::signFiles },
{ QStringLiteral("encrypt-sign"), &KleopatraApplication::signEncryptFiles },
{ QStringLiteral("sign-encrypt"), &KleopatraApplication::signEncryptFiles },
{ QStringLiteral("decrypt"), &KleopatraApplication::decryptFiles },
{ QStringLiteral("verify"), &KleopatraApplication::verifyFiles },
{ QStringLiteral("decrypt-verify"), &KleopatraApplication::decryptVerifyFiles }
};
QString found;
Q_FOREACH (const QString &opt, funcMap.keys()) {
if (parser.isSet(opt) && found.isEmpty()) {
found = opt;
} else if (parser.isSet(opt)) {
return i18n("Ambiguous commands \"%1\" and \"%2\"", found, opt);
}
}
if (!found.isEmpty()) {
if (files.empty()) {
- return i18n("No files specificed for \"%1\" command", found);
+ return i18n("No files specified for \"%1\" command", found);
}
qCDebug(KLEOPATRA_LOG) << "found" << found;
(this->*funcMap.value(found))(files, protocol);
} else {
if (files.empty()) {
if (!isSessionRestored()) {
qCDebug(KLEOPATRA_LOG) << "openOrRaiseMainWindow";
openOrRaiseMainWindow();
}
} else {
return i18n("No command provided but arguments present");
}
}
return QString();
}
#ifndef QT_NO_SYSTEMTRAYICON
const SysTrayIcon *KleopatraApplication::sysTrayIcon() const
{
return &d->sysTray;
}
SysTrayIcon *KleopatraApplication::sysTrayIcon()
{
return &d->sysTray;
}
#endif
const MainWindow *KleopatraApplication::mainWindow() const
{
return d->mainWindow;
}
MainWindow *KleopatraApplication::mainWindow()
{
return d->mainWindow;
}
void KleopatraApplication::setMainWindow(MainWindow *mainWindow)
{
if (mainWindow == d->mainWindow) {
return;
}
d->disconnectConfigureDialog();
d->mainWindow = mainWindow;
#ifndef QT_NO_SYSTEMTRAYICON
d->sysTray.setMainWindow(mainWindow);
#endif
d->connectConfigureDialog();
}
static void open_or_raise(QWidget *w)
{
if (w->isMinimized()) {
KWindowSystem::unminimizeWindow(w->winId());
w->raise();
} else if (w->isVisible()) {
w->raise();
} else {
w->show();
}
}
void KleopatraApplication::toggleMainWindowVisibility()
{
if (mainWindow()) {
mainWindow()->setVisible(!mainWindow()->isVisible());
} else {
openOrRaiseMainWindow();
}
}
void KleopatraApplication::restoreMainWindow()
{
qCDebug(KLEOPATRA_LOG) << "restoring main window";
// Sanity checks
if (!isSessionRestored()) {
qCDebug(KLEOPATRA_LOG) << "Not in session restore";
return;
}
if (mainWindow()) {
qCDebug(KLEOPATRA_LOG) << "Already have main window";
return;
}
MainWindow *mw = new MainWindow;
if (KMainWindow::canBeRestored(1)) {
// restore to hidden state, Mainwindow::readProperties() will
// restore saved visibility.
mw->restore(1, false);
}
mw->setAttribute(Qt::WA_DeleteOnClose);
setMainWindow(mw);
d->connectConfigureDialog();
}
void KleopatraApplication::openOrRaiseMainWindow()
{
MainWindow *mw = mainWindow();
if (!mw) {
mw = new MainWindow;
mw->setAttribute(Qt::WA_DeleteOnClose);
setMainWindow(mw);
d->connectConfigureDialog();
}
open_or_raise(mw);
}
void KleopatraApplication::openOrRaiseConfigDialog()
{
if (!d->configureDialog) {
d->configureDialog = new ConfigureDialog;
d->configureDialog->setAttribute(Qt::WA_DeleteOnClose);
d->connectConfigureDialog();
}
open_or_raise(d->configureDialog);
}
#ifndef QT_NO_SYSTEMTRAYICON
void KleopatraApplication::startMonitoringSmartCard()
{
d->readerStatus.startMonitoring();
}
#endif // QT_NO_SYSTEMTRAYICON
void KleopatraApplication::importCertificatesFromFile(const QStringList &files, GpgME::Protocol /*proto*/)
{
openOrRaiseMainWindow();
if (!files.empty()) {
mainWindow()->importCertificatesFromFile(files);
}
}
void KleopatraApplication::encryptFiles(const QStringList &files, GpgME::Protocol proto)
{
SignEncryptFilesCommand *const cmd = new SignEncryptFilesCommand(files, 0);
cmd->setEncryptionPolicy(Force);
cmd->setSigningPolicy(Allow);
if (proto != GpgME::UnknownProtocol) {
cmd->setProtocol(proto);
}
cmd->start();
}
void KleopatraApplication::signFiles(const QStringList &files, GpgME::Protocol proto)
{
SignEncryptFilesCommand *const cmd = new SignEncryptFilesCommand(files, 0);
cmd->setSigningPolicy(Force);
cmd->setEncryptionPolicy(Deny);
if (proto != GpgME::UnknownProtocol) {
cmd->setProtocol(proto);
}
cmd->start();
}
void KleopatraApplication::signEncryptFiles(const QStringList &files, GpgME::Protocol proto)
{
SignEncryptFilesCommand *const cmd = new SignEncryptFilesCommand(files, 0);
if (proto != GpgME::UnknownProtocol) {
cmd->setProtocol(proto);
}
cmd->start();
}
void KleopatraApplication::decryptFiles(const QStringList &files, GpgME::Protocol /*proto*/)
{
DecryptVerifyFilesCommand *const cmd = new DecryptVerifyFilesCommand(files, 0);
cmd->setOperation(Decrypt);
cmd->start();
}
void KleopatraApplication::verifyFiles(const QStringList &files, GpgME::Protocol /*proto*/)
{
DecryptVerifyFilesCommand *const cmd = new DecryptVerifyFilesCommand(files, 0);
cmd->setOperation(Verify);
cmd->start();
}
void KleopatraApplication::decryptVerifyFiles(const QStringList &files, GpgME::Protocol /*proto*/)
{
DecryptVerifyFilesCommand *const cmd = new DecryptVerifyFilesCommand(files, 0);
cmd->start();
}
void KleopatraApplication::setIgnoreNewInstance(bool ignore)
{
d->ignoreNewInstance = ignore;
}
bool KleopatraApplication::ignoreNewInstance() const
{
return d->ignoreNewInstance;
}
diff --git a/models/keylistmodel.cpp b/models/keylistmodel.cpp
index efc43214f..c7783eb7e 100644
--- a/models/keylistmodel.cpp
+++ b/models/keylistmodel.cpp
@@ -1,1008 +1,1008 @@
/* -*- mode: c++; c-basic-offset:4 -*-
models/keylistmodel.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2007 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
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "keylistmodel.h"
#include "predicates.h"
#ifdef KLEO_MODEL_TEST
# include "modeltest.h"
#endif
#include <utils/formatting.h>
#include <kleo/keyfiltermanager.h>
#include <kleo/keyfilter.h>
#include <KLocalizedString>
#include <QFont>
#include <QColor>
#include <QHash>
#include <QIcon>
#include <QDate>
#include <gpgme++/key.h>
#ifndef Q_MOC_RUN // QTBUG-22829
#include <boost/bind.hpp>
#include <boost/graph/topological_sort.hpp>
#include <boost/graph/adjacency_list.hpp>
#endif
#include <algorithm>
#include <vector>
#include <map>
#include <set>
#include <iterator>
#include <cassert>
#ifdef __GLIBCXX__
#include <ext/algorithm> // for is_sorted
#endif
using namespace GpgME;
using namespace Kleo;
/****************************************************************************
**
** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies).
** Contact: Qt Software Information (qt-info@nokia.com)
**
** This file is part of the QtCore module of the Qt Toolkit.
**
** Commercial Usage
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Nokia.
**
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License versions 2.0 or 3.0 as published by the Free
** Software Foundation and appearing in the file LICENSE.GPL included in
** the packaging of this file. Please review the following information
** to ensure GNU General Public Licensing requirements will be met:
** http://www.fsf.org/licensing/licenses/info/GPLv2.html and
** http://www.gnu.org/copyleft/gpl.html. In addition, as a special
** exception, Nokia gives you certain additional rights. These rights
** are described in the Nokia Qt GPL Exception version 1.3, included in
** the file GPL_EXCEPTION.txt in this package.
**
** Qt for Windows(R) Licensees
** As a special exception, Nokia, as the sole copyright holder for Qt
** Designer, grants users of the Qt/Eclipse Integration plug-in the
** right for the Qt/Eclipse Integration to link to functionality
** provided by Qt Designer and its related libraries.
**
** If you are unsure which license is appropriate for your use, please
** contact the sales department at qt-sales@nokia.com.
**
****************************************************************************/
/*
These functions are based on Peter J. Weinberger's hash function
(from the Dragon Book). The constant 24 in the original function
was replaced with 23 to produce fewer collisions on input such as
"a", "aa", "aaa", "aaaa", ...
*/
// adjustment to null-terminated strings
// (c) 2008 Klarälvdalens Datakonsult AB
static uint hash(const uchar *p)
{
uint h = 0;
uint g;
while (*p) {
h = (h << 4) + *p++;
if ((g = (h & 0xf0000000)) != 0) {
h ^= g >> 23;
}
h &= ~g;
}
return h;
}
//
// end Nokia-copyrighted code
//
static inline uint qHash(const char *data)
{
if (!data) {
return 1; // something != 0
}
return ::hash(reinterpret_cast<const uchar *>(data));
}
class AbstractKeyListModel::Private
{
public:
Private() : m_toolTipOptions(Formatting::Validity) {}
int m_toolTipOptions;
mutable QHash<const char *, QVariant> prettyEMailCache;
};
AbstractKeyListModel::AbstractKeyListModel(QObject *p)
: QAbstractItemModel(p), KeyListModelInterface(), d(new Private)
{
}
AbstractKeyListModel::~AbstractKeyListModel() {}
void AbstractKeyListModel::setToolTipOptions(int opts)
{
d->m_toolTipOptions = opts;
}
int AbstractKeyListModel::toolTipOptions() const
{
return d->m_toolTipOptions;
}
Key AbstractKeyListModel::key(const QModelIndex &idx) const
{
if (idx.isValid()) {
return doMapToKey(idx);
} else {
return Key::null;
}
}
std::vector<Key> AbstractKeyListModel::keys(const QList<QModelIndex> &indexes) const
{
std::vector<Key> result;
result.reserve(indexes.size());
std::transform(indexes.begin(), indexes.end(),
std::back_inserter(result),
boost::bind(&AbstractKeyListModel::key, this, _1));
result.erase(std::unique(result.begin(), result.end(), _detail::ByFingerprint<std::equal_to>()), result.end());
return result;
}
QModelIndex AbstractKeyListModel::index(const Key &key, int col) const
{
if (key.isNull() || col < 0 || col >= NumColumns) {
return QModelIndex();
} else {
return doMapFromKey(key, col);
}
}
QList<QModelIndex> AbstractKeyListModel::indexes(const std::vector<Key> &keys) const
{
QList<QModelIndex> result;
std::transform(keys.begin(), keys.end(),
std::back_inserter(result),
- // if some compilers are complaining about ambigious overloads, use this line instead:
+ // if some compilers are complaining about ambiguous overloads, use this line instead:
//bind( static_cast<QModelIndex(AbstractKeyListModel::*)(const Key&,int)const>( &AbstractKeyListModel::index ), this, _1, 0 ) );
boost::bind(&AbstractKeyListModel::index, this, _1, 0));
return result;
}
void AbstractKeyListModel::setKeys(const std::vector<Key> &keys)
{
clear();
addKeys(keys);
}
QModelIndex AbstractKeyListModel::addKey(const Key &key)
{
const std::vector<Key> vec(1, key);
const QList<QModelIndex> l = doAddKeys(vec);
return l.empty() ? QModelIndex() : l.front() ;
}
void AbstractKeyListModel::removeKey(const Key &key)
{
if (key.isNull()) {
return;
}
doRemoveKey(key);
d->prettyEMailCache.remove(key.primaryFingerprint());
}
QList<QModelIndex> AbstractKeyListModel::addKeys(const std::vector<Key> &keys)
{
std::vector<Key> sorted;
sorted.reserve(keys.size());
std::remove_copy_if(keys.begin(), keys.end(),
std::back_inserter(sorted),
boost::bind(&Key::isNull, _1));
std::sort(sorted.begin(), sorted.end(), _detail::ByFingerprint<std::less>());
return doAddKeys(sorted);
}
void AbstractKeyListModel::clear()
{
doClear();
d->prettyEMailCache.clear();
reset();
}
int AbstractKeyListModel::columnCount(const QModelIndex &) const
{
return NumColumns;
}
QVariant AbstractKeyListModel::headerData(int section, Qt::Orientation o, int role) const
{
if (o == Qt::Horizontal)
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole)
switch (section) {
case PrettyName: return i18n("Name");
case PrettyEMail: return i18n("E-Mail");
case ValidFrom: return i18n("Valid From");
case ValidUntil: return i18n("Valid Until");
case TechnicalDetails: return i18n("Details");
case ShortKeyID: return i18n("Key-ID");
case NumColumns: ;
}
return QVariant();
}
static QVariant returnIfValid(const QColor &t)
{
if (t.isValid()) {
return t;
} else {
return QVariant();
}
}
static QVariant returnIfValid(const QIcon &t)
{
if (!t.isNull()) {
return t;
} else {
return QVariant();
}
}
QVariant AbstractKeyListModel::data(const QModelIndex &index, int role) const
{
const Key key = this->key(index);
if (key.isNull()) {
return QVariant();
}
const int column = index.column();
if (role == Qt::DisplayRole || role == Qt::EditRole)
switch (column) {
case PrettyName:
return Formatting::prettyName(key);
case PrettyEMail:
if (const char *const fpr = key.primaryFingerprint()) {
const QHash<const char *, QVariant>::const_iterator it = d->prettyEMailCache.constFind(fpr);
if (it != d->prettyEMailCache.constEnd()) {
return *it;
} else {
return d->prettyEMailCache[fpr] = Formatting::prettyEMail(key);
}
} else {
return QVariant();
}
case ValidFrom:
if (role == Qt::EditRole) {
return Formatting::creationDate(key);
} else {
return Formatting::creationDateString(key);
}
case ValidUntil:
if (role == Qt::EditRole) {
return Formatting::expirationDate(key);
} else {
return Formatting::expirationDateString(key);
}
case TechnicalDetails:
return Formatting::type(key);
case ShortKeyID:
return QString::fromLatin1(key.shortKeyID());
case NumColumns:
break;
}
else if (role == Qt::ToolTipRole) {
return Formatting::toolTip(key, toolTipOptions());
} else if (role == Qt::FontRole) {
return KeyFilterManager::instance()->font(key, (column == ShortKeyID) ? QFont(QStringLiteral("courier")) : QFont());
} else if (role == Qt::DecorationRole) {
return column == Icon ? returnIfValid(KeyFilterManager::instance()->icon(key)) : QVariant() ;
} else if (role == Qt::BackgroundRole) {
return returnIfValid(KeyFilterManager::instance()->bgColor(key));
} else if (role == Qt::ForegroundRole) {
return returnIfValid(KeyFilterManager::instance()->fgColor(key));
} else if (role == FingerprintRole) {
return QString::fromLatin1(key.primaryFingerprint());
}
return QVariant();
}
namespace
{
template <typename Base>
class TableModelMixin : public Base
{
public:
explicit TableModelMixin(QObject *p = Q_NULLPTR) : Base(p) {}
~TableModelMixin() {}
using Base::index;
QModelIndex index(int row, int column, const QModelIndex &pidx = QModelIndex()) const Q_DECL_OVERRIDE
{
return this->hasIndex(row, column, pidx) ? this->createIndex(row, column, Q_NULLPTR) : QModelIndex() ;
}
private:
QModelIndex parent(const QModelIndex &) const Q_DECL_OVERRIDE
{
return QModelIndex();
}
bool hasChildren(const QModelIndex &pidx) const Q_DECL_OVERRIDE
{
return (pidx.model() == this || !pidx.isValid()) && this->rowCount(pidx) > 0 && this->columnCount(pidx) > 0 ;
}
};
class FlatKeyListModel
#ifndef Q_MOC_RUN
: public TableModelMixin<AbstractKeyListModel>
#else
: public AbstractKeyListModel
#endif
{
Q_OBJECT
public:
explicit FlatKeyListModel(QObject *parent = Q_NULLPTR);
~FlatKeyListModel();
int rowCount(const QModelIndex &pidx) const Q_DECL_OVERRIDE
{
return pidx.isValid() ? 0 : mKeysByFingerprint.size() ;
}
private:
Key doMapToKey(const QModelIndex &index) const Q_DECL_OVERRIDE;
QModelIndex doMapFromKey(const Key &key, int col) const Q_DECL_OVERRIDE;
QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) Q_DECL_OVERRIDE;
void doRemoveKey(const Key &key) Q_DECL_OVERRIDE;
void doClear() Q_DECL_OVERRIDE {
mKeysByFingerprint.clear();
}
private:
std::vector<Key> mKeysByFingerprint;
};
class HierarchicalKeyListModel : public AbstractKeyListModel
{
Q_OBJECT
public:
explicit HierarchicalKeyListModel(QObject *parent = Q_NULLPTR);
~HierarchicalKeyListModel();
int rowCount(const QModelIndex &pidx) const Q_DECL_OVERRIDE;
using AbstractKeyListModel::index;
QModelIndex index(int row, int col, const QModelIndex &pidx) const Q_DECL_OVERRIDE;
QModelIndex parent(const QModelIndex &idx) const Q_DECL_OVERRIDE;
bool hasChildren(const QModelIndex &pidx) const Q_DECL_OVERRIDE
{
return rowCount(pidx) > 0 ;
}
private:
Key doMapToKey(const QModelIndex &index) const Q_DECL_OVERRIDE;
QModelIndex doMapFromKey(const Key &key, int col) const Q_DECL_OVERRIDE;
QList<QModelIndex> doAddKeys(const std::vector<Key> &keys) Q_DECL_OVERRIDE;
void doRemoveKey(const Key &key) Q_DECL_OVERRIDE;
void doClear() Q_DECL_OVERRIDE {
mTopLevels.clear();
mKeysByFingerprint.clear();
mKeysByExistingParent.clear();
mKeysByNonExistingParent.clear();
}
private:
void addTopLevelKey(const Key &key);
void addKeyWithParent(const char *issuer_fpr, const Key &key);
void addKeyWithoutParent(const char *issuer_fpr, const Key &key);
private:
typedef std::map< std::string, std::vector<Key> > Map;
std::vector<Key> mKeysByFingerprint; // all keys
Map mKeysByExistingParent, mKeysByNonExistingParent; // parent->child map
std::vector<Key> mTopLevels; // all roots + parent-less
};
static const char *cleanChainID(const Key &key)
{
if (key.isRoot()) {
return "";
}
if (const char *chid = key.chainID()) {
return chid;
}
return "";
}
}
FlatKeyListModel::FlatKeyListModel(QObject *p)
: TableModelMixin<AbstractKeyListModel>(p),
mKeysByFingerprint()
{
}
FlatKeyListModel::~FlatKeyListModel() {}
Key FlatKeyListModel::doMapToKey(const QModelIndex &idx) const
{
assert(idx.isValid());
if (static_cast<unsigned>(idx.row()) < mKeysByFingerprint.size() && idx.column() < NumColumns) {
return mKeysByFingerprint[ idx.row() ];
} else {
return Key::null;
}
}
QModelIndex FlatKeyListModel::doMapFromKey(const Key &key, int col) const
{
assert(!key.isNull());
const std::vector<Key>::const_iterator it
= std::lower_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
key, _detail::ByFingerprint<std::less>());
if (it == mKeysByFingerprint.end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
return QModelIndex();
} else {
return createIndex(it - mKeysByFingerprint.begin(), col);
}
}
QList<QModelIndex> FlatKeyListModel::doAddKeys(const std::vector<Key> &keys)
{
#ifdef __GLIBCXX__
assert(__gnu_cxx::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
#endif
if (keys.empty()) {
return QList<QModelIndex>();
}
for (std::vector<Key>::const_iterator it = keys.begin(), end = keys.end() ; it != end ; ++it) {
// find an insertion point:
const std::vector<Key>::iterator pos = std::upper_bound(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), *it, _detail::ByFingerprint<std::less>());
const unsigned int idx = std::distance(mKeysByFingerprint.begin(), pos);
if (idx > 0 && qstrcmp(mKeysByFingerprint[idx - 1].primaryFingerprint(), it->primaryFingerprint()) == 0) {
// key existed before - replace with new one:
mKeysByFingerprint[idx - 1] = *it;
Q_EMIT dataChanged(createIndex(idx - 1, 0), createIndex(idx - 1, NumColumns - 1));
} else {
// new key - insert:
beginInsertRows(QModelIndex(), idx, idx);
mKeysByFingerprint.insert(pos, *it);
endInsertRows();
}
}
return indexes(keys);
}
void FlatKeyListModel::doRemoveKey(const Key &key)
{
const std::vector<Key>::iterator it
= qBinaryFind(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
key, _detail::ByFingerprint<std::less>());
if (it == mKeysByFingerprint.end()) {
return;
}
const unsigned int row = std::distance(mKeysByFingerprint.begin(), it);
beginRemoveRows(QModelIndex(), row, row);
mKeysByFingerprint.erase(it);
endRemoveRows();
}
HierarchicalKeyListModel::HierarchicalKeyListModel(QObject *p)
: AbstractKeyListModel(p),
mKeysByFingerprint(),
mKeysByExistingParent(),
mKeysByNonExistingParent(),
mTopLevels()
{
}
HierarchicalKeyListModel::~HierarchicalKeyListModel() {}
int HierarchicalKeyListModel::rowCount(const QModelIndex &pidx) const
{
// toplevel item:
if (!pidx.isValid()) {
return mTopLevels.size();
}
if (pidx.column() != 0) {
return 0;
}
// non-toplevel item - find the number of subjects for this issuer:
const Key issuer = this->key(pidx);
const char *const fpr = issuer.primaryFingerprint();
if (!fpr || !*fpr) {
return 0;
}
const Map::const_iterator it = mKeysByExistingParent.find(fpr);
if (it == mKeysByExistingParent.end()) {
return 0;
}
return it->second.size();
}
QModelIndex HierarchicalKeyListModel::index(int row, int col, const QModelIndex &pidx) const
{
if (row < 0 || col < 0 || col >= NumColumns) {
return QModelIndex();
}
// toplevel item:
if (!pidx.isValid()) {
if (static_cast<unsigned>(row) < mTopLevels.size()) {
return index(mTopLevels[row], col);
} else {
return QModelIndex();
}
}
// non-toplevel item - find the row'th subject of this key:
const Key issuer = this->key(pidx);
const char *const fpr = issuer.primaryFingerprint();
if (!fpr || !*fpr) {
return QModelIndex();
}
const Map::const_iterator it = mKeysByExistingParent.find(fpr);
if (it == mKeysByExistingParent.end() || static_cast<unsigned>(row) >= it->second.size()) {
return QModelIndex();
}
return index(it->second[row], col);
}
QModelIndex HierarchicalKeyListModel::parent(const QModelIndex &idx) const
{
const Key key = this->key(idx);
if (key.isNull() || key.isRoot()) {
return QModelIndex();
}
const std::vector<Key>::const_iterator it
= qBinaryFind(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
cleanChainID(key), _detail::ByFingerprint<std::less>());
return it != mKeysByFingerprint.end() ? index(*it) : QModelIndex();
}
Key HierarchicalKeyListModel::doMapToKey(const QModelIndex &idx) const
{
if (!idx.isValid()) {
return Key::null;
}
const char *const issuer_fpr = static_cast<const char *>(idx.internalPointer());
if (!issuer_fpr || !*issuer_fpr) {
// top-level:
if (static_cast<unsigned>(idx.row()) >= mTopLevels.size()) {
return Key::null;
} else {
return mTopLevels[idx.row()];
}
}
// non-toplevel:
const Map::const_iterator it
= mKeysByExistingParent.find(issuer_fpr);
if (it == mKeysByExistingParent.end() || static_cast<unsigned>(idx.row()) >= it->second.size()) {
return Key::null;
}
return it->second[idx.row()];
}
QModelIndex HierarchicalKeyListModel::doMapFromKey(const Key &key, int col) const
{
if (key.isNull()) {
return QModelIndex();
}
const char *issuer_fpr = cleanChainID(key);
// we need to look in the toplevels list,...
const std::vector<Key> *v = &mTopLevels;
if (issuer_fpr && *issuer_fpr) {
const std::map< std::string, std::vector<Key> >::const_iterator it
= mKeysByExistingParent.find(issuer_fpr);
// ...unless we find an existing parent:
if (it != mKeysByExistingParent.end()) {
v = &it->second;
} else {
issuer_fpr = 0; // force internalPointer to zero for toplevels
}
}
const std::vector<Key>::const_iterator it
= std::lower_bound(v->begin(), v->end(), key, _detail::ByFingerprint<std::less>());
if (it == v->end() || !_detail::ByFingerprint<std::equal_to>()(*it, key)) {
return QModelIndex();
}
const unsigned int row = std::distance(v->begin(), it);
return createIndex(row, col, const_cast<char * /* thanks, Trolls :/ */ >(issuer_fpr));
}
void HierarchicalKeyListModel::addKeyWithParent(const char *issuer_fpr, const Key &key)
{
assert(issuer_fpr); assert(*issuer_fpr); assert(!key.isNull());
std::vector<Key> &subjects = mKeysByExistingParent[issuer_fpr];
// find insertion point:
const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
const int row = std::distance(subjects.begin(), it);
if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
// exists -> replace
*it = key;
Q_EMIT dataChanged(createIndex(row, 0, const_cast<char *>(issuer_fpr)), createIndex(row, NumColumns - 1, const_cast<char *>(issuer_fpr)));
} else {
// doesn't exist -> insert
const std::vector<Key>::const_iterator pos = qBinaryFind(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
assert(pos != mKeysByFingerprint.end());
beginInsertRows(index(*pos), row, row);
subjects.insert(it, key);
endInsertRows();
}
}
void HierarchicalKeyListModel::addKeyWithoutParent(const char *issuer_fpr, const Key &key)
{
assert(issuer_fpr); assert(*issuer_fpr); assert(!key.isNull());
std::vector<Key> &subjects = mKeysByNonExistingParent[issuer_fpr];
// find insertion point:
const std::vector<Key>::iterator it = std::lower_bound(subjects.begin(), subjects.end(), key, _detail::ByFingerprint<std::less>());
if (it != subjects.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0)
// exists -> replace
{
*it = key;
} else
// doesn't exist -> insert
{
subjects.insert(it, key);
}
addTopLevelKey(key);
}
void HierarchicalKeyListModel::addTopLevelKey(const Key &key)
{
// find insertion point:
const std::vector<Key>::iterator it = std::lower_bound(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
const int row = std::distance(mTopLevels.begin(), it);
if (it != mTopLevels.end() && qstricmp(it->primaryFingerprint(), key.primaryFingerprint()) == 0) {
// exists -> replace
*it = key;
Q_EMIT dataChanged(createIndex(row, 0), createIndex(row, NumColumns - 1));
} else {
// doesn't exist -> insert
beginInsertRows(QModelIndex(), row, row);
mTopLevels.insert(it, key);
endInsertRows();
}
}
// sorts 'keys' such that parent always come before their children:
static std::vector<Key> topological_sort(const std::vector<Key> &keys)
{
boost::adjacency_list<> graph(keys.size());
// add edges from children to parents:
for (unsigned int i = 0, end = keys.size() ; i != end ; ++i) {
const char *const issuer_fpr = cleanChainID(keys[i]);
if (!issuer_fpr || !*issuer_fpr) {
continue;
}
const std::vector<Key>::const_iterator it
= qBinaryFind(keys.begin(), keys.end(), issuer_fpr, _detail::ByFingerprint<std::less>());
if (it == keys.end()) {
continue;
}
add_edge(i, std::distance(keys.begin(), it), graph);
}
std::vector<int> order;
order.reserve(keys.size());
topological_sort(graph, std::back_inserter(order));
assert(order.size() == keys.size());
std::vector<Key> result;
result.reserve(keys.size());
Q_FOREACH (int i, order) {
result.push_back(keys[i]);
}
return result;
}
QList<QModelIndex> HierarchicalKeyListModel::doAddKeys(const std::vector<Key> &keys)
{
#ifdef __GLIBCXX__
assert(__gnu_cxx::is_sorted(keys.begin(), keys.end(), _detail::ByFingerprint<std::less>()));
#endif
if (keys.empty()) {
return QList<QModelIndex>();
}
const std::vector<Key> oldKeys = mKeysByFingerprint;
std::vector<Key> merged;
merged.reserve(keys.size() + mKeysByFingerprint.size());
std::set_union(keys.begin(), keys.end(),
mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
std::back_inserter(merged), _detail::ByFingerprint<std::less>());
mKeysByFingerprint = merged;
std::set<Key, _detail::ByFingerprint<std::less> > changedParents;
Q_FOREACH (const Key &key, topological_sort(keys)) {
// check to see whether this key is a parent for a previously parent-less group:
const char *const fpr = key.primaryFingerprint();
if (!fpr || !*fpr) {
continue;
}
const bool keyAlreadyExisted = qBinaryFind(oldKeys.begin(), oldKeys.end(), key, _detail::ByFingerprint<std::less>()) != oldKeys.end();
const Map::iterator it = mKeysByNonExistingParent.find(fpr);
const std::vector<Key> children = it != mKeysByNonExistingParent.end() ? it->second : std::vector<Key>();
if (it != mKeysByNonExistingParent.end()) {
mKeysByNonExistingParent.erase(it);
}
// Step 1: For new keys, remove children from toplevel:
if (!keyAlreadyExisted) {
std::vector<Key>::iterator last = mTopLevels.begin();
std::vector<Key>::iterator lastFP = mKeysByFingerprint.begin();
Q_FOREACH (const Key &k, children) {
last = qBinaryFind(last, mTopLevels.end(), k, _detail::ByFingerprint<std::less>());
assert(last != mTopLevels.end());
const int row = std::distance(mTopLevels.begin(), last);
lastFP = qBinaryFind(lastFP, mKeysByFingerprint.end(), k, _detail::ByFingerprint<std::less>());
assert(lastFP != mKeysByFingerprint.end());
Q_EMIT rowAboutToBeMoved(QModelIndex(), row);
beginRemoveRows(QModelIndex(), row, row);
last = mTopLevels.erase(last);
lastFP = mKeysByFingerprint.erase(lastFP);
endRemoveRows();
}
}
// Step 2: add/update key
const char *const issuer_fpr = cleanChainID(key);
if (!issuer_fpr || !*issuer_fpr)
// root or something...
{
addTopLevelKey(key);
} else if (std::binary_search(mKeysByFingerprint.begin(), mKeysByFingerprint.end(), issuer_fpr, _detail::ByFingerprint<std::less>()))
// parent exists...
{
addKeyWithParent(issuer_fpr, key);
} else
// parent does't exist yet...
{
addKeyWithoutParent(issuer_fpr, key);
}
const QModelIndex key_idx = index(key);
QModelIndex key_parent = key_idx.parent();
while (key_parent.isValid()) {
changedParents.insert(doMapToKey(key_parent));
key_parent = key_parent.parent();
}
// Step 3: Add children to new parent ( == key )
if (!keyAlreadyExisted && !children.empty()) {
addKeys(children);
const QModelIndex new_parent = index(key);
// Q_EMIT the rowMoved() signals in reversed direction, so the
// implementation can use a stack for mapping.
for (int i = children.size() - 1 ; i >= 0 ; --i) {
Q_EMIT rowMoved(new_parent, i);
}
}
}
//Q_EMIT dataChanged for all parents with new children. This triggers KeyListSortFilterProxyModel to
//show a parent node if it just got children matching the proxy's filter
Q_FOREACH (const Key &i, changedParents) {
const QModelIndex idx = index(i);
if (idx.isValid()) {
Q_EMIT dataChanged(idx.sibling(idx.row(), 0), idx.sibling(idx.row(), NumColumns - 1));
}
}
return indexes(keys);
}
void HierarchicalKeyListModel::doRemoveKey(const Key &key)
{
const QModelIndex idx = index(key);
if (!idx.isValid()) {
return;
}
const char *const fpr = key.primaryFingerprint();
if (mKeysByExistingParent.find(fpr) != mKeysByExistingParent.end()) {
//handle non-leave nodes:
std::vector<Key> keys = mKeysByFingerprint;
const std::vector<Key>::iterator it = qBinaryFind(keys.begin(), keys.end(),
key, _detail::ByFingerprint<std::less>());
if (it == keys.end()) {
return;
}
keys.erase(it);
// FIXME for simplicity, we just clear the model and re-add all keys minus the removed one. This is suboptimal,
// but acceptable given that deletion of non-leave nodes is rather rare.
clear();
addKeys(keys);
return;
}
//handle leave nodes:
const std::vector<Key>::iterator it = qBinaryFind(mKeysByFingerprint.begin(), mKeysByFingerprint.end(),
key, _detail::ByFingerprint<std::less>());
assert(it != mKeysByFingerprint.end());
assert(mKeysByNonExistingParent.find(fpr) == mKeysByNonExistingParent.end());
assert(mKeysByExistingParent.find(fpr) == mKeysByExistingParent.end());
beginRemoveRows(parent(idx), idx.row(), idx.row());
mKeysByFingerprint.erase(it);
const char *const issuer_fpr = cleanChainID(key);
const std::vector<Key>::iterator tlIt = qBinaryFind(mTopLevels.begin(), mTopLevels.end(), key, _detail::ByFingerprint<std::less>());
if (tlIt != mTopLevels.end()) {
mTopLevels.erase(tlIt);
}
if (issuer_fpr && *issuer_fpr) {
const Map::iterator nexIt = mKeysByNonExistingParent.find(issuer_fpr);
if (nexIt != mKeysByNonExistingParent.end()) {
const std::vector<Key>::iterator eit = qBinaryFind(nexIt->second.begin(), nexIt->second.end(), key, _detail::ByFingerprint<std::less>());
if (eit != nexIt->second.end()) {
nexIt->second.erase(eit);
}
if (nexIt->second.empty()) {
mKeysByNonExistingParent.erase(nexIt);
}
}
const Map::iterator exIt = mKeysByExistingParent.find(issuer_fpr);
if (exIt != mKeysByExistingParent.end()) {
const std::vector<Key>::iterator eit = qBinaryFind(exIt->second.begin(), exIt->second.end(), key, _detail::ByFingerprint<std::less>());
if (eit != exIt->second.end()) {
exIt->second.erase(eit);
}
if (exIt->second.empty()) {
mKeysByExistingParent.erase(exIt);
}
}
}
endRemoveRows();
}
// static
AbstractKeyListModel *AbstractKeyListModel::createFlatKeyListModel(QObject *p)
{
AbstractKeyListModel *const m = new FlatKeyListModel(p);
#ifdef KLEO_MODEL_TEST
new ModelTest(m, p);
#endif
return m;
}
// static
AbstractKeyListModel *AbstractKeyListModel::createHierarchicalKeyListModel(QObject *p)
{
AbstractKeyListModel *const m = new HierarchicalKeyListModel(p);
#ifdef KLEO_MODEL_TEST
new ModelTest(m, p);
#endif
return m;
}
#include "keylistmodel.moc"
/*!
\fn AbstractKeyListModel::rowAboutToBeMoved( const QModelIndex & old_parent, int old_row )
Emitted before the removal of a row from that model. It will later
be added to the model again, in response to which rowMoved() will be
emitted. If multiple rows are moved in one go, multiple
rowAboutToBeMoved() signals are emitted before the corresponding
number of rowMoved() signals is emitted - in reverse order.
This works around the absence of move semantics in
QAbstractItemModel. Clients can maintain a stack to perform the
QModelIndex-mapping themselves, or, e.g., to preserve the selection
status of the row:
\code
std::vector<bool> mMovingRowWasSelected; // transient, used when rows are moved
// ...
void slotRowAboutToBeMoved( const QModelIndex & p, int row ) {
mMovingRowWasSelected.push_back( selectionModel()->isSelected( model()->index( row, 0, p ) ) );
}
void slotRowMoved( const QModelIndex & p, int row ) {
const bool wasSelected = mMovingRowWasSelected.back();
mMovingRowWasSelected.pop_back();
if ( wasSelected )
selectionModel()->select( model()->index( row, 0, p ), Select|Rows );
}
\endcode
A similar mechanism could be used to preserve the current item during moves.
*/
/*!
\fn AbstractKeyListModel::rowMoved( const QModelIndex & new_parent, int new_parent )
See rowAboutToBeMoved()
*/
diff --git a/models/subkeylistmodel.cpp b/models/subkeylistmodel.cpp
index d0d495b7b..f22283897 100644
--- a/models/subkeylistmodel.cpp
+++ b/models/subkeylistmodel.cpp
@@ -1,214 +1,214 @@
/* -*- mode: c++; c-basic-offset:4 -*-
models/subkeylistmodel.cpp
This file is part of Kleopatra, the KDE keymanager
Copyright (c) 2007 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
In addition, as a special exception, the copyright holders give
permission to link the code of this program with any edition of
the Qt library by Trolltech AS, Norway (or with modified versions
of Qt that use the same license as Qt), and distribute linked
combinations including the two. You must obey the GNU General
Public License in all respects for all of the code used other than
Qt. If you modify this file, you may extend this exception to
your version of the file, but you are not obligated to do so. If
you do not wish to do so, delete this exception statement from
your version.
*/
#include <config-kleopatra.h>
#include "subkeylistmodel.h"
#include <utils/formatting.h>
#include <gpgme++/key.h>
#include <KLocalizedString>
#include <QVariant>
#include <QDate>
#include <boost/bind.hpp>
#include <algorithm>
#include <iterator>
using namespace GpgME;
using namespace Kleo;
using namespace boost;
class SubkeyListModel::Private
{
friend class ::Kleo::SubkeyListModel;
SubkeyListModel *const q;
public:
explicit Private(SubkeyListModel *qq)
: q(qq), key() {}
private:
Key key;
};
SubkeyListModel::SubkeyListModel(QObject *p)
: QAbstractTableModel(p), d(new Private(this))
{
}
SubkeyListModel::~SubkeyListModel() {}
Key SubkeyListModel::key() const
{
return d->key;
}
// slot
void SubkeyListModel::setKey(const Key &key)
{
const Key oldKey = d->key;
d->key = key;
if (qstricmp(key.primaryFingerprint(), oldKey.primaryFingerprint()) != 0) {
// different key -> reset
reset();
return;
}
// ### diff them, and signal more fine-grained than this:
if (key.numSubkeys() > 0 && oldKey.numSubkeys() == key.numSubkeys()) {
Q_EMIT dataChanged(index(0, 0), index(key.numSubkeys() - 1, NumColumns - 1));
} else {
Q_EMIT layoutAboutToBeChanged();
Q_EMIT layoutChanged();
}
}
Subkey SubkeyListModel::subkey(const QModelIndex &idx) const
{
if (idx.isValid()) {
return d->key.subkey(idx.row());
} else {
return Subkey();
}
}
std::vector<Subkey> SubkeyListModel::subkeys(const QList<QModelIndex> &indexes) const
{
std::vector<Subkey> result;
result.reserve(indexes.size());
std::transform(indexes.begin(), indexes.end(),
std::back_inserter(result),
boost::bind(&SubkeyListModel::subkey, this, _1));
return result;
}
QModelIndex SubkeyListModel::index(const Subkey &subkey, int col) const
{
// O(N), but not sorted, so no better way...
for (unsigned int row = 0, end = d->key.numSubkeys() ; row != end ; ++row)
if (qstricmp(subkey.keyID(), d->key.subkey(row).keyID()) == 0) {
return index(row, col);
}
return QModelIndex();
}
QList<QModelIndex> SubkeyListModel::indexes(const std::vector<Subkey> &subkeys) const
{
QList<QModelIndex> result;
// O(N*M), but who cares...?
std::transform(subkeys.begin(), subkeys.end(),
std::back_inserter(result),
- // if some compilers are complaining about ambigious overloads, use this line instead:
+ // if some compilers are complaining about ambiguous overloads, use this line instead:
//bind( static_cast<QModelIndex(SubKeyListModel::*)(const Subkey&,int)const>( &SubkeyListModel::index ), this, _1, 0 ) );
boost::bind(&SubkeyListModel::index, this, _1, 0));
return result;
}
void SubkeyListModel::clear()
{
d->key = Key::null;
reset();
}
int SubkeyListModel::columnCount(const QModelIndex &) const
{
return NumColumns;
}
int SubkeyListModel::rowCount(const QModelIndex &pidx) const
{
return pidx.isValid() ? 0 : d->key.numSubkeys() ;
}
QVariant SubkeyListModel::headerData(int section, Qt::Orientation o, int role) const
{
if (o == Qt::Horizontal)
if (role == Qt::DisplayRole || role == Qt::EditRole || role == Qt::ToolTipRole)
switch (section) {
case ID: return i18n("ID");
case Type: return i18n("Type");
case ValidFrom: return i18n("Valid From");
case ValidUntil: return i18n("Valid Until");
case Status: return i18n("Status");
case Bits: return i18n("Strength");
case NumColumns: ;
}
return QVariant();
}
QVariant SubkeyListModel::data(const QModelIndex &idx, int role) const
{
if (role != Qt::DisplayRole && role != Qt::EditRole && role != Qt::ToolTipRole) {
return QVariant();
}
const Subkey subkey = this->subkey(idx);
if (subkey.isNull()) {
return QVariant();
}
switch (idx.column()) {
case ID:
return QString::fromLatin1(subkey.keyID());
case Type:
return Formatting::type(subkey);
case ValidFrom:
if (role == Qt::EditRole) {
return Formatting::creationDate(subkey);
} else {
return Formatting::creationDateString(subkey);
}
case ValidUntil:
if (role == Qt::EditRole) {
return Formatting::expirationDate(subkey);
} else {
return Formatting::expirationDateString(subkey);
}
case Status:
return Formatting::validityShort(subkey);
case Bits:
return subkey.length();
}
return QVariant();
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Jul 8, 12:30 PM (17 h, 45 m)
Storage Engine
local-disk
Storage Format
Raw Data
Storage Handle
aa/9d/9ccc245717a7a1e848d49436f443
Attached To
rKLEOPATRA Kleopatra
Event Timeline
Log In to Comment