From cb0b948603d9876f5a9db9ee5a90c26c1676b658 Mon Sep 17 00:00:00 2001 From: Jonathan White Date: Sun, 24 Sep 2017 17:53:42 -0400 Subject: [PATCH] Multiple fixes to custom icon downloading * Fixes #904, icons are saved at or below 128x128 * Fixes #403, crash occurs due to dialog on non-gui thread * Fixes #232, icon hashes calculated and compared against --- src/core/Metadata.cpp | 22 ++++++++++++++ src/core/Metadata.h | 4 +++ src/gui/EditWidgetIcons.cpp | 59 ++++++++++++++++++++++--------------- src/gui/EditWidgetIcons.h | 3 +- 4 files changed, 64 insertions(+), 24 deletions(-) diff --git a/src/core/Metadata.cpp b/src/core/Metadata.cpp index a7207b592..eb976d0e5 100644 --- a/src/core/Metadata.cpp +++ b/src/core/Metadata.cpp @@ -15,6 +15,7 @@ * along with this program. If not, see . */ +#include #include "Metadata.h" #include "core/Entry.h" @@ -390,6 +391,9 @@ void Metadata::addCustomIcon(const Uuid& uuid, const QImage& icon) m_customIconCacheKeys[uuid] = QPixmapCache::Key(); m_customIconScaledCacheKeys[uuid] = QPixmapCache::Key(); m_customIconsOrder.append(uuid); + // Associate image hash to uuid + QByteArray hash = hashImage(icon); + m_customIconsHashes[hash] = uuid; Q_ASSERT(m_customIcons.count() == m_customIconsOrder.count()); emit modified(); } @@ -415,6 +419,12 @@ void Metadata::removeCustomIcon(const Uuid& uuid) Q_ASSERT(!uuid.isNull()); Q_ASSERT(m_customIcons.contains(uuid)); + // Remove hash record only if this is the same uuid + QByteArray hash = hashImage(m_customIcons[uuid]); + if (m_customIconsHashes.contains(hash) && m_customIconsHashes[hash] == uuid) { + m_customIconsHashes.remove(hash); + } + m_customIcons.remove(uuid); QPixmapCache::remove(m_customIconCacheKeys.value(uuid)); m_customIconCacheKeys.remove(uuid); @@ -425,6 +435,12 @@ void Metadata::removeCustomIcon(const Uuid& uuid) emit modified(); } +Uuid Metadata::findCustomIcon(const QImage &candidate) +{ + QByteArray hash = hashImage(candidate); + return m_customIconsHashes.value(hash, Uuid()); +} + void Metadata::copyCustomIcons(const QSet& iconList, const Metadata* otherMetadata) { for (const Uuid& uuid : iconList) { @@ -436,6 +452,12 @@ void Metadata::copyCustomIcons(const QSet& iconList, const Metadata* other } } +QByteArray Metadata::hashImage(const QImage& image) +{ + auto data = QByteArray((char*)image.bits(), image.byteCount()); + return QCryptographicHash::hash(data, QCryptographicHash::Md5); +} + void Metadata::setRecycleBinEnabled(bool value) { set(m_data.recycleBinEnabled, value); diff --git a/src/core/Metadata.h b/src/core/Metadata.h index 4f435d759..2104868c2 100644 --- a/src/core/Metadata.h +++ b/src/core/Metadata.h @@ -122,6 +122,7 @@ public: void addCustomIconScaled(const Uuid& uuid, const QImage& icon); void removeCustomIcon(const Uuid& uuid); void copyCustomIcons(const QSet& iconList, const Metadata* otherMetadata); + Uuid findCustomIcon(const QImage& candidate); void setRecycleBinEnabled(bool value); void setRecycleBin(Group* group); void setRecycleBinChanged(const QDateTime& value); @@ -154,12 +155,15 @@ private: template bool set(P& property, const V& value); template bool set(P& property, const V& value, QDateTime& dateTime); + QByteArray hashImage(const QImage& image); + MetadataData m_data; QHash m_customIcons; mutable QHash m_customIconCacheKeys; mutable QHash m_customIconScaledCacheKeys; QList m_customIconsOrder; + QHash m_customIconsHashes; QPointer m_recycleBin; QDateTime m_recycleBinChanged; diff --git a/src/gui/EditWidgetIcons.cpp b/src/gui/EditWidgetIcons.cpp index 1ab9dfe4a..691e93210 100644 --- a/src/gui/EditWidgetIcons.cpp +++ b/src/gui/EditWidgetIcons.cpp @@ -68,7 +68,7 @@ EditWidgetIcons::EditWidgetIcons(QWidget* parent) this, SLOT(updateWidgetsDefaultIcons(bool))); connect(m_ui->customIconsRadio, SIGNAL(toggled(bool)), this, SLOT(updateWidgetsCustomIcons(bool))); - connect(m_ui->addButton, SIGNAL(clicked()), SLOT(addCustomIcon())); + connect(m_ui->addButton, SIGNAL(clicked()), SLOT(addCustomIconFromFile())); connect(m_ui->deleteButton, SIGNAL(clicked()), SLOT(removeCustomIcon())); connect(m_ui->faviconButton, SIGNAL(clicked()), SLOT(downloadFavicon())); @@ -185,15 +185,7 @@ void EditWidgetIcons::fetchFavicon(const QUrl& url) image.loadFromData(response->collectedData()); if (!image.isNull()) { - //Set the image - Uuid uuid = Uuid::random(); - m_database->metadata()->addCustomIcon(uuid, image.scaled(16, 16)); - m_customIconModel->setIcons(m_database->metadata()->customIconsScaledPixmaps(), - m_database->metadata()->customIconsOrder()); - QModelIndex index = m_customIconModel->indexFromUuid(uuid); - m_ui->customIconsView->setCurrentIndex(index); - m_ui->customIconsRadio->setChecked(true); - + addCustomIcon(image); resetFaviconDownload(); } else { fetchFaviconFromGoogle(url.host()); @@ -226,7 +218,9 @@ void EditWidgetIcons::fetchFavicon(const QUrl& url) QUrl tempurl = QUrl(m_url); if (tempurl.scheme() == "http") { resetFaviconDownload(); - MessageBox::warning(this, tr("Error"), tr("Unable to fetch favicon.") + "\n" + tr("Hint: You can enable Google as a fallback under Tools>Settings>Security")); + emit messageEditEntry(tr("Unable to fetch favicon.") + "\n" + + tr("Hint: You can enable Google as a fallback under Tools>Settings>Security"), + MessageWidget::Error); } else { tempurl.setScheme("http"); m_url = tempurl.url(); @@ -248,7 +242,7 @@ void EditWidgetIcons::fetchFaviconFromGoogle(const QString& domain) fetchFavicon(faviconUrl); } else { resetFaviconDownload(); - MessageBox::warning(this, tr("Error"), tr("Unable to fetch favicon.")); + emit messageEditEntry(tr("Unable to fetch favicon."), MessageWidget::Error); } } @@ -269,7 +263,7 @@ void EditWidgetIcons::resetFaviconDownload(bool clearRedirect) } #endif -void EditWidgetIcons::addCustomIcon() +void EditWidgetIcons::addCustomIconFromFile() { if (m_database) { QString filter = QString("%1 (%2);;%3 (*)").arg(tr("Images"), @@ -278,22 +272,41 @@ void EditWidgetIcons::addCustomIcon() QString filename = QFileDialog::getOpenFileName( this, tr("Select Image"), "", filter); if (!filename.isEmpty()) { - QImage image(filename); - if (!image.isNull()) { - Uuid uuid = Uuid::random(); - m_database->metadata()->addCustomIcon(uuid, image.scaled(16, 16)); - m_customIconModel->setIcons(m_database->metadata()->customIconsScaledPixmaps(), - m_database->metadata()->customIconsOrder()); - QModelIndex index = m_customIconModel->indexFromUuid(uuid); - m_ui->customIconsView->setCurrentIndex(index); - } - else { + auto icon = QImage(filename); + if (!icon.isNull()) { + addCustomIcon(QImage(filename)); + } else { emit messageEditEntry(tr("Can't read icon"), MessageWidget::Error); } } } } +void EditWidgetIcons::addCustomIcon(const QImage &icon) +{ + if (m_database) { + Uuid uuid = m_database->metadata()->findCustomIcon(icon); + if (uuid.isNull()) { + uuid = Uuid::random(); + // Don't add an icon larger than 128x128, but retain original size if smaller + if (icon.width() > 128 || icon.height() > 128) { + m_database->metadata()->addCustomIcon(uuid, icon.scaled(128, 128)); + } else { + m_database->metadata()->addCustomIcon(uuid, icon); + } + + m_customIconModel->setIcons(m_database->metadata()->customIconsScaledPixmaps(), + m_database->metadata()->customIconsOrder()); + } else { + emit messageEditEntry(tr("Custom icon already exists"), MessageWidget::Information); + } + + // Select the new or existing icon + QModelIndex index = m_customIconModel->indexFromUuid(uuid); + m_ui->customIconsView->setCurrentIndex(index); + } +} + void EditWidgetIcons::removeCustomIcon() { if (m_database) { diff --git a/src/gui/EditWidgetIcons.h b/src/gui/EditWidgetIcons.h index 3cc191d73..5c27ffe29 100644 --- a/src/gui/EditWidgetIcons.h +++ b/src/gui/EditWidgetIcons.h @@ -78,7 +78,8 @@ private slots: void fetchFaviconFromGoogle(const QString& domain); void resetFaviconDownload(bool clearRedirect = true); #endif - void addCustomIcon(); + void addCustomIconFromFile(); + void addCustomIcon(const QImage& icon); void removeCustomIcon(); void updateWidgetsDefaultIcons(bool checked); void updateWidgetsCustomIcons(bool checked);