Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/keepassxreboot/keepassxc.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
path: root/src/gui
diff options
context:
space:
mode:
Diffstat (limited to 'src/gui')
-rw-r--r--src/gui/DatabaseIcons.cpp119
-rw-r--r--src/gui/DatabaseIcons.h65
-rw-r--r--src/gui/DatabaseTabWidget.cpp5
-rw-r--r--src/gui/EditWidgetIcons.cpp17
-rw-r--r--src/gui/EntryPreviewWidget.cpp4
-rw-r--r--src/gui/HtmlExporter.cpp264
-rw-r--r--src/gui/HtmlExporter.h41
-rw-r--r--src/gui/IconDownloaderDialog.cpp14
-rw-r--r--src/gui/IconModels.cpp2
-rw-r--r--src/gui/Icons.cpp103
-rw-r--r--src/gui/Icons.h11
-rw-r--r--src/gui/dbsettings/DatabaseSettingsWidgetMaintenance.cpp3
-rw-r--r--src/gui/entry/EntryModel.cpp6
-rw-r--r--src/gui/group/GroupModel.cpp6
-rw-r--r--src/gui/reports/ReportsWidgetHealthcheck.cpp4
-rw-r--r--src/gui/reports/ReportsWidgetHibp.cpp4
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(" ", "&nbsp;").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("&hellip;");
+ }
+
+ 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(" &rarr; ");
+ }
+ 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("&nbsp;");
+ 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()) {