diff options
author | Janek Bevendorff <janek@jbev.net> | 2020-03-09 03:27:16 +0300 |
---|---|---|
committer | Jonathan White <support@dmapps.us> | 2020-03-14 17:13:12 +0300 |
commit | b045160e4fcd612def4c5cff55f9469baf12a738 (patch) | |
tree | 6c262e2a48355bb054400b647a3d515f54b682fe /src/core | |
parent | 4ff781fa4846c7e1d9d90230dac9bc2f14369fa4 (diff) |
Bundle icons using the Qt resource system.
Simplify resource loading logic and enable reproducible builds.
Fixes #2582
Diffstat (limited to 'src/core')
-rw-r--r-- | src/core/Bootstrap.cpp | 1 | ||||
-rw-r--r-- | src/core/DatabaseIcons.cpp | 13 | ||||
-rw-r--r-- | src/core/FilePath.cpp | 259 | ||||
-rw-r--r-- | src/core/PassphraseGenerator.cpp | 4 | ||||
-rw-r--r-- | src/core/Resources.cpp | 232 | ||||
-rw-r--r-- | src/core/Resources.h (renamed from src/core/FilePath.h) | 27 | ||||
-rw-r--r-- | src/core/Translator.cpp | 6 |
7 files changed, 257 insertions, 285 deletions
diff --git a/src/core/Bootstrap.cpp b/src/core/Bootstrap.cpp index c983253f0..99b950928 100644 --- a/src/core/Bootstrap.cpp +++ b/src/core/Bootstrap.cpp @@ -70,6 +70,7 @@ namespace Bootstrap #ifdef QT_NO_DEBUG disableCoreDumps(); #endif + setupSearchPaths(); applyEarlyQNetworkAccessManagerWorkaround(); Translator::installTranslators(); diff --git a/src/core/DatabaseIcons.cpp b/src/core/DatabaseIcons.cpp index 6219d41f5..70c5c19c9 100644 --- a/src/core/DatabaseIcons.cpp +++ b/src/core/DatabaseIcons.cpp @@ -17,7 +17,7 @@ #include "DatabaseIcons.h" -#include "core/FilePath.h" +#include "core/Resources.h" DatabaseIcons* DatabaseIcons::m_instance(nullptr); const int DatabaseIcons::IconCount(69); @@ -103,18 +103,15 @@ QImage DatabaseIcons::icon(int index) { if (index < 0 || index >= IconCount) { qWarning("DatabaseIcons::icon: invalid icon index %d", index); - return QImage(); + return {}; } if (!m_iconCache[index].isNull()) { return m_iconCache[index]; - } else { - QString iconPath = QString("icons/database/").append(m_indexToName[index]); - QImage icon(filePath()->dataPath(iconPath)); - - m_iconCache[index] = icon; - return icon; } + QImage icon(QStringLiteral(":/icons/database/").append(m_indexToName[index])); + m_iconCache[index] = icon; + return icon; } QPixmap DatabaseIcons::iconPixmap(int index) diff --git a/src/core/FilePath.cpp b/src/core/FilePath.cpp deleted file mode 100644 index 6ae52bd8f..000000000 --- a/src/core/FilePath.cpp +++ /dev/null @@ -1,259 +0,0 @@ -/* - * Copyright (C) 2017 KeePassXC Team <team@keepassxc.org> - * Copyright (C) 2011 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 "FilePath.h" - -#include <QBitmap> -#include <QDir> -#include <QLibrary> -#include <QStyle> - -#include "config-keepassx.h" -#include "core/Config.h" -#include "core/Global.h" -#include "gui/MainWindow.h" - -FilePath* FilePath::m_instance(nullptr); - -QString FilePath::dataPath(const QString& name) -{ - if (name.isEmpty() || name.startsWith('/')) { - return m_dataPath + name; - } else { - return m_dataPath + "/" + name; - } -} - -QString FilePath::pluginPath(const QString& name) -{ - QStringList pluginPaths; - - QDir buildDir(QCoreApplication::applicationDirPath() + "/autotype"); - const QStringList buildDirEntryList = buildDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); - for (const QString& dir : buildDirEntryList) { - pluginPaths << QCoreApplication::applicationDirPath() + "/autotype/" + dir; - } - - // for TestAutoType - pluginPaths << QCoreApplication::applicationDirPath() + "/../src/autotype/test"; - -#if defined(Q_OS_MACOS) && defined(WITH_APP_BUNDLE) - pluginPaths << QCoreApplication::applicationDirPath() + "/../PlugIns"; -#endif - - pluginPaths << QCoreApplication::applicationDirPath(); - - QString configuredPluginDir = KEEPASSX_PLUGIN_DIR; - if (configuredPluginDir != ".") { - if (QDir(configuredPluginDir).isAbsolute()) { - pluginPaths << configuredPluginDir; - } else { - QString relativePluginDir = - QString("%1/../%2").arg(QCoreApplication::applicationDirPath(), configuredPluginDir); - pluginPaths << QDir(relativePluginDir).canonicalPath(); - - QString absolutePluginDir = QString("%1/%2").arg(KEEPASSX_PREFIX_DIR, configuredPluginDir); - pluginPaths << QDir(absolutePluginDir).canonicalPath(); - } - } - - QStringList dirFilter; - dirFilter << QString("*%1*").arg(name); - - for (const QString& path : asConst(pluginPaths)) { - const QStringList fileCandidates = QDir(path).entryList(dirFilter, QDir::Files); - - for (const QString& file : fileCandidates) { - QString filePath = path + "/" + file; - - if (QLibrary::isLibrary(filePath)) { - return filePath; - } - } - } - - return QString(); -} - -QString FilePath::wordlistPath(const QString& name) -{ - return dataPath("wordlists/" + name); -} - -QIcon FilePath::applicationIcon() -{ - return icon("apps", "keepassxc", false); -} - -QIcon FilePath::trayIcon() -{ - return useDarkIcon() ? icon("apps", "keepassxc-dark", false) : icon("apps", "keepassxc", false); -} - -QIcon FilePath::trayIconLocked() -{ - return icon("apps", "keepassxc-locked", false); -} - -QIcon FilePath::trayIconUnlocked() -{ - return useDarkIcon() ? icon("apps", "keepassxc-dark", false) : icon("apps", "keepassxc-unlocked", false); -} - -QIcon FilePath::icon(const QString& category, const QString& name, bool recolor) -{ - QString combinedName = category + "/" + name; - QIcon icon = m_iconCache.value(combinedName); - - if (!icon.isNull()) { - return icon; - } - - if (icon.isNull()) { - const QList<int> pngSizes = {16, 22, 24, 32, 48, 64, 128}; - QString filename; - for (int size : pngSizes) { - filename = - QString("%1/icons/application/%2x%2/%3.png").arg(m_dataPath, QString::number(size), combinedName); - if (QFile::exists(filename)) { - icon.addFile(filename, QSize(size, size)); - } - } - filename = QString("%1/icons/application/scalable/%2.svg").arg(m_dataPath, combinedName); - if (QFile::exists(filename) && getMainWindow() && recolor) { - QPalette palette = getMainWindow()->palette(); - - QFile f(filename); - QIcon scalable(filename); - QPixmap pixmap = scalable.pixmap({128, 128}); - - auto mask = QBitmap::fromImage(pixmap.toImage().createAlphaMask()); - pixmap.fill(palette.color(QPalette::WindowText)); - pixmap.setMask(mask); - icon.addPixmap(pixmap, QIcon::Mode::Normal); - - pixmap.fill(palette.color(QPalette::HighlightedText)); - pixmap.setMask(mask); - icon.addPixmap(pixmap, QIcon::Mode::Selected); - - pixmap.fill(palette.color(QPalette::Disabled, QPalette::WindowText)); - pixmap.setMask(mask); - icon.addPixmap(pixmap, QIcon::Mode::Disabled); - -#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) - icon.setIsMask(true); -#endif - } else if (QFile::exists(filename)) { - icon.addFile(filename); - } - } - - m_iconCache.insert(combinedName, icon); - - return icon; -} - -QIcon FilePath::onOffIcon(const QString& category, const QString& name, bool recolor) -{ - QString combinedName = category + "/" + name; - QString cacheName = "onoff/" + combinedName; - - QIcon icon = m_iconCache.value(cacheName); - - if (!icon.isNull()) { - return icon; - } - - QIcon on = FilePath::icon(category, name + "-on", recolor); - for (const auto& size : on.availableSizes()) { - icon.addPixmap(on.pixmap(size, QIcon::Mode::Normal), QIcon::Mode::Normal, QIcon::On); - icon.addPixmap(on.pixmap(size, QIcon::Mode::Selected), QIcon::Mode::Selected, QIcon::On); - icon.addPixmap(on.pixmap(size, QIcon::Mode::Disabled), QIcon::Mode::Disabled, QIcon::On); - } - QIcon off = FilePath::icon(category, name + "-off", recolor); - for (const auto& size : off.availableSizes()) { - icon.addPixmap(off.pixmap(size, QIcon::Mode::Normal), QIcon::Mode::Normal, QIcon::Off); - icon.addPixmap(off.pixmap(size, QIcon::Mode::Selected), QIcon::Mode::Selected, QIcon::Off); - icon.addPixmap(off.pixmap(size, QIcon::Mode::Disabled), QIcon::Mode::Disabled, QIcon::Off); - } - - m_iconCache.insert(cacheName, icon); - - return icon; -} - -FilePath::FilePath() -{ - const QString appDirPath = QCoreApplication::applicationDirPath(); - bool isDataDirAbsolute = QDir::isAbsolutePath(KEEPASSX_DATA_DIR); - Q_UNUSED(isDataDirAbsolute); - - if (false) { - } -#ifdef QT_DEBUG - else if (testSetDir(QString(KEEPASSX_SOURCE_DIR) + "/share")) { - } -#endif -#if defined(Q_OS_UNIX) && !(defined(Q_OS_MACOS) && defined(WITH_APP_BUNDLE)) - else if (isDataDirAbsolute && testSetDir(KEEPASSX_DATA_DIR)) { - } else if (!isDataDirAbsolute && testSetDir(QString("%1/../%2").arg(appDirPath, KEEPASSX_DATA_DIR))) { - } else if (!isDataDirAbsolute && testSetDir(QString("%1/%2").arg(KEEPASSX_PREFIX_DIR, KEEPASSX_DATA_DIR))) { - } -#endif -#if defined(Q_OS_MACOS) && defined(WITH_APP_BUNDLE) - else if (testSetDir(appDirPath + "/../Resources")) { - } -#endif -#ifdef Q_OS_WIN - else if (testSetDir(appDirPath + "/share")) { - } -#endif - // Last ditch test when running in the build directory (mainly for travis tests) - else if (testSetDir(QString(KEEPASSX_SOURCE_DIR) + "/share")) { - } - - if (m_dataPath.isEmpty()) { - qWarning("FilePath::DataPath: can't find data dir"); - } else { - m_dataPath = QDir::cleanPath(m_dataPath); - } -} - -bool FilePath::testSetDir(const QString& dir) -{ - if (QFile::exists(dir + "/icons/database/C00_Password.png")) { - m_dataPath = dir; - return true; - } else { - return false; - } -} - -bool FilePath::useDarkIcon() -{ - return config()->get("GUI/DarkTrayIcon").toBool(); -} - -FilePath* FilePath::instance() -{ - if (!m_instance) { - m_instance = new FilePath(); - } - - return m_instance; -} diff --git a/src/core/PassphraseGenerator.cpp b/src/core/PassphraseGenerator.cpp index b14886a1a..57dd2bb57 100644 --- a/src/core/PassphraseGenerator.cpp +++ b/src/core/PassphraseGenerator.cpp @@ -21,7 +21,7 @@ #include <QTextStream> #include <cmath> -#include "core/FilePath.h" +#include "core/Resources.h" #include "crypto/Random.h" const char* PassphraseGenerator::DefaultSeparator = " "; @@ -80,7 +80,7 @@ void PassphraseGenerator::setWordList(const QString& path) void PassphraseGenerator::setDefaultWordList() { - const QString path = filePath()->wordlistPath(PassphraseGenerator::DefaultWordList); + const QString path = resources()->wordlistPath(PassphraseGenerator::DefaultWordList); setWordList(path); } diff --git a/src/core/Resources.cpp b/src/core/Resources.cpp new file mode 100644 index 000000000..d6c57d7ae --- /dev/null +++ b/src/core/Resources.cpp @@ -0,0 +1,232 @@ +/* + * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org> + * Copyright (C) 2011 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 "Resources.h" + +#include <QBitmap> +#include <QDir> +#include <QLibrary> +#include <QStyle> + +#include "config-keepassx.h" +#include "core/Config.h" +#include "core/Global.h" +#include "gui/MainWindow.h" + +Resources* Resources::m_instance(nullptr); + +QString Resources::dataPath(const QString& name) +{ + if (name.isEmpty() || name.startsWith('/')) { + return m_dataPath + name; + } + return m_dataPath + "/" + name; +} + +QString Resources::pluginPath(const QString& name) +{ + QStringList pluginPaths; + + QDir buildDir(QCoreApplication::applicationDirPath() + "/autotype"); + const QStringList buildDirEntryList = buildDir.entryList(QDir::Dirs | QDir::NoDotAndDotDot); + for (const QString& dir : buildDirEntryList) { + pluginPaths << QCoreApplication::applicationDirPath() + "/autotype/" + dir; + } + + // for TestAutoType + pluginPaths << QCoreApplication::applicationDirPath() + "/../src/autotype/test"; + +#if defined(Q_OS_MACOS) && defined(WITH_APP_BUNDLE) + pluginPaths << QCoreApplication::applicationDirPath() + "/../PlugIns"; +#endif + + pluginPaths << QCoreApplication::applicationDirPath(); + + QString configuredPluginDir = KEEPASSX_PLUGIN_DIR; + if (configuredPluginDir != ".") { + if (QDir(configuredPluginDir).isAbsolute()) { + pluginPaths << configuredPluginDir; + } else { + QString relativePluginDir = + QStringLiteral("%1/../%2").arg(QCoreApplication::applicationDirPath(), configuredPluginDir); + pluginPaths << QDir(relativePluginDir).canonicalPath(); + + QString absolutePluginDir = QStringLiteral("%1/%2").arg(KEEPASSX_PREFIX_DIR, configuredPluginDir); + pluginPaths << QDir(absolutePluginDir).canonicalPath(); + } + } + + QStringList dirFilter; + dirFilter << QStringLiteral("*%1*").arg(name); + + for (const QString& path : asConst(pluginPaths)) { + const QStringList fileCandidates = QDir(path).entryList(dirFilter, QDir::Files); + + for (const QString& file : fileCandidates) { + QString filePath = path + "/" + file; + + if (QLibrary::isLibrary(filePath)) { + return filePath; + } + } + } + + return {}; +} + +QString Resources::wordlistPath(const QString& name) +{ + return dataPath(QStringLiteral("wordlists/%1").arg(name)); +} + +QIcon Resources::applicationIcon() +{ + return icon("keepassxc", false); +} + +QIcon Resources::trayIcon() +{ + return useDarkIcon() ? icon("keepassxc-dark", false) : icon("keepassxc", false); +} + +QIcon Resources::trayIconLocked() +{ + return icon("keepassxc-locked", false); +} + +QIcon Resources::trayIconUnlocked() +{ + return useDarkIcon() ? icon("keepassxc-dark", false) : icon("keepassxc-unlocked", false); +} + +QIcon Resources::icon(const QString& name, bool recolor) +{ + QIcon icon = m_iconCache.value(name); + + if (!icon.isNull()) { + return icon; + } + + icon = QIcon::fromTheme(name); + if (getMainWindow() && recolor) { + QPixmap pixmap = icon.pixmap(128, 128); + icon = {}; + + QPalette palette = getMainWindow()->palette(); + + auto mask = QBitmap::fromImage(pixmap.toImage().createAlphaMask()); + pixmap.fill(palette.color(QPalette::WindowText)); + pixmap.setMask(mask); + icon.addPixmap(pixmap, QIcon::Mode::Normal); + + pixmap.fill(palette.color(QPalette::HighlightedText)); + pixmap.setMask(mask); + icon.addPixmap(pixmap, QIcon::Mode::Selected); + + pixmap.fill(palette.color(QPalette::Disabled, QPalette::WindowText)); + pixmap.setMask(mask); + icon.addPixmap(pixmap, QIcon::Mode::Disabled); + +#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0) + icon.setIsMask(true); +#endif + } + + m_iconCache.insert(name, icon); + + return icon; +} + +QIcon Resources::onOffIcon(const QString& name, bool recolor) +{ + QString cacheName = "onoff/" + name; + + QIcon icon = m_iconCache.value(cacheName); + + if (!icon.isNull()) { + return icon; + } + + QIcon on = Resources::icon(name + "-on", recolor); + for (const auto& size : on.availableSizes()) { + icon.addPixmap(on.pixmap(size, QIcon::Mode::Normal), QIcon::Mode::Normal, QIcon::On); + icon.addPixmap(on.pixmap(size, QIcon::Mode::Selected), QIcon::Mode::Selected, QIcon::On); + icon.addPixmap(on.pixmap(size, QIcon::Mode::Disabled), QIcon::Mode::Disabled, QIcon::On); + } + QIcon off = Resources::icon(name + "-off", recolor); + for (const auto& size : off.availableSizes()) { + icon.addPixmap(off.pixmap(size, QIcon::Mode::Normal), QIcon::Mode::Normal, QIcon::Off); + icon.addPixmap(off.pixmap(size, QIcon::Mode::Selected), QIcon::Mode::Selected, QIcon::Off); + icon.addPixmap(off.pixmap(size, QIcon::Mode::Disabled), QIcon::Mode::Disabled, QIcon::Off); + } + + m_iconCache.insert(cacheName, icon); + + return icon; +} + +Resources::Resources() +{ + const QString appDirPath = QCoreApplication::applicationDirPath(); +#if defined(Q_OS_UNIX) && !(defined(Q_OS_MACOS) && defined(WITH_APP_BUNDLE)) + testResourceDir(KEEPASSX_DATA_DIR) || testResourceDir(QStringLiteral("%1/../%2").arg(appDirPath, KEEPASSX_DATA_DIR)) + || testResourceDir(QStringLiteral("%1/%2").arg(KEEPASSX_PREFIX_DIR, KEEPASSX_DATA_DIR)); +#elif defined(Q_OS_MACOS) && defined(WITH_APP_BUNDLE) + testResourceDir(appDirPath + QStringLiteral("/../Resources")); +#elif defined(Q_OS_WIN) + testResourceDir(appDirPath + QStringLiteral("/share")); +#endif + + if (m_dataPath.isEmpty()) { + // Last ditch check if we are running from inside the src or test build directory + testResourceDir(appDirPath + QStringLiteral("/../../share")) + || testResourceDir(appDirPath + QStringLiteral("/../share")) + || testResourceDir(appDirPath + QStringLiteral("/../../../share")); + } + + if (m_dataPath.isEmpty()) { + qWarning("Resources::DataPath: can't find data dir"); + } +} + +bool Resources::testResourceDir(const QString& dir) +{ + if (QFile::exists(dir + QStringLiteral("/icons/application/256x256/apps/keepassxc.png"))) { + m_dataPath = QDir::cleanPath(dir); + return true; + } + return false; +} + +bool Resources::useDarkIcon() +{ + return config()->get("GUI/DarkTrayIcon").toBool(); +} + +Resources* Resources::instance() +{ + if (!m_instance) { + m_instance = new Resources(); + + Q_INIT_RESOURCE(icons); + QIcon::setThemeSearchPaths({":/icons"}); + QIcon::setThemeName("application"); + } + + return m_instance; +} diff --git a/src/core/FilePath.h b/src/core/Resources.h index 008dfc33e..1f506b78e 100644 --- a/src/core/FilePath.h +++ b/src/core/Resources.h @@ -1,4 +1,5 @@ /* + * Copyright (C) 2020 KeePassXC Team <team@keepassxc.org> * Copyright (C) 2011 Felix Geyer <debfx@fobos.de> * * This program is free software: you can redistribute it and/or modify @@ -15,14 +16,14 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#ifndef KEEPASSX_FILEPATH_H -#define KEEPASSX_FILEPATH_H +#ifndef KEEPASSX_RESOURCES_H +#define KEEPASSX_RESOURCES_H #include <QHash> #include <QIcon> #include <QString> -class FilePath +class Resources { public: QString dataPath(const QString& name); @@ -32,27 +33,27 @@ public: QIcon trayIcon(); QIcon trayIconLocked(); QIcon trayIconUnlocked(); - QIcon icon(const QString& category, const QString& name, bool recolor = true); - QIcon onOffIcon(const QString& category, const QString& name, bool recolor = true); + QIcon icon(const QString& name, bool recolor = true); + QIcon onOffIcon(const QString& name, bool recolor = true); - static FilePath* instance(); + static Resources* instance(); private: - FilePath(); - bool testSetDir(const QString& dir); + Resources(); + bool testResourceDir(const QString& dir); bool useDarkIcon(); - static FilePath* m_instance; + static Resources* m_instance; QString m_dataPath; QHash<QString, QIcon> m_iconCache; - Q_DISABLE_COPY(FilePath) + Q_DISABLE_COPY(Resources) }; -inline FilePath* filePath() +inline Resources* resources() { - return FilePath::instance(); + return Resources::instance(); } -#endif // KEEPASSX_FILEPATH_H +#endif // KEEPASSX_RESOURCES_H diff --git a/src/core/Translator.cpp b/src/core/Translator.cpp index 4e3f568cb..28dc6aa68 100644 --- a/src/core/Translator.cpp +++ b/src/core/Translator.cpp @@ -27,7 +27,7 @@ #include "config-keepassx.h" #include "core/Config.h" -#include "core/FilePath.h" +#include "core/Resources.h" /** * Install all KeePassXC and Qt translators. @@ -53,7 +53,7 @@ void Translator::installTranslators() #ifdef QT_DEBUG QString("%1/share/translations").arg(KEEPASSX_BINARY_DIR), #endif - filePath()->dataPath("translations")}; + resources()->dataPath("translations")}; bool translationsLoaded = false; for (const QString& path : paths) { @@ -121,7 +121,7 @@ QList<QPair<QString, QString>> Translator::availableLanguages() #ifdef QT_DEBUG QString("%1/share/translations").arg(KEEPASSX_BINARY_DIR), #endif - filePath()->dataPath("translations")}; + resources()->dataPath("translations")}; QList<QPair<QString, QString>> languages; languages.append(QPair<QString, QString>("system", "System default")); |