Removing QWidget dependency from src/core.

This commit is contained in:
louib 2021-03-13 14:07:49 -05:00 committed by Jonathan White
parent 6f5bbf7ad1
commit 004f2b6801
46 changed files with 298 additions and 313 deletions

View File

@ -266,11 +266,11 @@ if(CMAKE_BUILD_TYPE_LOWER STREQUAL "debug")
endif() endif()
if (NOT HAIKU) if (NOT HAIKU)
if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8.999) OR CMAKE_COMPILER_IS_CLANGXX) if((CMAKE_COMPILER_IS_GNUCXX AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 4.8.999) OR CMAKE_COMPILER_IS_CLANGXX)
add_gcc_compiler_flags("-fstack-protector-strong") add_gcc_compiler_flags("-fstack-protector-strong")
else() else()
add_gcc_compiler_flags("-fstack-protector --param=ssp-buffer-size=4") add_gcc_compiler_flags("-fstack-protector --param=ssp-buffer-size=4")
endif() endif()
endif() endif()
add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual") add_gcc_compiler_cxxflags("-Wnon-virtual-dtor -Wold-style-cast -Woverloaded-virtual")
@ -466,7 +466,7 @@ elseif(WIN32)
message(STATUS "Using windeployqt: ${WINDEPLOYQT_EXE}") message(STATUS "Using windeployqt: ${WINDEPLOYQT_EXE}")
endif() endif()
# Debian sets the the build type to None for package builds. # Debian sets the build type to None for package builds.
# Make sure we don't enable asserts there. # Make sure we don't enable asserts there.
set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG) set_property(DIRECTORY APPEND PROPERTY COMPILE_DEFINITIONS_NONE QT_NO_DEBUG)

View File

@ -39,7 +39,6 @@ set(keepassx_SOURCES
core/Config.cpp core/Config.cpp
core/CustomData.cpp core/CustomData.cpp
core/Database.cpp core/Database.cpp
core/DatabaseIcons.cpp
core/Entry.cpp core/Entry.cpp
core/EntryAttachments.cpp core/EntryAttachments.cpp
core/EntryAttributes.cpp core/EntryAttributes.cpp
@ -71,7 +70,6 @@ set(keepassx_SOURCES
crypto/kdf/Argon2Kdf.cpp crypto/kdf/Argon2Kdf.cpp
format/CsvExporter.cpp format/CsvExporter.cpp
format/CsvParser.cpp format/CsvParser.cpp
format/HtmlExporter.cpp
format/KeePass1Reader.cpp format/KeePass1Reader.cpp
format/KeePass2.cpp format/KeePass2.cpp
format/KeePass2RandomStream.cpp format/KeePass2RandomStream.cpp
@ -101,6 +99,7 @@ set(keepassx_SOURCES
gui/CategoryListWidget.cpp gui/CategoryListWidget.cpp
gui/Clipboard.cpp gui/Clipboard.cpp
gui/CloneDialog.cpp gui/CloneDialog.cpp
gui/DatabaseIcons.cpp
gui/DatabaseOpenWidget.cpp gui/DatabaseOpenWidget.cpp
gui/DatabaseTabWidget.cpp gui/DatabaseTabWidget.cpp
gui/DatabaseWidget.cpp gui/DatabaseWidget.cpp
@ -114,6 +113,7 @@ set(keepassx_SOURCES
gui/FileDialog.cpp gui/FileDialog.cpp
gui/Font.cpp gui/Font.cpp
gui/GuiTools.cpp gui/GuiTools.cpp
gui/HtmlExporter.cpp
gui/IconModels.cpp gui/IconModels.cpp
gui/KeePass1OpenWidget.cpp gui/KeePass1OpenWidget.cpp
gui/KMessageWidget.cpp gui/KMessageWidget.cpp

View File

@ -20,8 +20,12 @@
#include <QFont> #include <QFont>
#include "core/Entry.h"
#include "core/Global.h"
#include "core/Group.h" #include "core/Group.h"
#include "core/Metadata.h" #include "core/Metadata.h"
#include "gui/DatabaseIcons.h"
#include "gui/Icons.h"
AutoTypeMatchModel::AutoTypeMatchModel(QObject* parent) AutoTypeMatchModel::AutoTypeMatchModel(QObject* parent)
: QAbstractTableModel(parent) : QAbstractTableModel(parent)
@ -114,11 +118,11 @@ QVariant AutoTypeMatchModel::data(const QModelIndex& index, int role) const
switch (index.column()) { switch (index.column()) {
case ParentGroup: case ParentGroup:
if (match.first->group()) { if (match.first->group()) {
return match.first->group()->iconPixmap(); return Icons::groupIconPixmap(match.first->group());
} }
break; break;
case Title: case Title:
return match.first->iconPixmap(); return Icons::entryIconPixmap(match.first);
} }
} else if (role == Qt::FontRole) { } else if (role == Qt::FontRole) {
QFont font; QFont font;

View File

@ -44,7 +44,7 @@ set(cli_SOURCES
Show.cpp) Show.cpp)
add_library(cli STATIC ${cli_SOURCES}) add_library(cli STATIC ${cli_SOURCES})
target_link_libraries(cli Qt5::Core Qt5::Widgets) target_link_libraries(cli Qt5::Core)
find_package(Readline) find_package(Readline)

View File

@ -19,7 +19,7 @@
#include "Entry.h" #include "Entry.h"
#include "core/Config.h" #include "core/Config.h"
#include "core/DatabaseIcons.h" #include "core/Database.h"
#include "core/Group.h" #include "core/Group.h"
#include "core/Metadata.h" #include "core/Metadata.h"
#include "core/PasswordHealth.h" #include "core/PasswordHealth.h"
@ -159,40 +159,6 @@ const QString Entry::uuidToHex() const
return Tools::uuidToHex(m_uuid); return Tools::uuidToHex(m_uuid);
} }
QImage Entry::icon() const
{
if (m_data.customIcon.isNull()) {
return databaseIcons()->icon(m_data.iconNumber).toImage();
} else {
Q_ASSERT(database());
if (database()) {
return database()->metadata()->customIcon(m_data.customIcon);
} else {
return QImage();
}
}
}
QPixmap Entry::iconPixmap(IconSize size) const
{
QPixmap icon(size, size);
if (m_data.customIcon.isNull()) {
icon = databaseIcons()->icon(m_data.iconNumber, size);
} else {
Q_ASSERT(database());
if (database()) {
icon = database()->metadata()->customIconPixmap(m_data.customIcon, size);
}
}
if (isExpired()) {
icon = databaseIcons()->applyBadge(icon, DatabaseIcons::Badges::Expired);
}
return icon;
}
int Entry::iconNumber() const int Entry::iconNumber() const
{ {
return m_data.iconNumber; return m_data.iconNumber;
@ -1217,7 +1183,8 @@ void Entry::setGroup(Group* group)
// copy custom icon to the new database // copy custom icon to the new database
if (!iconUuid().isNull() && group->database() && m_group->database()->metadata()->hasCustomIcon(iconUuid()) if (!iconUuid().isNull() && group->database() && m_group->database()->metadata()->hasCustomIcon(iconUuid())
&& !group->database()->metadata()->hasCustomIcon(iconUuid())) { && !group->database()->metadata()->hasCustomIcon(iconUuid())) {
group->database()->metadata()->addCustomIcon(iconUuid(), icon()); group->database()->metadata()->addCustomIcon(iconUuid(),
m_group->database()->metadata()->customIcon(iconUuid()));
} }
} }
} }

View File

@ -19,7 +19,7 @@
#ifndef KEEPASSX_ENTRY_H #ifndef KEEPASSX_ENTRY_H
#define KEEPASSX_ENTRY_H #define KEEPASSX_ENTRY_H
#include <QImage> #include <QMap>
#include <QPointer> #include <QPointer>
#include <QUuid> #include <QUuid>
@ -80,8 +80,6 @@ public:
~Entry(); ~Entry();
const QUuid& uuid() const; const QUuid& uuid() const;
const QString uuidToHex() const; const QString uuidToHex() const;
QImage icon() const;
QPixmap iconPixmap(IconSize size = IconSize::Default) const;
int iconNumber() const; int iconNumber() const;
const QUuid& iconUuid() const; const QUuid& iconUuid() const;
QString foregroundColor() const; QString foregroundColor() const;

View File

@ -45,13 +45,6 @@
static const auto TRUE_STR = QStringLiteral("true"); static const auto TRUE_STR = QStringLiteral("true");
static const auto FALSE_STR = QStringLiteral("false"); static const auto FALSE_STR = QStringLiteral("false");
enum IconSize
{
Default,
Medium,
Large
};
enum class AuthDecision enum class AuthDecision
{ {
Undecided, Undecided,

View File

@ -20,14 +20,16 @@
#include "config-keepassx.h" #include "config-keepassx.h"
#include "core/Config.h" #include "core/Config.h"
#include "core/DatabaseIcons.h"
#include "core/Metadata.h"
#include "core/Tools.h"
#ifdef WITH_XC_KEESHARE #ifdef WITH_XC_KEESHARE
#include "keeshare/KeeShare.h" #include "keeshare/KeeShare.h"
#endif #endif
#include "core/Global.h"
#include "core/Metadata.h"
#include "core/Tools.h"
#include <QtConcurrent>
#include <QtConcurrentFilter> #include <QtConcurrentFilter>
const int Group::DefaultIconNumber = 48; const int Group::DefaultIconNumber = 48;
@ -123,44 +125,6 @@ QString Group::notes() const
return m_data.notes; return m_data.notes;
} }
QImage Group::icon() const
{
if (m_data.customIcon.isNull()) {
return databaseIcons()->icon(m_data.iconNumber).toImage();
} else {
Q_ASSERT(m_db);
if (m_db) {
return m_db->metadata()->customIcon(m_data.customIcon);
} else {
return QImage();
}
}
}
QPixmap Group::iconPixmap(IconSize size) const
{
QPixmap icon(size, size);
if (m_data.customIcon.isNull()) {
icon = databaseIcons()->icon(m_data.iconNumber, size);
} else {
Q_ASSERT(m_db);
if (m_db) {
icon = m_db->metadata()->customIconPixmap(m_data.customIcon, size);
}
}
if (isExpired()) {
icon = databaseIcons()->applyBadge(icon, DatabaseIcons::Badges::Expired);
}
#ifdef WITH_XC_KEESHARE
else if (KeeShare::isShared(this)) {
icon = KeeShare::indicatorBadge(this, icon);
}
#endif
return icon;
}
int Group::iconNumber() const int Group::iconNumber() const
{ {
return m_data.iconNumber; return m_data.iconNumber;
@ -469,7 +433,7 @@ void Group::setParent(Group* parent, int index)
// copy custom icon to the new database // copy custom icon to the new database
if (!iconUuid().isNull() && parent->m_db && m_db->metadata()->hasCustomIcon(iconUuid()) if (!iconUuid().isNull() && parent->m_db && m_db->metadata()->hasCustomIcon(iconUuid())
&& !parent->m_db->metadata()->hasCustomIcon(iconUuid())) { && !parent->m_db->metadata()->hasCustomIcon(iconUuid())) {
parent->m_db->metadata()->addCustomIcon(iconUuid(), icon()); parent->m_db->metadata()->addCustomIcon(iconUuid(), m_db->metadata()->customIcon(iconUuid()));
} }
} }
if (m_db != parent->m_db) { if (m_db != parent->m_db) {

View File

@ -19,7 +19,7 @@
#ifndef KEEPASSX_GROUP_H #ifndef KEEPASSX_GROUP_H
#define KEEPASSX_GROUP_H #define KEEPASSX_GROUP_H
#include <QImage> #include <QPointer>
#include "core/CustomData.h" #include "core/CustomData.h"
#include "core/Database.h" #include "core/Database.h"
@ -82,8 +82,6 @@ public:
const QString uuidToHex() const; const QString uuidToHex() const;
QString name() const; QString name() const;
QString notes() const; QString notes() const;
QImage icon() const;
QPixmap iconPixmap(IconSize size = IconSize::Default) const;
int iconNumber() const; int iconNumber() const;
const QUuid& iconUuid() const; const QUuid& iconUuid() const;
const TimeInfo& timeInfo() const; const TimeInfo& timeInfo() const;

View File

@ -611,8 +611,7 @@ Merger::ChangeList Merger::mergeMetadata(const MergeContext& context)
for (const auto& iconUuid : sourceMetadata->customIconsOrder()) { for (const auto& iconUuid : sourceMetadata->customIconsOrder()) {
if (!targetMetadata->hasCustomIcon(iconUuid)) { if (!targetMetadata->hasCustomIcon(iconUuid)) {
QImage customIcon = sourceMetadata->customIcon(iconUuid); targetMetadata->addCustomIcon(iconUuid, sourceMetadata->customIcon(iconUuid));
targetMetadata->addCustomIcon(iconUuid, customIcon);
changes << tr("Adding missing icon %1").arg(QString::fromLatin1(iconUuid.toRfc4122().toHex())); changes << tr("Adding missing icon %1").arg(QString::fromLatin1(iconUuid.toRfc4122().toHex()));
} }
} }

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de> * Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -17,7 +18,8 @@
#include "Metadata.h" #include "Metadata.h"
#include "core/DatabaseIcons.h" #include "core/Clock.h"
#include "core/Entry.h"
#include "core/Group.h" #include "core/Group.h"
#include <QApplication> #include <QApplication>
@ -64,7 +66,6 @@ void Metadata::clear()
{ {
init(); init();
m_customIcons.clear(); m_customIcons.clear();
m_customIconsRaw.clear();
m_customIconsOrder.clear(); m_customIconsOrder.clear();
m_customIconsHashes.clear(); m_customIconsHashes.clear();
m_customData->clear(); m_customData->clear();
@ -175,33 +176,14 @@ bool Metadata::protectNotes() const
return m_data.protectNotes; return m_data.protectNotes;
} }
QImage Metadata::customIcon(const QUuid& uuid) const QByteArray Metadata::customIcon(const QUuid& uuid) const
{ {
return m_customIconsRaw.value(uuid); return m_customIcons.value(uuid);
}
QPixmap Metadata::customIconPixmap(const QUuid& uuid, IconSize size) const
{
if (!hasCustomIcon(uuid)) {
return {};
}
return m_customIcons.value(uuid).pixmap(databaseIcons()->iconSize(size));
}
QHash<QUuid, QPixmap> Metadata::customIconsPixmaps(IconSize size) const
{
QHash<QUuid, QPixmap> result;
for (const QUuid& uuid : m_customIconsOrder) {
result.insert(uuid, customIconPixmap(uuid, size));
}
return result;
} }
bool Metadata::hasCustomIcon(const QUuid& uuid) const bool Metadata::hasCustomIcon(const QUuid& uuid) const
{ {
return m_customIconsRaw.contains(uuid); return m_customIcons.contains(uuid);
} }
QList<QUuid> Metadata::customIconsOrder() const QList<QUuid> Metadata::customIconsOrder() const
@ -357,32 +339,19 @@ void Metadata::setProtectNotes(bool value)
set(m_data.protectNotes, value); set(m_data.protectNotes, value);
} }
void Metadata::addCustomIcon(const QUuid& uuid, const QImage& image) void Metadata::addCustomIcon(const QUuid& uuid, const QByteArray& iconData)
{ {
Q_ASSERT(!uuid.isNull()); Q_ASSERT(!uuid.isNull());
Q_ASSERT(!m_customIconsRaw.contains(uuid)); Q_ASSERT(!m_customIcons.contains(uuid));
m_customIconsRaw[uuid] = image; m_customIcons[uuid] = iconData;
// remove all uuids to prevent duplicates in release mode // remove all uuids to prevent duplicates in release mode
m_customIconsOrder.removeAll(uuid); m_customIconsOrder.removeAll(uuid);
m_customIconsOrder.append(uuid); m_customIconsOrder.append(uuid);
// Associate image hash to uuid // Associate image hash to uuid
QByteArray hash = hashImage(image); QByteArray hash = hashIcon(iconData);
m_customIconsHashes[hash] = uuid; m_customIconsHashes[hash] = uuid;
Q_ASSERT(m_customIconsRaw.count() == m_customIconsOrder.count()); Q_ASSERT(m_customIcons.count() == m_customIconsOrder.count());
// TODO: This check can go away when we move all QIcon handling outside of core
// On older versions of Qt, loading a QPixmap from QImage outside of a GUI
// environment causes ASAN to fail and crash on nullptr violation
static bool isGui = qApp->inherits("QGuiApplication");
if (isGui) {
// Generate QIcon with pre-baked resolutions
auto basePixmap = QPixmap::fromImage(image.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
QIcon icon(basePixmap);
m_customIcons.insert(uuid, icon);
} else {
m_customIcons.insert(uuid, QIcon());
}
emitModified(); emitModified();
} }
@ -390,24 +359,23 @@ void Metadata::addCustomIcon(const QUuid& uuid, const QImage& image)
void Metadata::removeCustomIcon(const QUuid& uuid) void Metadata::removeCustomIcon(const QUuid& uuid)
{ {
Q_ASSERT(!uuid.isNull()); Q_ASSERT(!uuid.isNull());
Q_ASSERT(m_customIconsRaw.contains(uuid)); Q_ASSERT(m_customIcons.contains(uuid));
// Remove hash record only if this is the same uuid // Remove hash record only if this is the same uuid
QByteArray hash = hashImage(m_customIconsRaw[uuid]); QByteArray hash = hashIcon(m_customIcons[uuid]);
if (m_customIconsHashes.contains(hash) && m_customIconsHashes[hash] == uuid) { if (m_customIconsHashes.contains(hash) && m_customIconsHashes[hash] == uuid) {
m_customIconsHashes.remove(hash); m_customIconsHashes.remove(hash);
} }
m_customIcons.remove(uuid); m_customIcons.remove(uuid);
m_customIconsRaw.remove(uuid);
m_customIconsOrder.removeAll(uuid); m_customIconsOrder.removeAll(uuid);
Q_ASSERT(m_customIconsRaw.count() == m_customIconsOrder.count()); Q_ASSERT(m_customIcons.count() == m_customIconsOrder.count());
emitModified(); emitModified();
} }
QUuid Metadata::findCustomIcon(const QImage& candidate) QUuid Metadata::findCustomIcon(const QByteArray& candidate)
{ {
QByteArray hash = hashImage(candidate); QByteArray hash = hashIcon(candidate);
return m_customIconsHashes.value(hash, QUuid()); return m_customIconsHashes.value(hash, QUuid());
} }
@ -422,14 +390,9 @@ void Metadata::copyCustomIcons(const QSet<QUuid>& iconList, const Metadata* othe
} }
} }
QByteArray Metadata::hashImage(const QImage& image) QByteArray Metadata::hashIcon(const QByteArray& iconData)
{ {
#if QT_VERSION >= QT_VERSION_CHECK(5, 10, 0) return QCryptographicHash::hash(iconData, QCryptographicHash::Md5);
auto data = QByteArray(reinterpret_cast<const char*>(image.bits()), static_cast<int>(image.sizeInBytes()));
#else
auto data = QByteArray(reinterpret_cast<const char*>(image.bits()), image.byteCount());
#endif
return QCryptographicHash::hash(data, QCryptographicHash::Md5);
} }
void Metadata::setRecycleBinEnabled(bool value) void Metadata::setRecycleBinEnabled(bool value)

View File

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2010 Felix Geyer <debfx@fobos.de> * Copyright (C) 2010 Felix Geyer <debfx@fobos.de>
* Copyright (C) 2021 KeePassXC Team <team@keepassxc.org>
* *
* This program is free software: you can redistribute it and/or modify * This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by * it under the terms of the GNU General Public License as published by
@ -19,7 +20,7 @@
#define KEEPASSX_METADATA_H #define KEEPASSX_METADATA_H
#include <QDateTime> #include <QDateTime>
#include <QIcon> #include <QHash>
#include <QPointer> #include <QPointer>
#include <QUuid> #include <QUuid>
@ -78,10 +79,8 @@ public:
bool protectPassword() const; bool protectPassword() const;
bool protectUrl() const; bool protectUrl() const;
bool protectNotes() const; bool protectNotes() const;
QImage customIcon(const QUuid& uuid) const; QByteArray customIcon(const QUuid& uuid) const;
bool hasCustomIcon(const QUuid& uuid) const; bool hasCustomIcon(const QUuid& uuid) const;
QPixmap customIconPixmap(const QUuid& uuid, IconSize size = IconSize::Default) const;
QHash<QUuid, QPixmap> customIconsPixmaps(IconSize size = IconSize::Default) const;
QList<QUuid> customIconsOrder() const; QList<QUuid> customIconsOrder() const;
bool recycleBinEnabled() const; bool recycleBinEnabled() const;
Group* recycleBin(); Group* recycleBin();
@ -117,10 +116,10 @@ public:
void setProtectPassword(bool value); void setProtectPassword(bool value);
void setProtectUrl(bool value); void setProtectUrl(bool value);
void setProtectNotes(bool value); void setProtectNotes(bool value);
void addCustomIcon(const QUuid& uuid, const QImage& image); void addCustomIcon(const QUuid& uuid, const QByteArray& iconData);
void removeCustomIcon(const QUuid& uuid); void removeCustomIcon(const QUuid& uuid);
void copyCustomIcons(const QSet<QUuid>& iconList, const Metadata* otherMetadata); void copyCustomIcons(const QSet<QUuid>& iconList, const Metadata* otherMetadata);
QUuid findCustomIcon(const QImage& candidate); QUuid findCustomIcon(const QByteArray& candidate);
void setRecycleBinEnabled(bool value); void setRecycleBinEnabled(bool value);
void setRecycleBin(Group* group); void setRecycleBin(Group* group);
void setRecycleBinChanged(const QDateTime& value); void setRecycleBinChanged(const QDateTime& value);
@ -148,13 +147,12 @@ private:
template <class P, class V> bool set(P& property, const V& value); template <class P, class V> bool set(P& property, const V& value);
template <class P, class V> bool set(P& property, const V& value, QDateTime& dateTime); template <class P, class V> bool set(P& property, const V& value, QDateTime& dateTime);
QByteArray hashImage(const QImage& image); QByteArray hashIcon(const QByteArray& iconData);
MetadataData m_data; MetadataData m_data;
QHash<QUuid, QIcon> m_customIcons;
QHash<QUuid, QImage> m_customIconsRaw;
QList<QUuid> m_customIconsOrder; QList<QUuid> m_customIconsOrder;
QHash<QUuid, QByteArray> m_customIcons;
QHash<QByteArray, QUuid> m_customIconsHashes; QHash<QByteArray, QUuid> m_customIconsHashes;
QPointer<Group> m_recycleBin; QPointer<Group> m_recycleBin;

View File

@ -24,9 +24,11 @@
#include "core/Clock.h" #include "core/Clock.h"
#include <QCoreApplication>
#include <QElapsedTimer> #include <QElapsedTimer>
#include <QEventLoop>
#include <QFileInfo> #include <QFileInfo>
#include <QImageReader> #include <QIODevice>
#include <QLocale> #include <QLocale>
#include <QMetaProperty> #include <QMetaProperty>
#include <QRegularExpression> #include <QRegularExpression>
@ -172,24 +174,6 @@ namespace Tools
} }
} }
QString imageReaderFilter()
{
const QList<QByteArray> formats = QImageReader::supportedImageFormats();
QStringList formatsStringList;
for (const QByteArray& format : formats) {
for (char codePoint : format) {
if (!QChar(codePoint).isLetterOrNumber()) {
continue;
}
}
formatsStringList.append("*." + QString::fromLatin1(format).toLower());
}
return formatsStringList.join(" ");
}
bool isHex(const QByteArray& ba) bool isHex(const QByteArray& ba)
{ {
for (const uchar c : ba) { for (const uchar c : ba) {

View File

@ -32,7 +32,6 @@ namespace Tools
QString humanReadableFileSize(qint64 bytes, quint32 precision = 2); QString humanReadableFileSize(qint64 bytes, quint32 precision = 2);
bool readFromDevice(QIODevice* device, QByteArray& data, int size = 16384); bool readFromDevice(QIODevice* device, QByteArray& data, int size = 16384);
bool readAllFromDevice(QIODevice* device, QByteArray& data); bool readAllFromDevice(QIODevice* device, QByteArray& data);
QString imageReaderFilter();
bool isHex(const QByteArray& ba); bool isHex(const QByteArray& ba);
bool isBase64(const QByteArray& ba); bool isBase64(const QByteArray& ba);
void sleep(int ms); void sleep(int ms);

View File

@ -24,6 +24,7 @@
#include "fdosecrets/widgets/RowButtonHelper.h" #include "fdosecrets/widgets/RowButtonHelper.h"
#include "core/Entry.h" #include "core/Entry.h"
#include "gui/Icons.h"
#include <QWindow> #include <QWindow>
@ -250,7 +251,7 @@ QVariant AccessControlDialog::EntryModel::data(const QModelIndex& index, int rol
case Qt::DisplayRole: case Qt::DisplayRole:
return entry->title(); return entry->title();
case Qt::DecorationRole: case Qt::DecorationRole:
return entry->icon(); return Icons::entryIconPixmap(entry);
case Qt::CheckStateRole: case Qt::CheckStateRole:
return QVariant::fromValue(m_selected.contains(entry) ? Qt::Checked : Qt::Unchecked); return QVariant::fromValue(m_selected.contains(entry) ? Qt::Checked : Qt::Unchecked);
default: default:

View File

@ -147,7 +147,7 @@ namespace FdoSecrets
case Qt::DisplayRole: case Qt::DisplayRole:
return group->name(); return group->name();
case Qt::DecorationRole: case Qt::DecorationRole:
return group->iconPixmap(); return Icons::groupIconPixmap(group);
case Qt::FontRole: case Qt::FontRole:
if (group->isExpired()) { if (group->isExpired()) {
QFont font; QFont font;

View File

@ -17,7 +17,7 @@
#include "KdbxXmlReader.h" #include "KdbxXmlReader.h"
#include "KeePass2RandomStream.h" #include "KeePass2RandomStream.h"
#include "core/DatabaseIcons.h" #include "core/Clock.h"
#include "core/Endian.h" #include "core/Endian.h"
#include "core/Group.h" #include "core/Group.h"
#include "core/Tools.h" #include "core/Tools.h"
@ -346,7 +346,7 @@ void KdbxXmlReader::parseIcon()
Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Icon"); Q_ASSERT(m_xml.isStartElement() && m_xml.name() == "Icon");
QUuid uuid; QUuid uuid;
QImage icon; QByteArray iconData;
bool uuidSet = false; bool uuidSet = false;
bool iconSet = false; bool iconSet = false;
@ -355,7 +355,7 @@ void KdbxXmlReader::parseIcon()
uuid = readUuid(); uuid = readUuid();
uuidSet = !uuid.isNull(); uuidSet = !uuid.isNull();
} else if (m_xml.name() == "Data") { } else if (m_xml.name() == "Data") {
icon.loadFromData(readBinary()); iconData = readBinary();
iconSet = true; iconSet = true;
} else { } else {
skipCurrentElement(); skipCurrentElement();
@ -367,7 +367,7 @@ void KdbxXmlReader::parseIcon()
if (m_meta->hasCustomIcon(uuid)) { if (m_meta->hasCustomIcon(uuid)) {
uuid = QUuid::createUuid(); uuid = QUuid::createUuid();
} }
m_meta->addCustomIcon(uuid, icon); m_meta->addCustomIcon(uuid, iconData);
return; return;
} }
@ -509,9 +509,6 @@ Group* KdbxXmlReader::parseGroup()
raiseError(tr("Invalid group icon number")); raiseError(tr("Invalid group icon number"));
} }
iconId = 0; iconId = 0;
} else if (iconId >= databaseIcons()->count()) {
qWarning("KdbxXmlReader::parseGroup: icon id \"%d\" not supported", iconId);
iconId = databaseIcons()->count() - 1;
} }
group->setIcon(iconId); group->setIcon(iconId);

View File

@ -162,19 +162,12 @@ void KdbxXmlWriter::writeCustomIcons()
m_xml.writeEndElement(); m_xml.writeEndElement();
} }
void KdbxXmlWriter::writeIcon(const QUuid& uuid, const QImage& icon) void KdbxXmlWriter::writeIcon(const QUuid& uuid, const QByteArray& iconData)
{ {
m_xml.writeStartElement("Icon"); m_xml.writeStartElement("Icon");
writeUuid("UUID", uuid); writeUuid("UUID", uuid);
writeBinary("Data", iconData);
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
// TODO: check !icon.save()
icon.save(&buffer, "PNG");
buffer.close();
writeBinary("Data", ba);
m_xml.writeEndElement(); m_xml.writeEndElement();
} }

View File

@ -18,6 +18,7 @@
#ifndef KEEPASSX_KDBXXMLWRITER_H #ifndef KEEPASSX_KDBXXMLWRITER_H
#define KEEPASSX_KDBXXMLWRITER_H #define KEEPASSX_KDBXXMLWRITER_H
#include <QDateTime>
#include <QXmlStreamWriter> #include <QXmlStreamWriter>
#include "core/Group.h" #include "core/Group.h"
@ -46,7 +47,7 @@ private:
void writeMetadata(); void writeMetadata();
void writeMemoryProtection(); void writeMemoryProtection();
void writeCustomIcons(); void writeCustomIcons();
void writeIcon(const QUuid& uuid, const QImage& icon); void writeIcon(const QUuid& uuid, const QByteArray& iconData);
void writeBinaries(); void writeBinaries();
void writeCustomData(const CustomData* customData); void writeCustomData(const CustomData* customData);
void writeCustomDataItem(const QString& key, const QString& value); void writeCustomDataItem(const QString& key, const QString& value);

View File

@ -857,13 +857,9 @@ bool KeePass1Reader::parseCustomIcons4(const QByteArray& data)
if (static_cast<quint32>(data.size()) < (pos + iconSize)) { if (static_cast<quint32>(data.size()) < (pos + iconSize)) {
return false; return false;
} }
QImage icon = QImage::fromData(data.mid(pos, iconSize)); QByteArray icon = data.mid(pos, iconSize);
pos += iconSize; pos += iconSize;
if (icon.width() != 16 || icon.height() != 16) {
icon = icon.scaled(16, 16);
}
QUuid uuid = QUuid::createUuid(); QUuid uuid = QUuid::createUuid();
iconUuids.append(uuid); iconUuids.append(uuid);
m_db->metadata()->addCustomIcon(uuid, icon); m_db->metadata()->addCustomIcon(uuid, icon);

View File

@ -57,8 +57,9 @@ DatabaseIcons* DatabaseIcons::instance()
QPixmap DatabaseIcons::icon(int index, IconSize size) QPixmap DatabaseIcons::icon(int index, IconSize size)
{ {
if (index < 0 || index >= count()) { if (index < 0 || index >= count()) {
qWarning("DatabaseIcons::icon: invalid icon index %d", index); qWarning("DatabaseIcons::icon: invalid icon index %d, using 0 instead", index);
return {}; index = 0;
Q_ASSERT_X(false, "DatabaseIcons::icon", "invalid icon index %d");
} }
auto cacheKey = QString::number(index); auto cacheKey = QString::number(index);

View File

@ -18,9 +18,15 @@
#ifndef KEEPASSX_DATABASEICONS_H #ifndef KEEPASSX_DATABASEICONS_H
#define KEEPASSX_DATABASEICONS_H #define KEEPASSX_DATABASEICONS_H
#include "core/Global.h"
#include <QIcon> #include <QIcon>
enum IconSize
{
Default,
Medium,
Large
};
class DatabaseIcons class DatabaseIcons
{ {
public: public:

View File

@ -22,10 +22,13 @@
#include "autotype/AutoType.h" #include "autotype/AutoType.h"
#include "core/Tools.h" #include "core/Tools.h"
#include "format/CsvExporter.h" #include "format/CsvExporter.h"
#include "format/HtmlExporter.h" #include "gui/Clipboard.h"
#include "gui/DatabaseOpenDialog.h"
#include "gui/DatabaseWidget.h"
#include "gui/DatabaseWidgetStateSync.h" #include "gui/DatabaseWidgetStateSync.h"
#include "gui/DragTabBar.h" #include "gui/DragTabBar.h"
#include "gui/FileDialog.h" #include "gui/FileDialog.h"
#include "gui/HtmlExporter.h"
#include "gui/MessageBox.h" #include "gui/MessageBox.h"
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
#include "gui/osutils/macutils/MacUtils.h" #include "gui/osutils/macutils/MacUtils.h"

View File

@ -25,6 +25,8 @@
#include "core/Tools.h" #include "core/Tools.h"
#include "gui/FileDialog.h" #include "gui/FileDialog.h"
#include "gui/IconModels.h" #include "gui/IconModels.h"
#include "gui/Icons.h"
#include "gui/MessageBox.h"
#ifdef WITH_XC_NETWORKING #ifdef WITH_XC_NETWORKING
#include "gui/IconDownloader.h" #include "gui/IconDownloader.h"
#endif #endif
@ -128,7 +130,7 @@ void EditWidgetIcons::load(const QUuid& currentUuid,
m_currentUuid = currentUuid; m_currentUuid = currentUuid;
setUrl(url); setUrl(url);
m_customIconModel->setIcons(database->metadata()->customIconsPixmaps(IconSize::Default), m_customIconModel->setIcons(Icons::customIconsPixmaps(database.data(), IconSize::Default),
database->metadata()->customIconsOrder()); database->metadata()->customIconsOrder());
QUuid iconUuid = iconStruct.uuid; QUuid iconUuid = iconStruct.uuid;
@ -231,7 +233,7 @@ void EditWidgetIcons::addCustomIconFromFile()
return; return;
} }
auto filter = QString("%1 (%2);;%3 (*)").arg(tr("Images"), Tools::imageReaderFilter(), tr("All files")); auto filter = QString("%1 (%2);;%3 (*)").arg(tr("Images"), Icons::imageFormatsFilter(), tr("All files"));
auto filenames = auto filenames =
fileDialog()->getOpenFileNames(this, tr("Select Image(s)"), FileDialog::getLastDir("icons"), filter); fileDialog()->getOpenFileNames(this, tr("Select Image(s)"), FileDialog::getLastDir("icons"), filter);
if (!filenames.empty()) { if (!filenames.empty()) {
@ -284,16 +286,17 @@ bool EditWidgetIcons::addCustomIcon(const QImage& icon)
bool added = false; bool added = false;
if (m_db) { if (m_db) {
// Don't add an icon larger than 128x128, but retain original size if smaller // Don't add an icon larger than 128x128, but retain original size if smaller
auto scaledicon = icon; auto scaledIcon = icon;
if (icon.width() > 128 || icon.height() > 128) { if (icon.width() > 128 || icon.height() > 128) {
scaledicon = icon.scaled(128, 128); scaledIcon = icon.scaled(128, 128);
} }
QUuid uuid = m_db->metadata()->findCustomIcon(scaledicon); QByteArray serializedIcon = Icons::saveToBytes(scaledIcon);
QUuid uuid = m_db->metadata()->findCustomIcon(serializedIcon);
if (uuid.isNull()) { if (uuid.isNull()) {
uuid = QUuid::createUuid(); uuid = QUuid::createUuid();
m_db->metadata()->addCustomIcon(uuid, scaledicon); m_db->metadata()->addCustomIcon(uuid, serializedIcon);
m_customIconModel->setIcons(m_db->metadata()->customIconsPixmaps(IconSize::Default), m_customIconModel->setIcons(Icons::customIconsPixmaps(m_db.data(), IconSize::Default),
m_db->metadata()->customIconsOrder()); m_db->metadata()->customIconsOrder());
added = true; added = true;
} }

View File

@ -173,7 +173,7 @@ void EntryPreviewWidget::updateEntryHeaderLine()
Q_ASSERT(m_currentEntry); Q_ASSERT(m_currentEntry);
const QString title = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->title()); const QString title = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->title());
m_ui->entryTitleLabel->setRawText(hierarchy(m_currentEntry->group(), title)); m_ui->entryTitleLabel->setRawText(hierarchy(m_currentEntry->group(), title));
m_ui->entryIcon->setPixmap(m_currentEntry->iconPixmap(IconSize::Large)); m_ui->entryIcon->setPixmap(Icons::entryIconPixmap(m_currentEntry, IconSize::Large));
} }
void EntryPreviewWidget::updateEntryTotp() void EntryPreviewWidget::updateEntryTotp()
@ -377,7 +377,7 @@ void EntryPreviewWidget::updateGroupHeaderLine()
{ {
Q_ASSERT(m_currentGroup); Q_ASSERT(m_currentGroup);
m_ui->groupTitleLabel->setRawText(hierarchy(m_currentGroup, {})); m_ui->groupTitleLabel->setRawText(hierarchy(m_currentGroup, {}));
m_ui->groupIcon->setPixmap(m_currentGroup->iconPixmap(IconSize::Large)); m_ui->groupIcon->setPixmap(Icons::groupIconPixmap(m_currentGroup, IconSize::Large));
} }
void EntryPreviewWidget::updateGroupGeneralTab() void EntryPreviewWidget::updateGroupGeneralTab()

View File

@ -22,6 +22,7 @@
#include "core/Group.h" #include "core/Group.h"
#include "core/Metadata.h" #include "core/Metadata.h"
#include "gui/Icons.h"
namespace namespace
{ {
@ -204,7 +205,7 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
// Header line // Header line
auto header = QString("<hr><h2>"); auto header = QString("<hr><h2>");
header.append(PixmapToHTML(group.iconPixmap(IconSize::Medium))); header.append(PixmapToHTML(Icons::groupIconPixmap(&group, IconSize::Medium)));
header.append("&nbsp;"); header.append("&nbsp;");
header.append(path); header.append(path);
header.append("</h2>\n"); header.append("</h2>\n");
@ -236,7 +237,7 @@ bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString pat
// Output it into our table. First the left side with // Output it into our table. First the left side with
// icon and entry title ... // icon and entry title ...
table += "<tr>"; table += "<tr>";
table += "<td width=\"1%\">" + PixmapToHTML(entry->iconPixmap(IconSize::Medium)) + "</td>"; table += "<td width=\"1%\">" + PixmapToHTML(Icons::entryIconPixmap(entry, IconSize::Medium)) + "</td>";
table += "<td width=\"19%\" valign=\"top\"><h3>" + entry->title().toHtmlEscaped() + "</h3></td>"; table += "<td width=\"19%\" valign=\"top\"><h3>" + entry->title().toHtmlEscaped() + "</h3></td>";
// ... then the right side with the data fields // ... then the right side with the data fields

View File

@ -24,7 +24,12 @@
#include "core/Metadata.h" #include "core/Metadata.h"
#include "core/Tools.h" #include "core/Tools.h"
#include "gui/IconDownloader.h" #include "gui/IconDownloader.h"
#include "gui/IconModels.h"
#include "gui/Icons.h"
#include "osutils/OSUtils.h" #include "osutils/OSUtils.h"
#ifdef Q_OS_MACOS
#include "gui/osutils/macutils/MacUtils.h"
#endif
#include <QStandardItemModel> #include <QStandardItemModel>
@ -126,15 +131,16 @@ void IconDownloaderDialog::downloadFinished(const QString& url, const QImage& ic
if (m_db && !icon.isNull()) { if (m_db && !icon.isNull()) {
// Don't add an icon larger than 128x128, but retain original size if smaller // Don't add an icon larger than 128x128, but retain original size if smaller
auto scaledicon = icon; auto scaledIcon = icon;
if (icon.width() > 128 || icon.height() > 128) { if (icon.width() > 128 || icon.height() > 128) {
scaledicon = icon.scaled(128, 128); scaledIcon = icon.scaled(128, 128);
} }
QUuid uuid = m_db->metadata()->findCustomIcon(scaledicon); QByteArray serializedIcon = Icons::saveToBytes(scaledIcon);
QUuid uuid = m_db->metadata()->findCustomIcon(serializedIcon);
if (uuid.isNull()) { if (uuid.isNull()) {
uuid = QUuid::createUuid(); uuid = QUuid::createUuid();
m_db->metadata()->addCustomIcon(uuid, scaledicon); m_db->metadata()->addCustomIcon(uuid, serializedIcon);
updateTable(url, tr("Ok")); updateTable(url, tr("Ok"));
} else { } else {
updateTable(url, tr("Already Exists")); updateTable(url, tr("Already Exists"));

View File

@ -19,7 +19,7 @@
#include <QUuid> #include <QUuid>
#include "core/DatabaseIcons.h" #include "gui/DatabaseIcons.h"
DefaultIconModel::DefaultIconModel(QObject* parent) DefaultIconModel::DefaultIconModel(QObject* parent)
: QAbstractListModel(parent) : QAbstractListModel(parent)

View File

@ -19,11 +19,20 @@
#include "Icons.h" #include "Icons.h"
#include <QIconEngine> #include <QIconEngine>
#include <QImageReader>
#include <QPaintDevice>
#include <QPainter> #include <QPainter>
#include "config-keepassx.h"
#include "core/Config.h"
#include "gui/DatabaseIcons.h"
#include "gui/MainWindow.h" #include "gui/MainWindow.h"
#include "gui/osutils/OSUtils.h" #include "gui/osutils/OSUtils.h"
#ifdef WITH_XC_KEESHARE
#include "keeshare/KeeShare.h"
#endif
class AdaptiveIconEngine : public QIconEngine class AdaptiveIconEngine : public QIconEngine
{ {
public: public:
@ -206,3 +215,97 @@ Icons* Icons::instance()
return m_instance; return m_instance;
} }
QPixmap Icons::customIconPixmap(const Database* db, const QUuid& uuid, IconSize size)
{
if (!db->metadata()->hasCustomIcon(uuid)) {
return {};
}
// Generate QIcon with pre-baked resolutions
auto icon = QImage::fromData(db->metadata()->customIcon(uuid));
auto basePixmap = QPixmap::fromImage(icon.scaled(64, 64, Qt::IgnoreAspectRatio, Qt::SmoothTransformation));
return QIcon(basePixmap).pixmap(databaseIcons()->iconSize(size));
}
QHash<QUuid, QPixmap> Icons::customIconsPixmaps(const Database* db, IconSize size)
{
QHash<QUuid, QPixmap> result;
for (const QUuid& uuid : db->metadata()->customIconsOrder()) {
result.insert(uuid, Icons::customIconPixmap(db, uuid, size));
}
return result;
}
QPixmap Icons::entryIconPixmap(const Entry* entry, IconSize size)
{
QPixmap icon(size, size);
if (entry->iconUuid().isNull()) {
icon = databaseIcons()->icon(entry->iconNumber(), size);
} else {
Q_ASSERT(entry->database());
if (entry->database()) {
icon = Icons::customIconPixmap(entry->database(), entry->iconUuid(), size);
}
}
if (entry->isExpired()) {
icon = databaseIcons()->applyBadge(icon, DatabaseIcons::Badges::Expired);
}
return icon;
}
QPixmap Icons::groupIconPixmap(const Group* group, IconSize size)
{
QPixmap icon(size, size);
if (group->iconUuid().isNull()) {
icon = databaseIcons()->icon(group->iconNumber(), size);
} else {
Q_ASSERT(group->database());
if (group->database()) {
icon = Icons::customIconPixmap(group->database(), group->iconUuid(), size);
}
}
if (group->isExpired()) {
icon = databaseIcons()->applyBadge(icon, DatabaseIcons::Badges::Expired);
}
#ifdef WITH_XC_KEESHARE
else if (KeeShare::isShared(group)) {
icon = KeeShare::indicatorBadge(group, icon);
}
#endif
return icon;
}
QString Icons::imageFormatsFilter()
{
const QList<QByteArray> formats = QImageReader::supportedImageFormats();
QStringList formatsStringList;
for (const QByteArray& format : formats) {
for (char codePoint : format) {
if (!QChar(codePoint).isLetterOrNumber()) {
continue;
}
}
formatsStringList.append("*." + QString::fromLatin1(format).toLower());
}
return formatsStringList.join(" ");
}
QByteArray Icons::saveToBytes(const QImage& image)
{
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
// TODO: check !icon.save()
image.save(&buffer, "PNG");
buffer.close();
return ba;
}

View File

@ -21,6 +21,9 @@
#include <QIcon> #include <QIcon>
#include <core/Database.h>
#include <gui/DatabaseIcons.h>
class Icons class Icons
{ {
public: public:
@ -32,6 +35,14 @@ public:
QIcon icon(const QString& name, bool recolor = true, const QColor& overrideColor = QColor::Invalid); QIcon icon(const QString& name, bool recolor = true, const QColor& overrideColor = QColor::Invalid);
QIcon onOffIcon(const QString& name, bool on, bool recolor = true); QIcon onOffIcon(const QString& name, bool on, bool recolor = true);
static QPixmap customIconPixmap(const Database* db, const QUuid& uuid, IconSize size = IconSize::Default);
static QHash<QUuid, QPixmap> customIconsPixmaps(const Database* db, IconSize size = IconSize::Default);
static QPixmap entryIconPixmap(const Entry* entry, IconSize size = IconSize::Default);
static QPixmap groupIconPixmap(const Group* group, IconSize size = IconSize::Default);
static QByteArray saveToBytes(const QImage& image);
static QString imageFormatsFilter();
static Icons* instance(); static Icons* instance();
private: private:

View File

@ -21,6 +21,7 @@
#include "core/Group.h" #include "core/Group.h"
#include "core/Metadata.h" #include "core/Metadata.h"
#include "gui/IconModels.h" #include "gui/IconModels.h"
#include "gui/Icons.h"
#include "gui/MessageBox.h" #include "gui/MessageBox.h"
DatabaseSettingsWidgetMaintenance::DatabaseSettingsWidgetMaintenance(QWidget* parent) DatabaseSettingsWidgetMaintenance::DatabaseSettingsWidgetMaintenance(QWidget* parent)
@ -47,7 +48,7 @@ DatabaseSettingsWidgetMaintenance::~DatabaseSettingsWidgetMaintenance()
void DatabaseSettingsWidgetMaintenance::populateIcons(QSharedPointer<Database> db) void DatabaseSettingsWidgetMaintenance::populateIcons(QSharedPointer<Database> db)
{ {
m_customIconModel->setIcons(db->metadata()->customIconsPixmaps(IconSize::Default), m_customIconModel->setIcons(Icons::customIconsPixmaps(db.data(), IconSize::Default),
db->metadata()->customIconsOrder()); db->metadata()->customIconsOrder());
m_ui->deleteButton->setEnabled(false); m_ui->deleteButton->setEnabled(false);
} }

View File

@ -21,9 +21,11 @@
#include <QMimeData> #include <QMimeData>
#include <QPalette> #include <QPalette>
#include "core/Entry.h"
#include "core/Group.h" #include "core/Group.h"
#include "core/Metadata.h" #include "core/Metadata.h"
#include "core/PasswordHealth.h" #include "core/PasswordHealth.h"
#include "gui/DatabaseIcons.h"
#include "gui/Icons.h" #include "gui/Icons.h"
#include "gui/styles/StateColorPalette.h" #include "gui/styles/StateColorPalette.h"
#ifdef Q_OS_MACOS #ifdef Q_OS_MACOS
@ -277,11 +279,11 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const
switch (index.column()) { switch (index.column()) {
case ParentGroup: case ParentGroup:
if (entry->group()) { if (entry->group()) {
return entry->group()->iconPixmap(); return Icons::groupIconPixmap(entry->group());
} }
break; break;
case Title: case Title:
return entry->iconPixmap(); return Icons::entryIconPixmap(entry);
case Paperclip: case Paperclip:
if (!entry->attachments()->isEmpty()) { if (!entry->attachments()->isEmpty()) {
return icons()->icon("paperclip"); return icons()->icon("paperclip");

View File

@ -19,8 +19,12 @@
#include <QMimeData> #include <QMimeData>
#include "core/Database.h"
#include "core/Group.h" #include "core/Group.h"
#include "core/Metadata.h" #include "core/Metadata.h"
#include "core/Tools.h"
#include "gui/DatabaseIcons.h"
#include "gui/Icons.h"
#include "keeshare/KeeShare.h" #include "keeshare/KeeShare.h"
GroupModel::GroupModel(Database* db, QObject* parent) GroupModel::GroupModel(Database* db, QObject* parent)
@ -126,7 +130,7 @@ QVariant GroupModel::data(const QModelIndex& index, int role) const
#endif #endif
return nameTemplate.arg(group->name()); return nameTemplate.arg(group->name());
} else if (role == Qt::DecorationRole) { } else if (role == Qt::DecorationRole) {
return group->iconPixmap(); return Icons::groupIconPixmap(group);
} else if (role == Qt::FontRole) { } else if (role == Qt::FontRole) {
QFont font; QFont font;
if (group->isExpired()) { if (group->isExpired()) {

View File

@ -203,8 +203,8 @@ void ReportsWidgetHealthcheck::addHealthRow(QSharedPointer<PasswordHealth> healt
auto row = QList<QStandardItem*>(); auto row = QList<QStandardItem*>();
row << new QStandardItem(descr); row << new QStandardItem(descr);
row << new QStandardItem(entry->iconPixmap(), title); row << new QStandardItem(Icons::entryIconPixmap(entry), title);
row << new QStandardItem(group->iconPixmap(), group->hierarchy().join("/")); row << new QStandardItem(Icons::groupIconPixmap(group), group->hierarchy().join("/"));
row << new QStandardItem(QString::number(health->score())); row << new QStandardItem(QString::number(health->score()));
row << new QStandardItem(health->scoreReason()); row << new QStandardItem(health->scoreReason());

View File

@ -163,8 +163,8 @@ void ReportsWidgetHibp::makeHibpTable()
} }
auto row = QList<QStandardItem*>(); auto row = QList<QStandardItem*>();
row << new QStandardItem(entry->iconPixmap(), title) row << new QStandardItem(Icons::entryIconPixmap(entry), title)
<< new QStandardItem(group->iconPixmap(), group->hierarchy().join("/")) << new QStandardItem(Icons::groupIconPixmap(group), group->hierarchy().join("/"))
<< new QStandardItem(countToText(count)); << new QStandardItem(countToText(count));
if (entry->excludeFromReports()) { if (entry->excludeFromReports()) {

View File

@ -16,8 +16,11 @@
*/ */
#include "KeeShare.h" #include "KeeShare.h"
#include "core/DatabaseIcons.h" #include "core/CustomData.h"
#include "core/Database.h"
#include "core/Group.h" #include "core/Group.h"
#include "core/Metadata.h"
#include "gui/DatabaseIcons.h"
#include "keeshare/ShareObserver.h" #include "keeshare/ShareObserver.h"
namespace namespace

View File

@ -17,7 +17,13 @@
#include "KeeShareSettings.h" #include "KeeShareSettings.h"
#include "core/CustomData.h"
#include "core/Database.h"
#include "core/Group.h"
#include "core/Metadata.h"
#include "crypto/Random.h" #include "crypto/Random.h"
#include "gui/DatabaseIcons.h"
#include "keeshare/Signature.h"
#include <QTextCodec> #include <QTextCodec>
#include <QXmlStreamWriter> #include <QXmlStreamWriter>

View File

@ -19,6 +19,7 @@
#include "core/Group.h" #include "core/Group.h"
#include "core/Metadata.h" #include "core/Metadata.h"
#include "format/KeePass2Writer.h" #include "format/KeePass2Writer.h"
#include "gui/Icons.h"
#include "keeshare/KeeShare.h" #include "keeshare/KeeShare.h"
#include "keeshare/Signature.h" #include "keeshare/Signature.h"
#include "keys/PasswordKey.h" #include "keys/PasswordKey.h"
@ -81,7 +82,7 @@ namespace
targetEntry->setUpdateTimeinfo(updateTimeinfoEntry); targetEntry->setUpdateTimeinfo(updateTimeinfoEntry);
const auto iconUuid = targetEntry->iconUuid(); const auto iconUuid = targetEntry->iconUuid();
if (!iconUuid.isNull() && !targetMetadata->hasCustomIcon(iconUuid)) { if (!iconUuid.isNull() && !targetMetadata->hasCustomIcon(iconUuid)) {
targetMetadata->addCustomIcon(iconUuid, sourceEntry->icon()); targetMetadata->addCustomIcon(iconUuid, sourceEntry->database()->metadata()->customIcon(iconUuid));
} }
} }

View File

@ -21,4 +21,4 @@ find_package(QREncode REQUIRED)
add_library(qrcode STATIC ${qrcode_SOURCES}) add_library(qrcode STATIC ${qrcode_SOURCES})
target_include_directories(qrcode PRIVATE ${QRENCODE_INCLUDE_DIR}) target_include_directories(qrcode PRIVATE ${QRENCODE_INCLUDE_DIR})
target_link_libraries(qrcode PUBLIC Qt5::Core Qt5::Widgets Qt5::Svg ${QRENCODE_LIBRARY}) target_link_libraries(qrcode PUBLIC Qt5::Core Qt5::Svg ${QRENCODE_LIBRARY})

View File

@ -20,9 +20,10 @@
#include <QSignalSpy> #include <QSignalSpy>
#include <QTest> #include <QTest>
#include "core/DatabaseIcons.h" #include "core/Entry.h"
#include "core/Group.h" #include "core/Group.h"
#include "crypto/Crypto.h" #include "crypto/Crypto.h"
#include "gui/DatabaseIcons.h"
#include "gui/IconModels.h" #include "gui/IconModels.h"
#include "gui/SortFilterHideProxyModel.h" #include "gui/SortFilterHideProxyModel.h"
#include "gui/entry/AutoTypeAssociationsModel.h" #include "gui/entry/AutoTypeAssociationsModel.h"

View File

@ -315,36 +315,34 @@ void TestGroup::testCopyCustomIcon()
QScopedPointer<Database> dbSource(new Database()); QScopedPointer<Database> dbSource(new Database());
QUuid groupIconUuid = QUuid::createUuid(); QUuid groupIconUuid = QUuid::createUuid();
QImage groupIcon(16, 16, QImage::Format_RGB32); QByteArray groupIcon("group icon");
groupIcon.setPixel(0, 0, qRgb(255, 0, 0));
dbSource->metadata()->addCustomIcon(groupIconUuid, groupIcon); dbSource->metadata()->addCustomIcon(groupIconUuid, groupIcon);
QUuid entryIconUuid = QUuid::createUuid(); QUuid entryIconUuid = QUuid::createUuid();
QImage entryIcon(16, 16, QImage::Format_RGB32); QByteArray entryIcon("entry icon");
entryIcon.setPixel(0, 0, qRgb(255, 0, 0));
dbSource->metadata()->addCustomIcon(entryIconUuid, entryIcon); dbSource->metadata()->addCustomIcon(entryIconUuid, entryIcon);
Group* group = new Group(); Group* group = new Group();
group->setParent(dbSource->rootGroup()); group->setParent(dbSource->rootGroup());
group->setIcon(groupIconUuid); group->setIcon(groupIconUuid);
QCOMPARE(group->icon(), groupIcon); QCOMPARE(group->database()->metadata()->customIcon(groupIconUuid), groupIcon);
Entry* entry = new Entry(); Entry* entry = new Entry();
entry->setGroup(dbSource->rootGroup()); entry->setGroup(dbSource->rootGroup());
entry->setIcon(entryIconUuid); entry->setIcon(entryIconUuid);
QCOMPARE(entry->icon(), entryIcon); QCOMPARE(entry->database()->metadata()->customIcon(entryIconUuid), entryIcon);
QScopedPointer<Database> dbTarget(new Database()); QScopedPointer<Database> dbTarget(new Database());
group->setParent(dbTarget->rootGroup()); group->setParent(dbTarget->rootGroup());
QVERIFY(dbTarget->metadata()->hasCustomIcon(groupIconUuid)); QVERIFY(dbTarget->metadata()->hasCustomIcon(groupIconUuid));
QCOMPARE(dbTarget->metadata()->customIcon(groupIconUuid), groupIcon); QCOMPARE(dbTarget->metadata()->customIcon(groupIconUuid), groupIcon);
QCOMPARE(group->icon(), groupIcon); QCOMPARE(group->database()->metadata()->customIcon(groupIconUuid), groupIcon);
entry->setGroup(dbTarget->rootGroup()); entry->setGroup(dbTarget->rootGroup());
QVERIFY(dbTarget->metadata()->hasCustomIcon(entryIconUuid)); QVERIFY(dbTarget->metadata()->hasCustomIcon(entryIconUuid));
QCOMPARE(dbTarget->metadata()->customIcon(entryIconUuid), entryIcon); QCOMPARE(dbTarget->metadata()->customIcon(entryIconUuid), entryIcon);
QCOMPARE(entry->icon(), entryIcon); QCOMPARE(entry->database()->metadata()->customIcon(entryIconUuid), entryIcon);
} }
void TestGroup::testClone() void TestGroup::testClone()
@ -425,11 +423,9 @@ void TestGroup::testCopyCustomIcons()
QScopedPointer<Database> dbSource(new Database()); QScopedPointer<Database> dbSource(new Database());
QScopedPointer<Database> dbTarget(new Database()); QScopedPointer<Database> dbTarget(new Database());
QImage iconImage1(1, 1, QImage::Format_RGB32); QByteArray iconImage1("icon 1");
iconImage1.setPixel(0, 0, qRgb(1, 2, 3));
QImage iconImage2(1, 1, QImage::Format_RGB32); QByteArray iconImage2("icon 2");
iconImage2.setPixel(0, 0, qRgb(4, 5, 6));
QScopedPointer<Group> group1(new Group()); QScopedPointer<Group> group1(new Group());
group1->setParent(dbSource->rootGroup()); group1->setParent(dbSource->rootGroup());
@ -469,8 +465,8 @@ void TestGroup::testCopyCustomIcons()
QVERIFY(metaTarget->hasCustomIcon(entry1IconOld)); QVERIFY(metaTarget->hasCustomIcon(entry1IconOld));
QVERIFY(metaTarget->hasCustomIcon(entry1IconNew)); QVERIFY(metaTarget->hasCustomIcon(entry1IconNew));
QCOMPARE(metaTarget->customIcon(group1Icon).pixel(0, 0), qRgb(1, 2, 3)); QCOMPARE(metaTarget->customIcon(group1Icon), iconImage1);
QCOMPARE(metaTarget->customIcon(group2Icon).pixel(0, 0), qRgb(4, 5, 6)); QCOMPARE(metaTarget->customIcon(group2Icon), iconImage2);
} }
void TestGroup::testFindEntry() void TestGroup::testFindEntry()
@ -1075,21 +1071,18 @@ void TestGroup::testApplyGroupIconRecursively()
// Set an icon per UUID to the subgroup and apply recursively // Set an icon per UUID to the subgroup and apply recursively
// -> all groups and entries except the root group have the same icon // -> all groups and entries except the root group have the same icon
const QUuid subgroupIconUuid = QUuid::createUuid(); const QUuid subgroupIconUuid = QUuid::createUuid();
QImage subgroupIcon(16, 16, QImage::Format_RGB32); QByteArray subgroupIcon("subgroup icon");
subgroupIcon.setPixel(0, 0, qRgb(255, 0, 0));
database.metadata()->addCustomIcon(subgroupIconUuid, subgroupIcon); database.metadata()->addCustomIcon(subgroupIconUuid, subgroupIcon);
subgroup->setIcon(subgroupIconUuid); subgroup->setIcon(subgroupIconUuid);
subgroup->applyGroupIconToChildGroups(); subgroup->applyGroupIconToChildGroups();
subgroup->applyGroupIconToChildEntries(); subgroup->applyGroupIconToChildEntries();
QVERIFY(database.rootGroup()->iconNumber() == rootIconNumber); QVERIFY(database.rootGroup()->iconNumber() == rootIconNumber);
QCOMPARE(subgroup->iconUuid(), subgroupIconUuid); QCOMPARE(subgroup->iconUuid(), subgroupIconUuid);
QCOMPARE(subgroup->icon(), subgroupIcon);
QCOMPARE(subgroupEntry->iconUuid(), subgroupIconUuid); QCOMPARE(subgroupEntry->iconUuid(), subgroupIconUuid);
QCOMPARE(subgroupEntry->icon(), subgroupIcon);
QCOMPARE(subsubgroup->iconUuid(), subgroupIconUuid); QCOMPARE(subsubgroup->iconUuid(), subgroupIconUuid);
QCOMPARE(subsubgroup->icon(), subgroupIcon);
QCOMPARE(subsubgroupEntry->iconUuid(), subgroupIconUuid); QCOMPARE(subsubgroupEntry->iconUuid(), subgroupIconUuid);
QCOMPARE(subsubgroupEntry->icon(), subgroupIcon); QCOMPARE(subgroup->database()->metadata()->customIcon(subgroupIconUuid), subgroupIcon);
// Reset all icons to root icon // Reset all icons to root icon
database.rootGroup()->setIcon(rootIconNumber); database.rootGroup()->setIcon(rootIconNumber);

View File

@ -114,20 +114,12 @@ void TestKeePass1Reader::testMasterKey()
void TestKeePass1Reader::testCustomIcons() void TestKeePass1Reader::testCustomIcons()
{ {
QCOMPARE(m_db->metadata()->customIconsOrder().size(), 1); QCOMPARE(m_db->metadata()->customIconsOrder().size(), 1);
QUuid uuid = m_db->metadata()->customIconsOrder().at(0);
QVERIFY(m_db->metadata()->hasCustomIcon(uuid));
QByteArray icon = m_db->metadata()->customIcon(uuid);
Entry* entry = m_db->rootGroup()->children().at(1)->entries().at(0); QVERIFY(icon.startsWith(
"\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\xF3\xFF"));
QCOMPARE(entry->icon().width(), 16);
QCOMPARE(entry->icon().height(), 16);
for (int x = 0; x < 16; x++) {
for (int y = 0; y < 16; y++) {
QRgb rgb = entry->icon().pixel(x, y);
QCOMPARE(qRed(rgb), 8);
QCOMPARE(qGreen(rgb), 160);
QCOMPARE(qBlue(rgb), 60);
}
}
} }
void TestKeePass1Reader::testGroupExpanded() void TestKeePass1Reader::testGroupExpanded()

View File

@ -113,18 +113,10 @@ void TestKeePass2Format::testXmlCustomIcons()
QCOMPARE(m_xmlDb->metadata()->customIconsOrder().size(), 1); QCOMPARE(m_xmlDb->metadata()->customIconsOrder().size(), 1);
QUuid uuid = QUuid::fromRfc4122(QByteArray::fromBase64("++vyI+daLk6omox4a6kQGA==")); QUuid uuid = QUuid::fromRfc4122(QByteArray::fromBase64("++vyI+daLk6omox4a6kQGA=="));
QVERIFY(m_xmlDb->metadata()->hasCustomIcon(uuid)); QVERIFY(m_xmlDb->metadata()->hasCustomIcon(uuid));
QImage icon = m_xmlDb->metadata()->customIcon(uuid); QByteArray icon = m_xmlDb->metadata()->customIcon(uuid);
QCOMPARE(icon.width(), 16);
QCOMPARE(icon.height(), 16);
for (int x = 0; x < 16; x++) { QVERIFY(icon.startsWith(
for (int y = 0; y < 16; y++) { "\x89PNG\r\n\x1A\n\x00\x00\x00\rIHDR\x00\x00\x00\x10\x00\x00\x00\x10\b\x06\x00\x00\x00\x1F\xF3\xFF"));
QRgb rgb = icon.pixel(x, y);
QCOMPARE(qRed(rgb), 128);
QCOMPARE(qGreen(rgb), 0);
QCOMPARE(qBlue(rgb), 128);
}
}
} }
void TestKeePass2Format::testXmlGroupRoot() void TestKeePass2Format::testXmlGroupRoot()

View File

@ -998,8 +998,7 @@ void TestMerge::testUpdateGroup()
groupSourceInitial->setName("group2 renamed"); groupSourceInitial->setName("group2 renamed");
groupSourceInitial->setNotes("updated notes"); groupSourceInitial->setNotes("updated notes");
QUuid customIconId = QUuid::createUuid(); QUuid customIconId = QUuid::createUuid();
QImage customIcon; dbSource->metadata()->addCustomIcon(customIconId, QString("custom icon").toLocal8Bit());
dbSource->metadata()->addCustomIcon(customIconId, customIcon);
groupSourceInitial->setIcon(customIconId); groupSourceInitial->setIcon(customIconId);
QPointer<Entry> entrySourceInitial = dbSource->rootGroup()->findEntryByPath("entry1"); QPointer<Entry> entrySourceInitial = dbSource->rootGroup()->findEntryByPath("entry1");
@ -1113,9 +1112,8 @@ void TestMerge::testMergeCustomIcons()
m_clock->advanceSecond(1); m_clock->advanceSecond(1);
QUuid customIconId = QUuid::createUuid(); QUuid customIconId = QUuid::createUuid();
QImage customIcon;
dbSource->metadata()->addCustomIcon(customIconId, customIcon); dbSource->metadata()->addCustomIcon(customIconId, QString("custom icon").toLocal8Bit());
// Sanity check. // Sanity check.
QVERIFY(dbSource->metadata()->hasCustomIcon(customIconId)); QVERIFY(dbSource->metadata()->hasCustomIcon(customIconId));
@ -1138,10 +1136,12 @@ void TestMerge::testMergeDuplicateCustomIcons()
m_clock->advanceSecond(1); m_clock->advanceSecond(1);
QUuid customIconId = QUuid::createUuid(); QUuid customIconId = QUuid::createUuid();
QImage customIcon;
dbSource->metadata()->addCustomIcon(customIconId, customIcon); QByteArray customIcon1 = QString("custom icon 1").toLocal8Bit();
dbDestination->metadata()->addCustomIcon(customIconId, customIcon); QByteArray customIcon2 = QString("custom icon 2").toLocal8Bit();
dbSource->metadata()->addCustomIcon(customIconId, customIcon1);
dbDestination->metadata()->addCustomIcon(customIconId, customIcon2);
// Sanity check. // Sanity check.
QVERIFY(dbSource->metadata()->hasCustomIcon(customIconId)); QVERIFY(dbSource->metadata()->hasCustomIcon(customIconId));
QVERIFY(dbDestination->metadata()->hasCustomIcon(customIconId)); QVERIFY(dbDestination->metadata()->hasCustomIcon(customIconId));
@ -1153,6 +1153,7 @@ void TestMerge::testMergeDuplicateCustomIcons()
QVERIFY(dbDestination->metadata()->hasCustomIcon(customIconId)); QVERIFY(dbDestination->metadata()->hasCustomIcon(customIconId));
QCOMPARE(dbDestination->metadata()->customIconsOrder().count(), 1); QCOMPARE(dbDestination->metadata()->customIconsOrder().count(), 1);
QCOMPARE(dbDestination->metadata()->customIcon(customIconId), customIcon2);
} }
void TestMerge::testMetadata() void TestMerge::testMetadata()

View File

@ -16,12 +16,14 @@
*/ */
#include "TestGuiPixmaps.h" #include "TestGuiPixmaps.h"
#include "core/DatabaseIcons.h"
#include "core/Metadata.h" #include "core/Metadata.h"
#include <QTest>
#include "core/Group.h" #include "core/Group.h"
#include "crypto/Crypto.h" #include "crypto/Crypto.h"
#include <QTest> #include "gui/DatabaseIcons.h"
#include "gui/Icons.h"
void TestGuiPixmaps::initTestCase() void TestGuiPixmaps::initTestCase()
{ {
@ -30,55 +32,53 @@ void TestGuiPixmaps::initTestCase()
void TestGuiPixmaps::testDatabaseIcons() void TestGuiPixmaps::testDatabaseIcons()
{ {
// check if the cache works correctly QVERIFY(!databaseIcons()->icon(0).isNull());
auto pixmap = databaseIcons()->icon(0);
auto pixmapCached = databaseIcons()->icon(0);
QCOMPARE(pixmapCached.cacheKey(), pixmap.cacheKey());
} }
void TestGuiPixmaps::testEntryIcons() void TestGuiPixmaps::testEntryIcons()
{ {
QScopedPointer<Database> db(new Database()); QScopedPointer<Database> db(new Database());
Entry* entry = new Entry(); auto entry = new Entry();
entry->setGroup(db->rootGroup()); entry->setGroup(db->rootGroup());
// Test setting standard icon // Test setting standard icon
entry->setIcon(10); entry->setIcon(10);
auto pixmap = entry->iconPixmap(); auto pixmap = Icons::entryIconPixmap(entry);
QCOMPARE(pixmap.cacheKey(), databaseIcons()->icon(10).cacheKey()); QVERIFY(pixmap.toImage() == databaseIcons()->icon(10).toImage());
// Test setting custom icon // Test setting custom icon
QUuid iconUuid = QUuid::createUuid(); QUuid iconUuid = QUuid::createUuid();
QImage icon(2, 1, QImage::Format_RGB32); QImage icon(2, 1, QImage::Format_RGB32);
icon.setPixel(0, 0, qRgb(0, 0, 0)); icon.setPixel(0, 0, qRgb(0, 0, 0));
icon.setPixel(1, 0, qRgb(0, 0, 50)); icon.setPixel(1, 0, qRgb(0, 0, 50));
db->metadata()->addCustomIcon(iconUuid, icon); db->metadata()->addCustomIcon(iconUuid, Icons::saveToBytes(icon));
QCOMPARE(db->metadata()->customIconsOrder().count(), 1);
entry->setIcon(iconUuid); entry->setIcon(iconUuid);
pixmap = entry->iconPixmap(); // Confirm the icon is the same as that stored in the database
QCOMPARE(pixmap.cacheKey(), db->metadata()->customIconPixmap(iconUuid).cacheKey()); QVERIFY(Icons::entryIconPixmap(entry).toImage() == Icons::customIconPixmap(db.data(), iconUuid).toImage());
} }
void TestGuiPixmaps::testGroupIcons() void TestGuiPixmaps::testGroupIcons()
{ {
QScopedPointer<Database> db(new Database()); QScopedPointer<Database> db(new Database());
Group* group = db->rootGroup(); auto group = db->rootGroup();
// Test setting standard icon // Test setting standard icon
group->setIcon(10); group->setIcon(10);
auto pixmap = group->iconPixmap(); auto pixmap = Icons::groupIconPixmap(group);
QCOMPARE(pixmap.cacheKey(), databaseIcons()->icon(10).cacheKey()); QVERIFY(pixmap.toImage() == databaseIcons()->icon(10).toImage());
// Test setting custom icon // Test setting custom icon
QUuid iconUuid = QUuid::createUuid(); QUuid iconUuid = QUuid::createUuid();
QImage icon(2, 1, QImage::Format_RGB32); QImage icon(2, 1, QImage::Format_RGB32);
icon.setPixel(0, 0, qRgb(0, 0, 0)); icon.setPixel(0, 0, qRgb(0, 0, 0));
icon.setPixel(1, 0, qRgb(0, 0, 50)); icon.setPixel(1, 0, qRgb(0, 0, 50));
db->metadata()->addCustomIcon(iconUuid, icon); db->metadata()->addCustomIcon(iconUuid, Icons::saveToBytes(icon));
group->setIcon(iconUuid); group->setIcon(iconUuid);
pixmap = group->iconPixmap(); // Confirm the icon is the same as that stored in the database
QCOMPARE(pixmap.cacheKey(), db->metadata()->customIconPixmap(iconUuid).cacheKey()); QVERIFY(Icons::groupIconPixmap(group).toImage() == Icons::customIconPixmap(db.data(), iconUuid).toImage());
} }
QTEST_MAIN(TestGuiPixmaps) QTEST_MAIN(TestGuiPixmaps)