diff options
author | louib <code@louib.net> | 2021-03-13 22:07:49 +0300 |
---|---|---|
committer | Jonathan White <support@dmapps.us> | 2021-11-12 15:41:30 +0300 |
commit | 004f2b6801d93116277778611920793b7dbe0afb (patch) | |
tree | ab328622af017c0e5735ffd0588480321bbed07d /src/gui | |
parent | 6f5bbf7ad1048abc3db7e378df654a61954f77e3 (diff) |
Removing QWidget dependency from src/core.
Diffstat (limited to 'src/gui')
-rw-r--r-- | src/gui/DatabaseIcons.cpp | 119 | ||||
-rw-r--r-- | src/gui/DatabaseIcons.h | 65 | ||||
-rw-r--r-- | src/gui/DatabaseTabWidget.cpp | 5 | ||||
-rw-r--r-- | src/gui/EditWidgetIcons.cpp | 17 | ||||
-rw-r--r-- | src/gui/EntryPreviewWidget.cpp | 4 | ||||
-rw-r--r-- | src/gui/HtmlExporter.cpp | 264 | ||||
-rw-r--r-- | src/gui/HtmlExporter.h | 41 | ||||
-rw-r--r-- | src/gui/IconDownloaderDialog.cpp | 14 | ||||
-rw-r--r-- | src/gui/IconModels.cpp | 2 | ||||
-rw-r--r-- | src/gui/Icons.cpp | 103 | ||||
-rw-r--r-- | src/gui/Icons.h | 11 | ||||
-rw-r--r-- | src/gui/dbsettings/DatabaseSettingsWidgetMaintenance.cpp | 3 | ||||
-rw-r--r-- | src/gui/entry/EntryModel.cpp | 6 | ||||
-rw-r--r-- | src/gui/group/GroupModel.cpp | 6 | ||||
-rw-r--r-- | src/gui/reports/ReportsWidgetHealthcheck.cpp | 4 | ||||
-rw-r--r-- | src/gui/reports/ReportsWidgetHibp.cpp | 4 |
16 files changed, 645 insertions, 23 deletions
diff --git a/src/gui/DatabaseIcons.cpp b/src/gui/DatabaseIcons.cpp new file mode 100644 index 000000000..f83ce9ac4 --- /dev/null +++ b/src/gui/DatabaseIcons.cpp @@ -0,0 +1,119 @@ +/* + * Copyright (C) 2010 Felix Geyer <debfx@fobos.de> + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program 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, see <http://www.gnu.org/licenses/>. + */ + +#include "DatabaseIcons.h" + +#include "core/Config.h" +#include "core/Global.h" + +#include <QDir> +#include <QImageReader> +#include <QPainter> +#include <QPixmapCache> + +DatabaseIcons* DatabaseIcons::m_instance(nullptr); + +namespace +{ + const QString iconDir = QStringLiteral(":/icons/database/"); + QStringList iconList; + + const QString badgeDir = QStringLiteral(":/icons/badges/"); + QStringList badgeList; +} // namespace + +DatabaseIcons::DatabaseIcons() +{ + iconList = QDir(iconDir).entryList(QDir::NoFilter, QDir::Name); + badgeList = QDir(badgeDir).entryList(QDir::NoFilter, QDir::Name); + + // Set this early and once to ensure consistent icon size until app restart + m_compactMode = config()->get(Config::GUI_CompactMode).toBool(); +} + +DatabaseIcons* DatabaseIcons::instance() +{ + if (!m_instance) { + m_instance = new DatabaseIcons(); + } + + return m_instance; +} + +QPixmap DatabaseIcons::icon(int index, IconSize size) +{ + if (index < 0 || index >= count()) { + qWarning("DatabaseIcons::icon: invalid icon index %d, using 0 instead", index); + index = 0; + Q_ASSERT_X(false, "DatabaseIcons::icon", "invalid icon index %d"); + } + + auto cacheKey = QString::number(index); + auto icon = m_iconCache.value(cacheKey); + if (icon.isNull()) { + icon.addFile(iconDir + iconList[index]); + icon.addPixmap(icon.pixmap(64)); + m_iconCache.insert(cacheKey, icon); + } + + return icon.pixmap(iconSize(size)); +} + +QPixmap DatabaseIcons::applyBadge(const QPixmap& basePixmap, Badges badgeIndex) +{ + const auto cacheKey = QStringLiteral("badgedicon-%1-%2").arg(basePixmap.cacheKey()).arg(badgeIndex); + QPixmap pixmap = basePixmap; + if (badgeIndex < 0 || badgeIndex >= badgeList.size()) { + qWarning("DatabaseIcons: Out-of-range badge index given to applyBadge: %d", badgeIndex); + } else if (!QPixmapCache::find(cacheKey, &pixmap)) { + int baseSize = basePixmap.width(); + int badgeSize = + baseSize <= iconSize(IconSize::Default) * basePixmap.devicePixelRatio() ? baseSize * 0.6 : baseSize * 0.5; + QPoint badgePos(baseSize - badgeSize, baseSize - badgeSize); + badgePos /= basePixmap.devicePixelRatio(); + + QImageReader reader(badgeDir + badgeList[badgeIndex]); + reader.setScaledSize({badgeSize, badgeSize}); + auto badge = QPixmap::fromImageReader(&reader); + badge.setDevicePixelRatio(basePixmap.devicePixelRatio()); + + QPainter painter(&pixmap); + painter.setCompositionMode(QPainter::CompositionMode_SourceOver); + painter.drawPixmap(badgePos, badge); + + QPixmapCache::insert(cacheKey, pixmap); + } + + return pixmap; +} + +int DatabaseIcons::count() +{ + return iconList.size(); +} + +int DatabaseIcons::iconSize(IconSize size) +{ + switch (size) { + case Medium: + return m_compactMode ? 26 : 30; + case Large: + return m_compactMode ? 30 : 36; + default: + return m_compactMode ? 16 : 22; + } +} diff --git a/src/gui/DatabaseIcons.h b/src/gui/DatabaseIcons.h new file mode 100644 index 000000000..38d1590c7 --- /dev/null +++ b/src/gui/DatabaseIcons.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2010 Felix Geyer <debfx@fobos.de> + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef KEEPASSX_DATABASEICONS_H +#define KEEPASSX_DATABASEICONS_H + +#include <QIcon> + +enum IconSize +{ + Default, + Medium, + Large +}; + +class DatabaseIcons +{ +public: + static DatabaseIcons* instance(); + + static constexpr int ExpiredIconIndex = 45; + + enum Badges + { + ShareActive = 0, + ShareInactive, + Expired + }; + + QPixmap icon(int index, IconSize size = IconSize::Default); + QPixmap applyBadge(const QPixmap& basePixmap, Badges badgeIndex); + int count(); + + int iconSize(IconSize size); + +private: + DatabaseIcons(); + + static DatabaseIcons* m_instance; + QHash<QString, QIcon> m_iconCache; + bool m_compactMode; + + Q_DISABLE_COPY(DatabaseIcons) +}; + +inline DatabaseIcons* databaseIcons() +{ + return DatabaseIcons::instance(); +} + +#endif // KEEPASSX_DATABASEICONS_H diff --git a/src/gui/DatabaseTabWidget.cpp b/src/gui/DatabaseTabWidget.cpp index 9aa58f556..40d42f4e1 100644 --- a/src/gui/DatabaseTabWidget.cpp +++ b/src/gui/DatabaseTabWidget.cpp @@ -22,10 +22,13 @@ #include "autotype/AutoType.h" #include "core/Tools.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/DragTabBar.h" #include "gui/FileDialog.h" +#include "gui/HtmlExporter.h" #include "gui/MessageBox.h" #ifdef Q_OS_MACOS #include "gui/osutils/macutils/MacUtils.h" diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 3158a6c9d..c301728b6 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -25,6 +25,8 @@ #include "core/Tools.h" #include "gui/FileDialog.h" #include "gui/IconModels.h" +#include "gui/Icons.h" +#include "gui/MessageBox.h" #ifdef WITH_XC_NETWORKING #include "gui/IconDownloader.h" #endif @@ -128,7 +130,7 @@ void EditWidgetIcons::load(const QUuid& currentUuid, m_currentUuid = currentUuid; setUrl(url); - m_customIconModel->setIcons(database->metadata()->customIconsPixmaps(IconSize::Default), + m_customIconModel->setIcons(Icons::customIconsPixmaps(database.data(), IconSize::Default), database->metadata()->customIconsOrder()); QUuid iconUuid = iconStruct.uuid; @@ -231,7 +233,7 @@ void EditWidgetIcons::addCustomIconFromFile() 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 = fileDialog()->getOpenFileNames(this, tr("Select Image(s)"), FileDialog::getLastDir("icons"), filter); if (!filenames.empty()) { @@ -284,16 +286,17 @@ bool EditWidgetIcons::addCustomIcon(const QImage& icon) bool added = false; if (m_db) { // 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) { - 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()) { uuid = QUuid::createUuid(); - m_db->metadata()->addCustomIcon(uuid, scaledicon); - m_customIconModel->setIcons(m_db->metadata()->customIconsPixmaps(IconSize::Default), + m_db->metadata()->addCustomIcon(uuid, serializedIcon); + m_customIconModel->setIcons(Icons::customIconsPixmaps(m_db.data(), IconSize::Default), m_db->metadata()->customIconsOrder()); added = true; } diff --git a/src/gui/EntryPreviewWidget.cpp b/src/gui/EntryPreviewWidget.cpp index 8b283d39a..7c3b8ffd2 100644 --- a/src/gui/EntryPreviewWidget.cpp +++ b/src/gui/EntryPreviewWidget.cpp @@ -173,7 +173,7 @@ void EntryPreviewWidget::updateEntryHeaderLine() Q_ASSERT(m_currentEntry); const QString title = m_currentEntry->resolveMultiplePlaceholders(m_currentEntry->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() @@ -377,7 +377,7 @@ void EntryPreviewWidget::updateGroupHeaderLine() { Q_ASSERT(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() diff --git a/src/gui/HtmlExporter.cpp b/src/gui/HtmlExporter.cpp new file mode 100644 index 000000000..c79681637 --- /dev/null +++ b/src/gui/HtmlExporter.cpp @@ -0,0 +1,264 @@ +/* + * Copyright (C) 2019 KeePassXC Team <team@keepassxc.org> + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program 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, see <http://www.gnu.org/licenses/>. + */ + +#include "HtmlExporter.h" + +#include <QBuffer> +#include <QFile> + +#include "core/Group.h" +#include "core/Metadata.h" +#include "gui/Icons.h" + +namespace +{ + QString PixmapToHTML(const QPixmap& pixmap) + { + if (pixmap.isNull()) { + return ""; + } + + // Based on https://stackoverflow.com/a/6621278 + QByteArray a; + QBuffer buffer(&a); + pixmap.save(&buffer, "PNG"); + return QString("<img src=\"data:image/png;base64,") + a.toBase64() + "\"/>"; + } + + QString formatHTML(const QString& value) + { + return value.toHtmlEscaped().replace(" ", " ").replace('\n', "<br>"); + } + + QString formatAttribute(const QString& key, + const QString& value, + const QString& classname, + const QString& templt = QString("<tr><th>%1</th><td class=\"%2\">%3</td></tr>")) + { + const auto& formatted_attribute = templt; + if (!value.isEmpty()) { + // Format key as well -> Translations into other languages may have non-standard chars + return formatted_attribute.arg(formatHTML(key), classname, formatHTML(value)); + } + return {}; + } + + QString formatAttribute(const Entry& entry, + const QString& key, + const QString& value, + const QString& classname, + const QString& templt = QString("<tr><th>%1</th><td class=\"%2\">%3</td></tr>")) + { + if (value.isEmpty()) + return {}; + return formatAttribute(key, entry.resolveMultiplePlaceholders(value), classname, templt); + } + + QString formatEntry(const Entry& entry) + { + // Here we collect the table rows with this entry's data fields + QString item; + + // Output the fixed fields + item.append(formatAttribute(entry, QObject::tr("User name"), entry.username(), "username")); + + item.append(formatAttribute(entry, QObject::tr("Password"), entry.password(), "password")); + + if (!entry.url().isEmpty()) { + constexpr auto maxlen = 100; + QString displayedURL(formatHTML(entry.url()).mid(0, maxlen)); + + if (displayedURL.size() == maxlen) { + displayedURL.append("…"); + } + + item.append(formatAttribute(entry, + QObject::tr("URL"), + entry.url(), + "url", + R"(<tr><th>%1</th><td class="%2"><a href="%3">%4</a></td></tr>)") + .arg(entry.resolveMultiplePlaceholders(displayedURL))); + } + + item.append(formatAttribute(entry, QObject::tr("Notes"), entry.notes(), "notes")); + + // Now add the attributes (if there are any) + const auto* const attr = entry.attributes(); + if (attr && !attr->customKeys().isEmpty()) { + for (const auto& key : attr->customKeys()) { + item.append(formatAttribute(entry, key, attr->value(key), "attr")); + } + } + return item; + } +} // namespace + +bool HtmlExporter::exportDatabase(const QString& filename, const QSharedPointer<const Database>& db) +{ + QFile file(filename); + if (!file.open(QIODevice::WriteOnly | QIODevice::Truncate)) { + m_error = file.errorString(); + return false; + } + return exportDatabase(&file, db); +} + +QString HtmlExporter::errorString() const +{ + return m_error; +} + +bool HtmlExporter::exportDatabase(QIODevice* device, const QSharedPointer<const Database>& db) +{ + const auto meta = db->metadata(); + if (!meta) { + m_error = "Internal error: metadata is NULL"; + return false; + } + + const auto header = QString("<html>" + "<head>" + "<meta charset=\"UTF-8\">" + "<title>" + + meta->name().toHtmlEscaped() + + "</title>" + "<style>" + "body " + "{ font-family: \"Open Sans\", Helvetica, Arial, sans-serif; }" + "h3 " + "{ margin-left: 2em; }" + "table " + "{ margin-left: 4em; } " + "th, td " + "{ text-align: left; vertical-align: top; padding: 1px; }" + "th " + "{ min-width: 5em; width: 20%; } " + ".username, .password, .url, .attr " + "{ font-size: larger; font-family: monospace; } " + ".notes " + "{ font-size: medium; } " + "</style>" + "</head>\n" + "<body>" + "<h1>" + + meta->name().toHtmlEscaped() + + "</h1>" + "<p>" + + meta->description().toHtmlEscaped().replace("\n", "<br>") + + "</p>" + "<p><code>" + + db->filePath().toHtmlEscaped() + "</code></p>"); + const auto footer = QString("</body>" + "</html>"); + + if (device->write(header.toUtf8()) == -1) { + m_error = device->errorString(); + return false; + } + + if (db->rootGroup()) { + if (!writeGroup(*device, *db->rootGroup())) { + return false; + } + } + + if (device->write(footer.toUtf8()) == -1) { + m_error = device->errorString(); + return false; + } + + return true; +} + +bool HtmlExporter::writeGroup(QIODevice& device, const Group& group, QString path) +{ + // Don't output the recycle bin + if (&group == group.database()->metadata()->recycleBin()) { + return true; + } + + if (!path.isEmpty()) { + path.append(" → "); + } + path.append(group.name().toHtmlEscaped()); + + // Output the header for this group (but only if there are + // any notes or entries in this group, otherwise we'd get + // a header with nothing after it, which looks stupid) + const auto& entries = group.entries(); + const auto notes = group.notes(); + if (!entries.empty() || !notes.isEmpty()) { + + // Header line + auto header = QString("<hr><h2>"); + header.append(PixmapToHTML(Icons::groupIconPixmap(&group, IconSize::Medium))); + header.append(" "); + header.append(path); + header.append("</h2>\n"); + + // Group notes + if (!notes.isEmpty()) { + header.append("<p>"); + header.append(notes.toHtmlEscaped().replace("\n", "<br>")); + header.append("</p>"); + } + + // Output it + if (device.write(header.toUtf8()) == -1) { + m_error = device.errorString(); + return false; + } + } + + // Begin the table for the entries in this group + auto table = QString("<table width=\"100%\">"); + + // Output the entries in this group + for (const auto entry : entries) { + auto formatted_entry = formatEntry(*entry); + + if (formatted_entry.isEmpty()) + continue; + + // Output it into our table. First the left side with + // icon and entry title ... + table += "<tr>"; + table += "<td width=\"1%\">" + PixmapToHTML(Icons::entryIconPixmap(entry, IconSize::Medium)) + "</td>"; + table += "<td width=\"19%\" valign=\"top\"><h3>" + entry->title().toHtmlEscaped() + "</h3></td>"; + + // ... then the right side with the data fields + table += "<td style=\"padding-bottom: 0.5em;\"><table width=\"100%\">" + formatted_entry + "</table></td>"; + table += "</tr>"; + } + + // Output the complete table of this group + table.append("</table>\n"); + if (device.write(table.toUtf8()) == -1) { + m_error = device.errorString(); + return false; + } + + // Recursively output the child groups + const auto& children = group.children(); + for (const auto child : children) { + if (child && !writeGroup(device, *child, path)) { + return false; + } + } + + return true; +} diff --git a/src/gui/HtmlExporter.h b/src/gui/HtmlExporter.h new file mode 100644 index 000000000..3a592e54a --- /dev/null +++ b/src/gui/HtmlExporter.h @@ -0,0 +1,41 @@ +/* + * Copyright (C) 2019 KeePassXC Team <team@keepassxc.org> + * + * 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 + * the Free Software Foundation, either version 2 or (at your option) + * version 3 of the License. + * + * This program 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, see <http://www.gnu.org/licenses/>. + */ + +#ifndef KEEPASSX_HTMLEXPORTER_H +#define KEEPASSX_HTMLEXPORTER_H + +#include <QSharedPointer> +#include <QString> + +class Database; +class Group; +class QIODevice; + +class HtmlExporter +{ +public: + bool exportDatabase(const QString& filename, const QSharedPointer<const Database>& db); + QString errorString() const; + +private: + bool exportDatabase(QIODevice* device, const QSharedPointer<const Database>& db); + bool writeGroup(QIODevice& device, const Group& group, QString path = QString()); + + QString m_error; +}; + +#endif // KEEPASSX_HTMLEXPORTER_H diff --git a/src/gui/IconDownloaderDialog.cpp b/src/gui/IconDownloaderDialog.cpp index c2d5c8190..271a916f3 100644 --- a/src/gui/IconDownloaderDialog.cpp +++ b/src/gui/IconDownloaderDialog.cpp @@ -24,7 +24,12 @@ #include "core/Metadata.h" #include "core/Tools.h" #include "gui/IconDownloader.h" +#include "gui/IconModels.h" +#include "gui/Icons.h" #include "osutils/OSUtils.h" +#ifdef Q_OS_MACOS +#include "gui/osutils/macutils/MacUtils.h" +#endif #include <QStandardItemModel> @@ -126,15 +131,16 @@ void IconDownloaderDialog::downloadFinished(const QString& url, const QImage& ic if (m_db && !icon.isNull()) { // 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) { - 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()) { uuid = QUuid::createUuid(); - m_db->metadata()->addCustomIcon(uuid, scaledicon); + m_db->metadata()->addCustomIcon(uuid, serializedIcon); updateTable(url, tr("Ok")); } else { updateTable(url, tr("Already Exists")); diff --git a/src/gui/IconModels.cpp b/src/gui/IconModels.cpp index 3bdd9a5e6..ab435aedc 100644 --- a/src/gui/IconModels.cpp +++ b/src/gui/IconModels.cpp @@ -19,7 +19,7 @@ #include <QUuid> -#include "core/DatabaseIcons.h" +#include "gui/DatabaseIcons.h" DefaultIconModel::DefaultIconModel(QObject* parent) : QAbstractListModel(parent) diff --git a/src/gui/Icons.cpp b/src/gui/Icons.cpp index cdebdcc75..95e6aadc4 100644 --- a/src/gui/Icons.cpp +++ b/src/gui/Icons.cpp @@ -19,11 +19,20 @@ #include "Icons.h" #include <QIconEngine> +#include <QImageReader> +#include <QPaintDevice> #include <QPainter> +#include "config-keepassx.h" +#include "core/Config.h" +#include "gui/DatabaseIcons.h" #include "gui/MainWindow.h" #include "gui/osutils/OSUtils.h" +#ifdef WITH_XC_KEESHARE +#include "keeshare/KeeShare.h" +#endif + class AdaptiveIconEngine : public QIconEngine { public: @@ -206,3 +215,97 @@ Icons* Icons::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; +} diff --git a/src/gui/Icons.h b/src/gui/Icons.h index d02fd79c4..01af0b289 100644 --- a/src/gui/Icons.h +++ b/src/gui/Icons.h @@ -21,6 +21,9 @@ #include <QIcon> +#include <core/Database.h> +#include <gui/DatabaseIcons.h> + class Icons { public: @@ -32,6 +35,14 @@ public: QIcon icon(const QString& name, bool recolor = true, const QColor& overrideColor = QColor::Invalid); 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(); private: diff --git a/src/gui/dbsettings/DatabaseSettingsWidgetMaintenance.cpp b/src/gui/dbsettings/DatabaseSettingsWidgetMaintenance.cpp index 12738a987..09650c6b0 100644 --- a/src/gui/dbsettings/DatabaseSettingsWidgetMaintenance.cpp +++ b/src/gui/dbsettings/DatabaseSettingsWidgetMaintenance.cpp @@ -21,6 +21,7 @@ #include "core/Group.h" #include "core/Metadata.h" #include "gui/IconModels.h" +#include "gui/Icons.h" #include "gui/MessageBox.h" DatabaseSettingsWidgetMaintenance::DatabaseSettingsWidgetMaintenance(QWidget* parent) @@ -47,7 +48,7 @@ DatabaseSettingsWidgetMaintenance::~DatabaseSettingsWidgetMaintenance() 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()); m_ui->deleteButton->setEnabled(false); } diff --git a/src/gui/entry/EntryModel.cpp b/src/gui/entry/EntryModel.cpp index 7b61da042..313b7ee50 100644 --- a/src/gui/entry/EntryModel.cpp +++ b/src/gui/entry/EntryModel.cpp @@ -21,9 +21,11 @@ #include <QMimeData> #include <QPalette> +#include "core/Entry.h" #include "core/Group.h" #include "core/Metadata.h" #include "core/PasswordHealth.h" +#include "gui/DatabaseIcons.h" #include "gui/Icons.h" #include "gui/styles/StateColorPalette.h" #ifdef Q_OS_MACOS @@ -277,11 +279,11 @@ QVariant EntryModel::data(const QModelIndex& index, int role) const switch (index.column()) { case ParentGroup: if (entry->group()) { - return entry->group()->iconPixmap(); + return Icons::groupIconPixmap(entry->group()); } break; case Title: - return entry->iconPixmap(); + return Icons::entryIconPixmap(entry); case Paperclip: if (!entry->attachments()->isEmpty()) { return icons()->icon("paperclip"); diff --git a/src/gui/group/GroupModel.cpp b/src/gui/group/GroupModel.cpp index 9a2b745ea..c21d1f911 100644 --- a/src/gui/group/GroupModel.cpp +++ b/src/gui/group/GroupModel.cpp @@ -19,8 +19,12 @@ #include <QMimeData> +#include "core/Database.h" #include "core/Group.h" #include "core/Metadata.h" +#include "core/Tools.h" +#include "gui/DatabaseIcons.h" +#include "gui/Icons.h" #include "keeshare/KeeShare.h" GroupModel::GroupModel(Database* db, QObject* parent) @@ -126,7 +130,7 @@ QVariant GroupModel::data(const QModelIndex& index, int role) const #endif return nameTemplate.arg(group->name()); } else if (role == Qt::DecorationRole) { - return group->iconPixmap(); + return Icons::groupIconPixmap(group); } else if (role == Qt::FontRole) { QFont font; if (group->isExpired()) { diff --git a/src/gui/reports/ReportsWidgetHealthcheck.cpp b/src/gui/reports/ReportsWidgetHealthcheck.cpp index e6b441a4d..d23829031 100644 --- a/src/gui/reports/ReportsWidgetHealthcheck.cpp +++ b/src/gui/reports/ReportsWidgetHealthcheck.cpp @@ -203,8 +203,8 @@ void ReportsWidgetHealthcheck::addHealthRow(QSharedPointer<PasswordHealth> healt auto row = QList<QStandardItem*>(); row << new QStandardItem(descr); - row << new QStandardItem(entry->iconPixmap(), title); - row << new QStandardItem(group->iconPixmap(), group->hierarchy().join("/")); + row << new QStandardItem(Icons::entryIconPixmap(entry), title); + row << new QStandardItem(Icons::groupIconPixmap(group), group->hierarchy().join("/")); row << new QStandardItem(QString::number(health->score())); row << new QStandardItem(health->scoreReason()); diff --git a/src/gui/reports/ReportsWidgetHibp.cpp b/src/gui/reports/ReportsWidgetHibp.cpp index f84be1a18..86be3d92f 100644 --- a/src/gui/reports/ReportsWidgetHibp.cpp +++ b/src/gui/reports/ReportsWidgetHibp.cpp @@ -163,8 +163,8 @@ void ReportsWidgetHibp::makeHibpTable() } auto row = QList<QStandardItem*>(); - row << new QStandardItem(entry->iconPixmap(), title) - << new QStandardItem(group->iconPixmap(), group->hierarchy().join("/")) + row << new QStandardItem(Icons::entryIconPixmap(entry), title) + << new QStandardItem(Icons::groupIconPixmap(group), group->hierarchy().join("/")) << new QStandardItem(countToText(count)); if (entry->excludeFromReports()) { |