diff --git a/CMakeLists.txt b/CMakeLists.txt index 924478431d..4a35d93903 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -46,6 +46,7 @@ option(WEBUI "Allows to disable the WebUI." ON) if (WIN32) option(STACKTRACE_WIN "") else (WIN32) + option(PLASMA_INTEGRATION "Enable KDE/Plasma integration" OFF) cmake_dependent_option(SYSTEMD "Install the systemd service file (headless only)" OFF "NOT GUI" OFF) cmake_dependent_option(DBUS "Enable use of QtDBus (GUI only)" ON "GUI" OFF) diff --git a/configure b/configure index 67eb94016a..36c5a51d04 100755 --- a/configure +++ b/configure @@ -721,6 +721,7 @@ enable_gui enable_systemd enable_webui enable_qt_dbus +enable_plasma_integration with_boost with_boost_libdir with_boost_system @@ -1387,6 +1388,8 @@ Optional Features: --enable-systemd Install the systemd service file (headless only). --disable-webui Disable the WebUI. --disable-qt-dbus Disable use of QtDBus (GUI only) + --enable-plasma-integration + Enable KDE/Plasma integration Optional Packages: --with-PACKAGE[=ARG] use PACKAGE [ARG=yes] @@ -4221,6 +4224,14 @@ else fi +# Check whether --enable-plasma-integration was given. +if test "${enable_plasma_integration+set}" = set; then : + enableval=$enable_plasma_integration; +else + enable_plasma_integration=no +fi + + # Detect OS { $as_echo "$as_me:${as_lineno-$LINENO}: checking whether OS is FreeBSD" >&5 $as_echo_n "checking whether OS is FreeBSD... " >&6; } @@ -4632,7 +4643,22 @@ $as_echo "no" >&6; } $as_echo "$enable_qt_dbus" >&6; } as_fn_error $? "Unknown option \"$enable_qt_dbus\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; esac - +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking whether KDE/Plasma integration should be enabled" >&5 +$as_echo_n "checking whether KDE/Plasma integration should be enabled... " >&6; } +case "x$enable_plasma_integration" in #( + "xyes") : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +$as_echo "yes" >&6; } + QBT_ADD_CONFIG="$QBT_ADD_CONFIG plasma_integration" ;; #( + "xno") : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: no" >&5 +$as_echo "no" >&6; } + QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG plasma_integration" ;; #( + *) : + { $as_echo "$as_me:${as_lineno-$LINENO}: result: $enable_plasma_integration" >&5 +$as_echo "$enable_plasma_integration" >&6; } + as_fn_error $? "Unknown option \"$plasma_integration\". Use either \"yes\" or \"no\"." "$LINENO" 5 ;; +esac diff --git a/configure.ac b/configure.ac index d55156dc65..7c1c9f387c 100644 --- a/configure.ac +++ b/configure.ac @@ -48,6 +48,12 @@ AC_ARG_ENABLE(qt-dbus, [], [enable_qt_dbus=yes]) +AC_ARG_ENABLE(plasma-integration, + [AS_HELP_STRING([--enable-plasma-integration], + [Enable KDE/Plasma integration])], + [], + [enable_plasma_integration=no]) + # Detect OS AC_MSG_CHECKING([whether OS is FreeBSD]) AS_IF([expr "$host_os" : ".*freebsd.*" > /dev/null], @@ -136,7 +142,16 @@ AS_CASE(["x$enable_qt_dbus"], QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG dbus"], [AC_MSG_RESULT([$enable_qt_dbus]) AC_MSG_ERROR([Unknown option "$enable_qt_dbus". Use either "yes" or "no".])]) - +AC_MSG_CHECKING([whether KDE/Plasma integration should be enabled]) +AS_CASE(["x$enable_plasma_integration"], + ["xyes"], + [AC_MSG_RESULT([yes]) + QBT_ADD_CONFIG="$QBT_ADD_CONFIG plasma_integration"], + ["xno"], + [AC_MSG_RESULT([no]) + QBT_REMOVE_CONFIG="$QBT_REMOVE_CONFIG plasma_integration"], + [AC_MSG_RESULT([$enable_plasma_integration]) + AC_MSG_ERROR([Unknown option "$plasma_integration". Use either "yes" or "no".])]) AX_BOOST_BASE([1.35]) # HAVE_BOOST is set to an empty value when Boost is found. I don't know diff --git a/dist/CMakeLists.txt b/dist/CMakeLists.txt index 0eeb49dc0b..f85c7792c4 100644 --- a/dist/CMakeLists.txt +++ b/dist/CMakeLists.txt @@ -1,3 +1,9 @@ +set(ADDITIONAL_THEME_FILES + ${CMAKE_CURRENT_SOURCE_DIR}/theme/Classic.colortheme + ${CMAKE_CURRENT_SOURCE_DIR}/theme/AlternativeDark.colortheme + ${CMAKE_CURRENT_SOURCE_DIR}/theme/AlternativeLight.colortheme +) + if (APPLE) add_subdirectory(mac) else (APPLE) diff --git a/dist/theme/AlternativeDark.colortheme b/dist/theme/AlternativeDark.colortheme new file mode 100644 index 0000000000..b5d608aed1 --- /dev/null +++ b/dist/theme/AlternativeDark.colortheme @@ -0,0 +1,25 @@ +[Info] +Name=Alternative (Dark) +Description=Alternative color theme. Dark flavour. +Inherits=Default (Dark) + +[TorrentState] +Uploading=RGB:#63b8ff +PausedUploading=RGB:#4f94cd +QueuedUploading=RGB:#00cdcd +StalledUploading=RGB:#63b8ff +CheckingUploading=RGB:#00cdcd +ForcedUploading=RGB:#63b8ff +Allocating=RGB:#fa8072 +Downloading=RGB:#32cd32 +DownloadingMetadata=RGB:#32cd32 +PausedDownloading=RGB:#cccccc +QueuedDownloading=RGB:#00cdcd +StalledDownloading=RGB:#fa8072 +CheckingDownloading=RGB:#00cdcd +ForcedDownloading=RGB:#32cd32 +QueuedForChecking=RGB:#00cdcd +CheckingResumeData=RGB:#00cdcd +Unknown=RGB:#ff0000 +Error=RGB:#ff0000 +MissingFiles=RGB:#ff0000 diff --git a/dist/theme/AlternativeLight.colortheme b/dist/theme/AlternativeLight.colortheme new file mode 100644 index 0000000000..c04ba7b9b0 --- /dev/null +++ b/dist/theme/AlternativeLight.colortheme @@ -0,0 +1,22 @@ +[Info] +Name=Alternative (Light) +Description=Alternative color theme. Light flavour. +Inherits=Default (Light) + +[TorrentState] +Unknown=RGB:#ff0000 +Error=RGB:#ff0000 +MissingFiles=RGB:#ff0000 +Uploading=RGB:#4169e1 +PausedUploading=RGB:#00008b +QueuedUploading=RGB:#008080 +StalledUploading=RGB:#4169e1 +CheckingUploading=RGB:#008080 +ForcedUploading=RGB:#4169e1 +Allocating=RGB:#228b22 +PausedDownloading=RGB:#000000 +QueuedDownloading=RGB:#008080 +StalledDownloading=RGB:#228b22 +CheckingDownloading=RGB:#008080 +QueuedForChecking=RGB:#008080 +CheckingResumeData=RGB:#008080 diff --git a/dist/theme/Classic.colortheme b/dist/theme/Classic.colortheme new file mode 100644 index 0000000000..419a2d4b8b --- /dev/null +++ b/dist/theme/Classic.colortheme @@ -0,0 +1,24 @@ +[Info] +Name=Classic +Description=Classic qBittorrent colors. + +[TorrentState] +Unknown=RGB:#ff0000 +Error=RGB:#ff0000 +MissingFiles=RGB:#ff0000 +Uploading=RGB:#ffa500 +PausedUploading=RGB:#ff0000 +QueuedUploading=RGB:#008080 +StalledUploading=RGB:#808080 +CheckingUploading=RGB:#808080 +ForcedUploading=RGB:#ffa500 +Allocating=RGB:#808080 +Downloading=RGB:#008000 +DownloadingMetadata=RGB:#008000 +PausedDownloading=RGB:#ff0000 +QueuedDownloading=RGB:#008080 +StalledDownloading=RGB:#808080 +CheckingDownloading=RGB:#808080 +ForcedDownloading=RGB:#008000 +QueuedForChecking=RGB:#808080 +CheckingResumeData=RGB:#808080 diff --git a/dist/unix/CMakeLists.txt b/dist/unix/CMakeLists.txt index 2dfb3f4fa5..8d161e69ef 100644 --- a/dist/unix/CMakeLists.txt +++ b/dist/unix/CMakeLists.txt @@ -42,4 +42,15 @@ if (GUI) ${qBittorrent_SOURCE_DIR}/src/icons/skin/qbittorrent-tray-light.svg DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/icons/hicolor/scalable/status COMPONENT data) + + install(FILES ${ADDITIONAL_THEME_FILES} + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/qBittorrent/theme/ + COMPONENT data + ) endif() + +if (PLASMA_INTEGRATION) + install(FILES qBittorrentPlasma.colortheme + DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/qBittorrent/theme/ + COMPONENT data) +endif (PLASMA_INTEGRATION) diff --git a/dist/unix/qBittorrentPlasma.colortheme b/dist/unix/qBittorrentPlasma.colortheme new file mode 100644 index 0000000000..8026812a4d --- /dev/null +++ b/dist/unix/qBittorrentPlasma.colortheme @@ -0,0 +1,37 @@ +[Info] +Name=Plasma colors +Description=qBittorrent color theme which uses Plasma colors. + +[TorrentState] +Unknown=Plasma:ForegroundNegative:Disabled +Error=Plasma:ForegroundNegative +MissingFiles=Plasma:ForegroundNegative +Uploading=Plasma:ForegroundNeutral +PausedUploading=Plasma:ForegroundNeutral:Disabled +QueuedUploading=Plasma:ForegroundNeutral:Inactive +StalledUploading=Plasma:ForegroundInactive +CheckingUploading=Plasma:ForegroundActive:Inactive +ForcedUploading=Plasma:ForegroundNeutral:Active:View:Intensify +Allocating=Plasma:ForegroundInactive +Downloading=Plasma:ForegroundPositive +DownloadingMetadata=Plasma:ForegroundActive:Active:View:Reduce +PausedDownloading=Plasma:ForegroundPositive:Disabled +QueuedDownloading=Plasma:ForegroundPositive:Inactive +StalledDownloading=Plasma:ForegroundInactive +CheckingDownloading=Plasma:ForegroundActive:Inactive +ForcedDownloading=Plasma:ForegroundPositive:Active:View:Intensify +QueuedForChecking=Plasma:ForegroundActive:Inactive +CheckingResumeData=Plasma:ForegroundActive:Inactive + +[LogMessageType] +ALL=QPalette:Active:WindowText +NORMAL=QPalette:Active:WindowText +INFO=Plasma:ForegroundActive +WARNING=Plasma:ForegroundNeutral +CRITICAL=Plasma:ForegroundNegative + +[DownloadProgressBar] +Background=Plasma:BackgroundNormal +Border=Plasma:DecorationFocus +Complete=Plasma:ForegroundActive +Incomplete=Plasma:ForegroundPositive diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fc08356cea..41840a3377 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -54,6 +54,11 @@ endif (NOT WEBUI) if (STACKTRACE_WIN) add_definitions(-DSTACKTRACE_WIN) endif(STACKTRACE_WIN) + +if (PLASMA_INTEGRATION) + add_definitions(-DPLASMA_INTEGRATION) +endif (PLASMA_INTEGRATION) + # nogui { # TARGET = qbittorrent-nox # } else { diff --git a/src/app/application.cpp b/src/app/application.cpp index 2eae2b0878..8ede4d68c7 100644 --- a/src/app/application.cpp +++ b/src/app/application.cpp @@ -37,6 +37,9 @@ #ifndef DISABLE_GUI #include "gui/guiiconprovider.h" +#include "gui/theme/colorproviders.h" +#include "gui/theme/fontproviders.h" +#include "gui/theme/themeprovider.h" #ifdef Q_OS_WIN #include #include @@ -466,9 +469,14 @@ int Application::exec(const QStringList ¶ms) { Net::ProxyConfigurationManager::initInstance(); Net::DownloadManager::initInstance(); + #ifdef DISABLE_GUI IconProvider::initInstance(); #else + Theme::Serialization::registerColorProviders(); + Theme::Serialization::registerFontProviders(); + Theme::ThemeProvider::initInstance(); + GuiIconProvider::initInstance(); #endif diff --git a/src/base/iconprovider.cpp b/src/base/iconprovider.cpp index 8d137556d2..32c01cc4af 100644 --- a/src/base/iconprovider.cpp +++ b/src/base/iconprovider.cpp @@ -33,9 +33,21 @@ IconProvider::IconProvider(QObject *parent) : QObject(parent) { + setIconDir(defaultIconDir()); } -IconProvider::~IconProvider() {} +IconProvider::~IconProvider() = default; + +void IconProvider::setIconDir(const QString& path) +{ + m_iconThemeDir = QDir(path); + Q_ASSERT(m_iconThemeDir.exists()); +} + +QString IconProvider::defaultIconDir() +{ + return QLatin1String(":/icons/qbt-theme/"); +} void IconProvider::initInstance() { @@ -58,7 +70,7 @@ IconProvider *IconProvider::instance() QString IconProvider::getIconPath(const QString &iconId) { - return ":/icons/qbt-theme/" + iconId + ".png"; + return m_iconThemeDir.absoluteFilePath(iconId + QLatin1String(".svg")); } IconProvider *IconProvider::m_instance = 0; diff --git a/src/base/iconprovider.h b/src/base/iconprovider.h index f9c00d63cf..6b469bbdcd 100644 --- a/src/base/iconprovider.h +++ b/src/base/iconprovider.h @@ -30,6 +30,7 @@ #ifndef ICONPROVIDER_H #define ICONPROVIDER_H +#include #include class QString; @@ -49,7 +50,13 @@ class IconProvider : public QObject explicit IconProvider(QObject *parent = 0); ~IconProvider(); + void setIconDir(const QString &path); + static QString defaultIconDir(); + static IconProvider *m_instance; + +private: + QDir m_iconThemeDir; }; #endif // ICONPROVIDER_H diff --git a/src/base/logger.h b/src/base/logger.h index 441a7408b0..7802231ea0 100644 --- a/src/base/logger.h +++ b/src/base/logger.h @@ -10,7 +10,7 @@ const int MAX_LOG_MESSAGES = 20000; namespace Log { - enum MsgType + enum MsgType: int { ALL = -1, NORMAL = 0x1, diff --git a/src/base/preferences.cpp b/src/base/preferences.cpp index 8f8e4f3096..9f72efff12 100644 --- a/src/base/preferences.cpp +++ b/src/base/preferences.cpp @@ -31,6 +31,7 @@ */ #include +#include #include #include #include @@ -90,17 +91,28 @@ void Preferences::setValue(const QString &key, const QVariant &value) SettingsStorage::instance()->storeValue(key, value); } -// General options +// Appearance/Language options QString Preferences::getLocale() const { - return value("Preferences/General/Locale", QLocale::system().name()).toString(); + return value("Appearance/Locale", QLocale::system().name()).toString(); } void Preferences::setLocale(const QString &locale) { - setValue("Preferences/General/Locale", locale); + setValue("Appearance/Locale", locale); +} + +bool Preferences::useAlternatingRowColors() const +{ + return value("Appearance/AlternatingRowColors", true).toBool(); } +void Preferences::setAlternatingRowColors(bool b) +{ + setValue("Appearance/AlternatingRowColors", b); +} + +// General options bool Preferences::deleteTorrentFilesAsDefault() const { return value("Preferences/General/DeleteTorrentsFilesAsDefault", false).toBool(); @@ -131,16 +143,6 @@ void Preferences::showSpeedInTitleBar(bool show) setValue("Preferences/General/SpeedInTitleBar", show); } -bool Preferences::useAlternatingRowColors() const -{ - return value("Preferences/General/AlternatingRowColors", true).toBool(); -} - -void Preferences::setAlternatingRowColors(bool b) -{ - setValue("Preferences/General/AlternatingRowColors", b); -} - bool Preferences::getHideZeroValues() const { return value("Preferences/General/HideZeroValues", false).toBool(); @@ -730,18 +732,6 @@ void Preferences::resolvePeerHostNames(bool resolve) setValue("Preferences/Connection/ResolvePeerHostNames", resolve); } -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) -bool Preferences::useSystemIconTheme() const -{ - return value("Preferences/Advanced/useSystemIconTheme", true).toBool(); -} - -void Preferences::useSystemIconTheme(bool enabled) -{ - setValue("Preferences/Advanced/useSystemIconTheme", enabled); -} -#endif - bool Preferences::recursiveDownloadDisabled() const { return value("Preferences/Advanced/DisableRecursiveDownload", false).toBool(); @@ -1456,6 +1446,25 @@ void Preferences::setSpeedWidgetGraphEnable(int id, const bool enable) void Preferences::upgrade() { + // Migrate single-value prefs to new section/name + QList> prefsToMigrate = { + { "Preferences/General/Locale", "Appearance/Locale" }, + { "Preferences/General/AlternatingRowColors", "Appearance/AlternatingRowColors" }, + }; + + for (auto iter = prefsToMigrate.begin(); iter != prefsToMigrate.end(); ++iter) { + QString pre(iter->first); + QString post(iter->second); + + QVariant preValue = value(pre); + + if (!preValue.isNull()) { + qDebug() << "Migrating preference" << pre << "->" << post; + setValue(post, preValue); + SettingsStorage::instance()->removeValue(pre); + } + } + QStringList labels = value("TransferListFilters/customLabels").toStringList(); if (!labels.isEmpty()) { QVariantMap categories = value("BitTorrent/Session/Categories").toMap(); @@ -1468,6 +1477,15 @@ void Preferences::upgrade() } SettingsStorage::instance()->removeValue("Preferences/Downloads/AppendLabel"); + +#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) + const QLatin1String UseSystemIconsOldKey = QLatin1String("Preferences/Advanced/useSystemIconTheme"); + if (SettingsStorage::instance()->loadValue(UseSystemIconsOldKey, false).toBool()) { + // see GUiIconsProvider for the new key + SettingsStorage::instance()->storeValue(QLatin1String("Appearance/IconSet"), QLatin1String("SystemTheme")); + } + SettingsStorage::instance()->removeValue(UseSystemIconsOldKey); +#endif } void Preferences::apply() diff --git a/src/base/preferences.h b/src/base/preferences.h index e7f2e53e8e..48aff91e17 100644 --- a/src/base/preferences.h +++ b/src/base/preferences.h @@ -100,17 +100,19 @@ class Preferences: public QObject static void freeInstance(); static Preferences *instance(); - // General options + // Appearance options QString getLocale() const; void setLocale(const QString &locale); + bool useAlternatingRowColors() const; + void setAlternatingRowColors(bool b); + + // General options bool deleteTorrentFilesAsDefault() const; void setDeleteTorrentFilesAsDefault(bool del); bool confirmOnExit() const; void setConfirmOnExit(bool confirm); bool speedInTitleBar() const; void showSpeedInTitleBar(bool show); - bool useAlternatingRowColors() const; - void setAlternatingRowColors(bool b); bool getHideZeroValues() const; void setHideZeroValues(bool b); int getHideZeroComboValues() const; @@ -225,10 +227,6 @@ class Preferences: public QObject void resolvePeerCountries(bool resolve); bool resolvePeerHostNames() const; void resolvePeerHostNames(bool resolve); -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - bool useSystemIconTheme() const; - void useSystemIconTheme(bool enabled); -#endif bool recursiveDownloadDisabled() const; void disableRecursiveDownload(bool disable = true); #ifdef Q_OS_WIN diff --git a/src/config.h.cmakein b/src/config.h.cmakein index 5d6a30c32a..5486aa644c 100644 --- a/src/config.h.cmakein +++ b/src/config.h.cmakein @@ -12,3 +12,4 @@ #endif #cmakedefine STACKTRACE_WIN +#cmakedefine PLASMA_INTEGRATION diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index e4bfce773d..9aee86abc7 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -74,6 +74,20 @@ transferlistfilterswidget.h transferlistsortmodel.h transferlistwidget.h updownratiodlg.h +theme/colorprovider_p.h +theme/colorproviders.h +theme/colortheme.h +theme/fontprovider_p.h +theme/fontproviders.h +theme/fonttheme.h +theme/provider_p.h +theme/serializabletheme.h +theme/serializablecolortheme.h +theme/serializablefonttheme.h +theme/themecommon.h +theme/themeexceptions.h +theme/themeinfo.h +theme/themeprovider.h ) set(QBT_GUI_SOURCES @@ -118,6 +132,20 @@ transferlistfilterswidget.cpp transferlistsortmodel.cpp transferlistwidget.cpp updownratiodlg.cpp +theme/colorprovider_p.cpp +theme/colorproviders.cpp +theme/colortheme.cpp +theme/fonttheme.cpp +theme/fontprovider_p.cpp +theme/fontproviders.cpp +theme/provider_p.cpp +theme/serializabletheme.cpp +theme/serializablecolortheme.cpp +theme/serializablefonttheme.cpp +theme/themecommon.cpp +theme/themeexceptions.cpp +theme/themeinfo.cpp +theme/themeprovider.cpp ) if (APPLE) @@ -130,6 +158,19 @@ if (WIN32 OR APPLE) list(APPEND QBT_GUI_SOURCES programupdater.cpp) endif (WIN32 OR APPLE) +if (PLASMA_INTEGRATION) + list(APPEND QBT_GUI_HEADERS + theme/plasmacolorprovider.h + utils/colorutils.h + utils/plasmacolorscheme.h + ) + list(APPEND QBT_GUI_SOURCES + theme/plasmacolorprovider.cpp + utils/colorutils.cpp + utils/plasmacolorscheme.cpp + ) +endif (PLASMA_INTEGRATION) + set(QBT_GUI_FORMS mainwindow.ui about.ui @@ -151,7 +192,7 @@ torrentcreatordlg.ui shutdownconfirmdlg.ui ) -qbt_target_sources(about.qrc) +qbt_target_sources(about.qrc theme/builtinthemes.qrc) add_library(qbt_gui STATIC ${QBT_GUI_HEADERS} ${QBT_GUI_SOURCES} ${QBT_GUI_FORMS}) target_link_libraries(qbt_gui qbt_lineedit qbt_powermanagement qbt_rss qbt_properties qbt_searchengine diff --git a/src/gui/advancedsettings.cpp b/src/gui/advancedsettings.cpp index 31ad22068d..229e3255fe 100644 --- a/src/gui/advancedsettings.cpp +++ b/src/gui/advancedsettings.cpp @@ -69,9 +69,6 @@ enum AdvSettingsRows TORRENT_ADDED_NOTIFICATIONS, CONFIRM_REMOVE_ALL_TAGS, DOWNLOAD_TRACKER_FAVICON, -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - USE_ICON_THEME, -#endif // libtorrent section LIBTORRENT_HEADER, @@ -207,10 +204,6 @@ void AdvancedSettings::saveAdvancedSettings() #if defined(Q_OS_WIN) || defined(Q_OS_MAC) pref->setUpdateCheckEnabled(cb_update_check.isChecked()); -#endif - // Icon theme -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - pref->useSystemIconTheme(cb_use_icon_theme.isChecked()); #endif pref->setConfirmTorrentRecheck(cb_confirm_torrent_recheck.isChecked()); @@ -439,10 +432,6 @@ void AdvancedSettings::loadAdvancedSettings() #if defined(Q_OS_WIN) || defined(Q_OS_MAC) cb_update_check.setChecked(pref->isUpdateCheckEnabled()); addRow(UPDATE_CHECK, tr("Check for software updates"), &cb_update_check); -#endif -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - cb_use_icon_theme.setChecked(pref->useSystemIconTheme()); - addRow(USE_ICON_THEME, tr("Use system icon theme"), &cb_use_icon_theme); #endif // Torrent recheck confirmation cb_confirm_torrent_recheck.setChecked(pref->confirmTorrentRecheck()); diff --git a/src/gui/advancedsettings.h b/src/gui/advancedsettings.h index d780db5e50..3a63522147 100644 --- a/src/gui/advancedsettings.h +++ b/src/gui/advancedsettings.h @@ -88,10 +88,6 @@ private slots: #if defined(Q_OS_WIN) || defined(Q_OS_MAC) QCheckBox cb_update_check; #endif - -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - QCheckBox cb_use_icon_theme; -#endif }; #endif // ADVANCEDSETTINGS_H diff --git a/src/gui/executionlog.cpp b/src/gui/executionlog.cpp index b286f0ed47..cf46751862 100644 --- a/src/gui/executionlog.cpp +++ b/src/gui/executionlog.cpp @@ -28,15 +28,19 @@ * Contact : chris@qbittorrent.org */ +#include "executionlog.h" + #include #include #include #include #include -#include "executionlog.h" -#include "ui_executionlog.h" + #include "guiiconprovider.h" #include "loglistwidget.h" +#include "theme/colortheme.h" + +#include "ui_executionlog.h" ExecutionLog::ExecutionLog(QWidget *parent, const Log::MsgTypes &types) : QWidget(parent) @@ -75,39 +79,39 @@ void ExecutionLog::showMsgTypes(const Log::MsgTypes &types) m_msgList->showMsgTypes(types); } -void ExecutionLog::addLogMessage(const Log::Msg &msg) +namespace { - QString text; - QDateTime time = QDateTime::fromMSecsSinceEpoch(msg.timestamp); - QColor color; - - switch (msg.type) { - case Log::INFO: - color.setNamedColor("blue"); - break; - case Log::WARNING: - color.setNamedColor("orange"); - break; - case Log::CRITICAL: - color.setNamedColor("red"); - break; - default: - color = QApplication::palette().color(QPalette::WindowText); + QString coloredString(const QString &str, const QColor &color) + { + return QString(QLatin1String("%2")) + .arg(color.name(), str); } +} + +void ExecutionLog::addLogMessage(const Log::Msg &msg) +{ + const QDateTime time = QDateTime::fromMSecsSinceEpoch(msg.timestamp); + const QColor messageColor = Theme::ColorTheme::current().logMessageColor(msg.type); + const QColor neutralColor = QPalette().color(QPalette::Inactive, QPalette::WindowText); - text = "" + time.toString(Qt::SystemLocaleShortDate) + " - " + msg.message + ""; + QString text = coloredString(time.toString(Qt::SystemLocaleShortDate), neutralColor) + + QLatin1String(" - ") + + coloredString(msg.message, messageColor); m_msgList->appendLine(text, msg.type); } -void ExecutionLog::addPeerMessage(const Log::Peer& peer) +void ExecutionLog::addPeerMessage(const Log::Peer &peer) { - QString text; - QDateTime time = QDateTime::fromMSecsSinceEpoch(peer.timestamp); + const QDateTime time = QDateTime::fromMSecsSinceEpoch(peer.timestamp); + const QColor IPColor = Theme::ColorTheme::current().logMessageColor(Log::MsgType::CRITICAL); + const QColor neutralColor = QPalette().color(QPalette::Inactive, QPalette::WindowText); + QString text = coloredString(time.toString(Qt::SystemLocaleShortDate), neutralColor) + + QLatin1String(" - ") + coloredString(peer.ip, IPColor) + QLatin1Char(' '); if (peer.blocked) - text = "" + time.toString(Qt::SystemLocaleShortDate) + " - " + tr("%1 was blocked %2", "x.y.z.w was blocked").arg(peer.ip).arg(peer.reason); + text += tr("was blocked %1", "x.y.z.w was blocked").arg(peer.reason); else - text = "" + time.toString(Qt::SystemLocaleShortDate) + " - " + tr("%1 was banned", "x.y.z.w was banned").arg(peer.ip); + text += tr("was banned", "x.y.z.w was banned"); m_peerList->appendLine(text, Log::NORMAL); } diff --git a/src/gui/gui.pri b/src/gui/gui.pri index 6a80504038..48e5556555 100644 --- a/src/gui/gui.pri +++ b/src/gui/gui.pri @@ -63,6 +63,20 @@ HEADERS += \ $$PWD/fspathedit.h \ $$PWD/fspathedit_p.h \ $$PWD/previewselectdialog.h \ + $$PWD/theme/colorprovider_p.h \ + $$PWD/theme/colorproviders.h \ + $$PWD/theme/colortheme.h \ + $$PWD/theme/fontprovider_p.h \ + $$PWD/theme/fontproviders.h \ + $$PWD/theme/fonttheme.h \ + $$PWD/theme/provider_p.h \ + $$PWD/theme/serializabletheme.h \ + $$PWD/theme/serializablecolortheme.h \ + $$PWD/theme/serializablefonttheme.h \ + $$PWD/theme/themecommon.h \ + $$PWD/theme/themeexceptions.h \ + $$PWD/theme/themeinfo.h \ + $$PWD/theme/themeprovider.h \ SOURCES += \ $$PWD/mainwindow.cpp \ @@ -117,6 +131,20 @@ SOURCES += \ $$PWD/fspathedit.cpp \ $$PWD/fspathedit_p.cpp \ $$PWD/previewselectdialog.cpp \ + $$PWD/theme/colorprovider_p.cpp \ + $$PWD/theme/colortheme.cpp \ + $$PWD/theme/fonttheme.cpp \ + $$PWD/theme/fontprovider_p.cpp \ + $$PWD/theme/colorproviders.cpp \ + $$PWD/theme/fontproviders.cpp \ + $$PWD/theme/provider_p.cpp \ + $$PWD/theme/serializabletheme.cpp \ + $$PWD/theme/serializablecolortheme.cpp \ + $$PWD/theme/serializablefonttheme.cpp \ + $$PWD/theme/themecommon.cpp \ + $$PWD/theme/themeexceptions.cpp \ + $$PWD/theme/themeinfo.cpp \ + $$PWD/theme/themeprovider.cpp \ win32|macx { HEADERS += $$PWD/programupdater.h @@ -128,6 +156,15 @@ macx { OBJECTIVE_SOURCES += $$PWD/macutilities.mm } +plasma_integration { + HEADERS += $$PWD/theme/plasmacolorprovider.h \ + $$PWD/utils/colorutils.h \ + $$PWD/utils/plasmacolorscheme.h + SOURCES += $$PWD/theme/plasmacolorprovider.cpp \ + $$PWD/utils/colorutils.cpp \ + $$PWD/utils/plasmacolorscheme.cpp +} + FORMS += \ $$PWD/mainwindow.ui \ $$PWD/about.ui \ @@ -154,4 +191,6 @@ FORMS += \ $$PWD/rss/automatedrssdownloader.ui \ $$PWD/torrentcategorydialog.ui -RESOURCES += $$PWD/about.qrc +RESOURCES += \ + $$PWD/about.qrc \ + $$PWD/theme/builtinthemes.qrc diff --git a/src/gui/guiiconprovider.cpp b/src/gui/guiiconprovider.cpp index 98dead6447..047fb73d2d 100644 --- a/src/gui/guiiconprovider.cpp +++ b/src/gui/guiiconprovider.cpp @@ -28,19 +28,107 @@ */ #include "guiiconprovider.h" -#include "base/preferences.h" +#include +#include +#include +#include + +#include +#include +#include +#include #include +#include +#include +#include +#include +#include + +#include "base/preferences.h" +#include "base/settingvalue.h" +#include "base/bittorrent/torrenthandle.h" +#include "theme/colortheme.h" +#include "theme/themeprovider.h" + + #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) #include #include #endif +namespace +{ + const QLatin1String speedometerIconName {"speedometer"}; + const QLatin1String speedometerFlippedIconName {"speedometer-flipped"}; +} + +class GuiIconProvider::SVGManipulator +{ +public: + SVGManipulator(); + + void replaceSVGFillColor(const QString& svgFile, const QString &newSvgFile, const QColor& newColor); +private: + // !! Both regular expressions contain the same number of caption groups !! + // matches "color:xxxxx" + std::regex m_cssColorRegex; + // matches "fill="color" or "fill:color;" + std::regex m_fillColorRegex; +}; + +GuiIconProvider::SVGManipulator::SVGManipulator() + : m_cssColorRegex(R"regex((color)(:"?)[^;|^\s]+(;"?))regex") + , m_fillColorRegex(R"regex(( fill)(:|=")(?:(?!none|;|").)+(;|"))regex") +{ +} + +void GuiIconProvider::SVGManipulator::replaceSVGFillColor(const QString& svgFile, const QString &newSvgFile, + const QColor& newColor) +{ + // TODO detect and upack zipped SVGs + QFile inp(svgFile); + if (!inp.open(QIODevice::ReadOnly)) + throw std::runtime_error("Could not read input file"); + + std::ofstream res(newSvgFile.toStdString()); + + if (res.fail()) + throw std::runtime_error("Could not create result file"); + + Q_ASSERT(inp.size() < 100 * 1024); // SVG icons are small files + + std::stringstream svgContentsStream; + svgContentsStream << inp.readAll().constData(); + std::string svgContents = svgContentsStream.str(); + + // we support two types of colored icons: + // 1) the file defines CSS classes, that contains 'color:#xxxxxx;' + // 2) the file uses 'fill' attribute with explicit colors. + + // in case 1) we replace only CSS styled colors, in case 2) we replace color in all 'fill' + // attributes + // this code probably needs to be re-written using XML parser if the number of cases increases + + const bool svgContainsCSSStyles = svgContents.find("style type=\"text/css\"") != std::string::npos; + + res << std::regex_replace(svgContents, + svgContainsCSSStyles ? m_cssColorRegex : m_fillColorRegex, + "$1$2" + newColor.name().toStdString() + "$3"); +} + GuiIconProvider::GuiIconProvider(QObject *parent) - : IconProvider(parent) + : IconProvider {parent} + , m_iconsTemporaryDir {temporaryDirForIcons()} + , m_coloredIconsDir(m_iconsTemporaryDir.path() + QDir::separator() + QLatin1String("colorized")) + , m_svgManipulator(new SVGManipulator()) { - configure(); - connect(Preferences::instance(), SIGNAL(changed()), SLOT(configure())); + if (!m_coloredIconsDir.isValid()) + qCWarning(theme, "Could not create temporary directory '%s' for colorized icons", qUtf8Printable(m_coloredIconsDir.path())); + + connect(&Theme::ThemeProvider::instance(), &Theme::ThemeProvider::colorThemeChanged, + this, &GuiIconProvider::colorThemeChanged); + update(); } GuiIconProvider::~GuiIconProvider() = default; @@ -63,12 +151,15 @@ QIcon GuiIconProvider::getIcon(const QString &iconId) QIcon GuiIconProvider::getIcon(const QString &iconId, const QString &fallback) { + const auto it = m_generatedIcons.find(iconId); + if (it != m_generatedIcons.end()) { + return it.value(); + } #if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - if (m_useSystemTheme) { + if (iconSet() == IconSet::SystemTheme) { QIcon icon = QIcon::fromTheme(iconId); - if (icon.name() != iconId) + if (icon.isNull() || icon.name() != iconId) icon = QIcon::fromTheme(fallback, QIcon(IconProvider::getIconPath(iconId))); - icon = generateDifferentSizes(icon); return icon; } #else @@ -83,64 +174,186 @@ QIcon GuiIconProvider::getFlagIcon(const QString &countryIsoCode) return QIcon(":/icons/flags/" + countryIsoCode.toLower() + ".png"); } -// Makes sure the icon is at least available in 16px and 24px size -// It scales the icon from the theme if necessary -// Otherwise, the UI looks broken if the icon is not available -// in the correct size. -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) -QIcon GuiIconProvider::generateDifferentSizes(const QIcon &icon) +QIcon GuiIconProvider::icon(BitTorrent::TorrentState state) const { - // if icon is loaded from SVG format, it already contains all the required sizes and we shall not resize it - // In that case it will be available in the following sizes: - // (QSize(16, 16), QSize(22, 22), QSize(32, 32), QSize(48, 48), QSize(64, 64), QSize(128, 128), QSize(256, 256)) + return m_torrentStateIcons.value(state); +} - if (icon.availableSizes(QIcon::Normal, QIcon::On).size() > 6) - return icon; +GuiIconProvider::IconSet GuiIconProvider::iconSet() +{ + return iconSetSetting(); +} - QIcon newIcon; - QList requiredSizes; - requiredSizes << QSize(16, 16) << QSize(24, 24) << QSize(32, 32); - QList modes; - modes << QIcon::Normal << QIcon::Active << QIcon::Selected << QIcon::Disabled; - foreach (const QSize &size, requiredSizes) { - foreach (QIcon::Mode mode, modes) { - QPixmap pixoff = icon.pixmap(size, mode, QIcon::Off); - if (pixoff.height() > size.height()) - pixoff = pixoff.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); - newIcon.addPixmap(pixoff, mode, QIcon::Off); - QPixmap pixon = icon.pixmap(size, mode, QIcon::On); - if (pixon.height() > size.height()) - pixon = pixoff.scaled(size, Qt::KeepAspectRatio, Qt::SmoothTransformation); - newIcon.addPixmap(pixon, mode, QIcon::On); - } +void GuiIconProvider::setIconSet(GuiIconProvider::IconSet v) +{ + if (iconSet() != v) { + iconSetSetting() = v; + instance()->update(); } +} - return newIcon; +bool GuiIconProvider::stateIconsAreColorized() +{ + return stateIconsAreColorizedSetting(); } + +void GuiIconProvider::setStateIconsAreColorized(bool v) +{ + if (v != stateIconsAreColorized()) { + stateIconsAreColorizedSetting() = v; + instance()->update(); + } +} + +void GuiIconProvider::colorThemeChanged() +{ + update(); +} + +void GuiIconProvider::reset() +{ + using BitTorrent::TorrentState; + m_torrentStateIcons[TorrentState::Downloading] = + m_torrentStateIcons[TorrentState::ForcedDownloading] = + m_torrentStateIcons[TorrentState::DownloadingMetadata] = QIcon(":/icons/skin/downloading.svg"); + m_torrentStateIcons[TorrentState::Allocating] = + m_torrentStateIcons[TorrentState::StalledDownloading] = QIcon(":/icons/skin/stalledDL.svg"); + m_torrentStateIcons[TorrentState::StalledUploading] = QIcon(":/icons/skin/stalledUP.svg"); + m_torrentStateIcons[TorrentState::Uploading] = + m_torrentStateIcons[TorrentState::ForcedUploading] = QIcon(":/icons/skin/uploading.svg"); + m_torrentStateIcons[TorrentState::PausedDownloading] = QIcon(":/icons/skin/paused.svg"); + m_torrentStateIcons[TorrentState::PausedUploading] = QIcon(":/icons/skin/completed.svg"); + m_torrentStateIcons[TorrentState::QueuedDownloading] = + m_torrentStateIcons[TorrentState::QueuedUploading] = QIcon(":/icons/skin/queued.svg"); + m_torrentStateIcons[TorrentState::CheckingDownloading] = + m_torrentStateIcons[TorrentState::CheckingUploading] = +#if LIBTORRENT_VERSION_NUM < 10100 + m_torrentStateIcons[TorrentState::QueuedForChecking] = #endif + m_torrentStateIcons[TorrentState::CheckingResumeData] = QIcon(":/icons/skin/checking.svg"); + m_torrentStateIcons[TorrentState::Unknown] = + m_torrentStateIcons[TorrentState::MissingFiles] = + m_torrentStateIcons[TorrentState::Error] = QIcon(":/icons/skin/error.svg"); +} -QString GuiIconProvider::getIconPath(const QString &iconId) +void GuiIconProvider::update() { -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - if (m_useSystemTheme) { - QString path = QDir::temp().absoluteFilePath(iconId + ".png"); - if (!QFile::exists(path)) { - const QIcon icon = QIcon::fromTheme(iconId); - if (!icon.isNull()) - icon.pixmap(32).save(path); - else - path = IconProvider::getIconPath(iconId); + if (iconSet() == IconSet::Monochrome) { + decolorizeIcons(); + setIconDir(m_coloredIconsDir.path()); + } + else { + setIconDir(IconProvider::defaultIconDir()); + } + + m_generatedIcons[speedometerFlippedIconName] = flipIcon(getIcon(speedometerIconName), true, false); + + using BitTorrent::TorrentState; + const auto trySetColorizedIcon = [this](BitTorrent::TorrentState state, const char *stateName, const char *iconName) + { + const QString iconPath = QString(QLatin1String(":/icons/skin/%1.svg")).arg(QLatin1String(iconName)); + const QString colorizedIconPath = QDir(this->m_coloredIconsDir.path()) + .filePath(QLatin1String(stateName) + QLatin1String(".svg")); + try { + m_svgManipulator->replaceSVGFillColor(iconPath, colorizedIconPath, + Theme::ColorTheme::current().torrentStateColor(state)); + m_torrentStateIcons[state] = QIcon(colorizedIconPath); + } + catch (std::runtime_error &ex) { + qCWarning(theme, "Could not colorize icon '%s': '%s'", iconName, ex.what()); + m_torrentStateIcons[state] = QIcon(iconPath); } + }; - return path; - } + if (stateIconsAreColorized() && m_coloredIconsDir.isValid()) { + trySetColorizedIcon(TorrentState::Downloading, "torrent-state-downloading", "downloading"); + trySetColorizedIcon(TorrentState::ForcedDownloading, "torrent-state-forceddownloading", "downloading"); + trySetColorizedIcon(TorrentState::DownloadingMetadata, "torrent-state-downloadingmetadata", "downloading"); + trySetColorizedIcon(TorrentState::Allocating, "torrent-state-allocating", "stalledDL"); + trySetColorizedIcon(TorrentState::StalledDownloading, "torrent-state-stalleddownloading", "stalledDL"); + trySetColorizedIcon(TorrentState::StalledUploading, "torrent-state-stalleduploading", "stalledUP"); + trySetColorizedIcon(TorrentState::Uploading, "torrent-state-Uploading", "uploading"); + trySetColorizedIcon(TorrentState::ForcedUploading, "torrent-state-forceduploading", "uploading"); + trySetColorizedIcon(TorrentState::PausedDownloading, "torrent-state-pauseddownloading", "paused"); + trySetColorizedIcon(TorrentState::PausedUploading, "torrent-state-pauseduploading", "completed"); + trySetColorizedIcon(TorrentState::QueuedDownloading, "torrent-state-queueddownloading", "queued"); + trySetColorizedIcon(TorrentState::QueuedUploading, "torrent-state-queueduploading", "queued"); + trySetColorizedIcon(TorrentState::CheckingDownloading, "torrent-state-checkingdownloading", "checking"); + trySetColorizedIcon(TorrentState::CheckingUploading, "torrent-state-checkinguploading", "checking"); +#if LIBTORRENT_VERSION_NUM < 10100 + trySetColorizedIcon(TorrentState::QueuedForChecking, "torrent-state-queuedforchecking", "checking"); #endif - return IconProvider::getIconPath(iconId); + trySetColorizedIcon(TorrentState::CheckingResumeData, "torrent-state-checkingresumedata", "checking"); + trySetColorizedIcon(TorrentState::Unknown, "torrent-state-unknown", "error"); + trySetColorizedIcon(TorrentState::MissingFiles, "torrent-state-missingfiles", "error"); + trySetColorizedIcon(TorrentState::Error, "torrent-state-error", "error"); + } + else { + reset(); + } + + emit iconsChanged(); } -void GuiIconProvider::configure() +void GuiIconProvider::decolorizeIcons() { -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - m_useSystemTheme = Preferences::instance()->useSystemIconTheme(); -#endif + // process every svg file from :/icons/qbt-theme/ + QDir srcIconsDir(QLatin1String(":/icons/qbt-theme/")); + const QStringList icons = srcIconsDir.entryList({QLatin1String("*.svg")}, QDir::Files); + const QColor newIconColor = QPalette().color(QPalette::WindowText); + for (const QString &iconFile: icons) { + try { + m_svgManipulator->replaceSVGFillColor(srcIconsDir.absoluteFilePath(iconFile), + m_coloredIconsDir.path() + QDir::separator() + iconFile, newIconColor); + } + catch (std::runtime_error &ex) { + qCWarning(theme, "Could not colorize icon '%s': '%s'", qPrintable(iconFile), ex.what()); + } + } +} + +QString GuiIconProvider::temporaryDirForIcons() +{ + // TODO Application should create QTemporaryDir for /run/user/// + // and we then will be able to create directory "icons" inside + + QString runPath = QStandardPaths::writableLocation(QStandardPaths::RuntimeLocation); + if (runPath.isEmpty()) runPath = QDir::tempPath(); + + return runPath + QDir::separator() + + QCoreApplication::applicationName() + + QLatin1String("_icons_"); +} + +CachedSettingValue &GuiIconProvider::iconSetSetting() +{ + static CachedSettingValue setting("Appearance/IconSet", IconSet::Default); + return setting; +} + +CachedSettingValue &GuiIconProvider::stateIconsAreColorizedSetting() +{ + static CachedSettingValue setting("Appearance/StateIconColorization", true); + return setting; +} + +QIcon GuiIconProvider::flipIcon(const QIcon& icon, bool horizontal, bool vertical) +{ + QIcon res; + const QList sizes = !icon.availableSizes().isEmpty() + ? icon.availableSizes() + : QList{{16, 16}, {32, 32}, {64, 64}, {128, 128}, {256, 256}}; + for (const auto &size : sizes) { + QPixmap px {size}; + px.fill({0, 0, 0, 0}); + QPainter painter {&px}; + QRect bounds {0, 0, size.width(), size.height()}; + QTransform tr; + tr.scale(horizontal ? -1. : 1., vertical ? -1. : 1.); + tr.translate(horizontal ? -size.width() : 0, vertical ? -size.height() : 0); + painter.setTransform(tr); + painter.drawPixmap(0, 0, icon.pixmap(size)); + res.addPixmap(px); + } + return res; } diff --git a/src/gui/guiiconprovider.h b/src/gui/guiiconprovider.h index 665a7fabeb..522032268f 100644 --- a/src/gui/guiiconprovider.h +++ b/src/gui/guiiconprovider.h @@ -30,9 +30,19 @@ #ifndef GUIICONPROVIDER_H #define GUIICONPROVIDER_H +#include +#include +#include + #include "base/iconprovider.h" class QIcon; +template class CachedSettingValue; + +namespace BitTorrent +{ + enum class TorrentState; +} class GuiIconProvider : public IconProvider { @@ -46,19 +56,50 @@ class GuiIconProvider : public IconProvider QIcon getIcon(const QString &iconId); QIcon getIcon(const QString &iconId, const QString &fallback); QIcon getFlagIcon(const QString &countryIsoCode); - QString getIconPath(const QString &iconId); + + QIcon icon(BitTorrent::TorrentState state) const; + + enum class IconSet + { + Default, + Monochrome, + SystemTheme, + }; + + static bool stateIconsAreColorized(); + static void setStateIconsAreColorized(bool v); + + static IconSet iconSet(); + static void setIconSet(IconSet v); + +signals: + void iconsChanged(); private slots: - void configure(); + void colorThemeChanged(); private: explicit GuiIconProvider(QObject *parent = 0); ~GuiIconProvider(); -#if (defined(Q_OS_UNIX) && !defined(Q_OS_MAC)) - QIcon generateDifferentSizes(const QIcon &icon); - bool m_useSystemTheme; -#endif + Q_ENUM(IconSet) + + class SVGManipulator; + + void update(); + void reset(); + void decolorizeIcons(); + static QIcon flipIcon(const QIcon &icon, bool horizontal, bool vertical); + + static QString temporaryDirForIcons(); + static CachedSettingValue &iconSetSetting(); + static CachedSettingValue &stateIconsAreColorizedSetting(); + + QMap m_torrentStateIcons; // these icons are needed frequently + QTemporaryDir m_iconsTemporaryDir; + QTemporaryDir m_coloredIconsDir; + QMap m_generatedIcons; + QScopedPointer m_svgManipulator; }; #endif // GUIICONPROVIDER_H diff --git a/src/gui/loglistwidget.cpp b/src/gui/loglistwidget.cpp index c6a71f65c6..82ea86e141 100644 --- a/src/gui/loglistwidget.cpp +++ b/src/gui/loglistwidget.cpp @@ -26,16 +26,22 @@ * exception statement from your version. * * Contact : chris@qbittorrent.org - */ -#include +*/ + +#include "loglistwidget.h" + +#include #include #include -#include +#include +#include #include +#include #include -#include -#include "loglistwidget.h" + #include "guiiconprovider.h" +#include "theme/fonttheme.h" +#include "theme/themeprovider.h" LogListWidget::LogListWidget(int maxLines, const Log::MsgTypes &types, QWidget *parent) : QListWidget(parent) @@ -52,6 +58,9 @@ LogListWidget::LogListWidget(int maxLines, const Log::MsgTypes &types, QWidget * addAction(copyAct); addAction(clearAct); setContextMenuPolicy(Qt::ActionsContextMenu); + + connect(&Theme::ThemeProvider::instance(), &Theme::ThemeProvider::fontThemeChanged, + this, &LogListWidget::applyFontTheme); } void LogListWidget::showMsgTypes(const Log::MsgTypes &types) @@ -74,11 +83,23 @@ void LogListWidget::keyPressEvent(QKeyEvent *event) selectAll(); } +void LogListWidget::applyFontTheme() +{ + QFont font = Theme::FontTheme::current().font(Theme::FontThemeElement::ExecutionLog); + for (int row = 0; row < count(); ++row) { + QListWidgetItem *item = this->item(row); + QLabel *label = static_cast(this->itemWidget(item)); + label->setFont(font); + item->setSizeHint(label->sizeHint()); + } +} + void LogListWidget::appendLine(const QString &line, const Log::MsgType &type) { QListWidgetItem *item = new QListWidgetItem; // We need to use QLabel here to support rich text QLabel *lbl = new QLabel(line); + lbl->setFont(Theme::FontTheme::current().font(Theme::FontThemeElement::ExecutionLog)); lbl->setContentsMargins(4, 2, 4, 2); item->setSizeHint(lbl->sizeHint()); item->setData(Qt::UserRole, type); diff --git a/src/gui/loglistwidget.h b/src/gui/loglistwidget.h index 46147f2047..052779767d 100644 --- a/src/gui/loglistwidget.h +++ b/src/gui/loglistwidget.h @@ -55,6 +55,9 @@ protected slots: protected: void keyPressEvent(QKeyEvent *event); +private slots: + void applyFontTheme(); + private: int m_maxLines; Log::MsgTypes m_types; diff --git a/src/gui/mainwindow.cpp b/src/gui/mainwindow.cpp index f923729ac5..82b8a592bd 100644 --- a/src/gui/mainwindow.cpp +++ b/src/gui/mainwindow.cpp @@ -103,6 +103,7 @@ #include "lineedit.h" #include "executionlog.h" #include "hidabletabwidget.h" + #include "ui_mainwindow.h" #ifdef Q_OS_MAC @@ -160,7 +161,7 @@ MainWindow::MainWindow(QWidget *parent) // Setting icons #ifndef Q_OS_MAC #ifdef Q_OS_UNIX - if (Preferences::instance()->useSystemIconTheme()) + if (GuiIconProvider::iconSet() == GuiIconProvider::IconSet::SystemTheme) setWindowIcon(QIcon::fromTheme("qbittorrent", QIcon(":/icons/skin/qbittorrent32.png"))); else #endif // Q_OS_UNIX @@ -1503,10 +1504,10 @@ void MainWindow::updateGUI() html += "qBittorrent"; html += ""; html += "
"; - html += " " + tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate, true)); + html += " " + tr("DL speed: %1", "e.g: Download speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadDownloadRate, true)); html += "
"; html += "
"; - html += " " + tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadUploadRate, true)); + html += " " + tr("UP speed: %1", "e.g: Upload speed: 10 KiB/s").arg(Utils::Misc::friendlyUnit(status.payloadUploadRate, true)); html += "
"; #else // OSes such as Windows do not support html here diff --git a/src/gui/optionsdlg.cpp b/src/gui/optionsdlg.cpp index c40db1c97b..f7ec1abce6 100644 --- a/src/gui/optionsdlg.cpp +++ b/src/gui/optionsdlg.cpp @@ -30,7 +30,9 @@ #include "optionsdlg.h" +#include #include +#include #include #include @@ -63,13 +65,118 @@ #include "base/utils/random.h" #include "addnewtorrentdialog.h" #include "advancedsettings.h" +#include "guiiconprovider.h" #include "rss/automatedrssdownloader.h" +#include "theme/themeexceptions.h" +#include "theme/themeinfo.h" +#include "theme/themeprovider.h" #include "banlistoptions.h" #include "guiiconprovider.h" #include "scanfoldersdelegate.h" +#include "torrentmodel.h" #include "ui_optionsdlg.h" +namespace +{ + /** + * @brief Implements list model for themes of the given kind + * + * The class gets map of the themes from ThemeProvider, converts it into array sorting + * by theme localized name. + */ + class ThemeInfoListModel: public QAbstractListModel + { + public: + ThemeInfoListModel(Theme::Kind kind, QObject *parent); + + int rowCount(const QModelIndex &parent) const override; + QVariant data(const QModelIndex &index, int role) const override; + + struct Entry + { + Theme::ThemeInfo info; + QString filePath; + }; + + const Entry &entry(int index) const; + int indexForTheme(const Theme::ThemeInfo &info); + + private: + std::vector m_entries; + }; + + ThemeInfoListModel::ThemeInfoListModel(Theme::Kind kind, QObject *parent) + : QAbstractListModel(parent) + { + std::map themes = Theme::ThemeProvider::instance().availableThemes(kind); + + for (const auto &pair: themes) + m_entries.push_back({pair.first, pair.second}); + + std::sort(m_entries.begin(), m_entries.end(), [](const Entry &left, const Entry &right) + { + return left.info.localizedName() < right.info.localizedName(); + }); + } + + int ThemeInfoListModel::rowCount(const QModelIndex &/*parent*/) const + { + return static_cast(m_entries.size()); + } + + QVariant ThemeInfoListModel::data(const QModelIndex &index, int role) const + { + if ((index.row() >= static_cast(m_entries.size())) || (index.column() > 0)) return {}; + + switch (role) { + case Qt::DisplayRole: + return entry(index.row()).info.localizedName(); + case Qt::ToolTipRole: + return entry(index.row()).info.localizedDescription(); + } + + return {}; + } + + const ThemeInfoListModel::Entry &ThemeInfoListModel::entry(int index) const + { + return m_entries.at(static_cast(index)); + } + + int ThemeInfoListModel::indexForTheme(const Theme::ThemeInfo& info) + { + auto i = std::find_if(m_entries.begin(), m_entries.end(), [&info](const Entry &entry) + { + return (entry.info == info); + }); + if (i == m_entries.end()) { + qDebug() << "Could not find theme info in the model"; + return 0; // don't want to crash, will lead to theme re-appliance most likely + } + return std::distance(m_entries.begin(), i); + } + + //just a shortcut + const ThemeInfoListModel::Entry &themeEntry(QComboBox *comboBox, int index = -1) + { + if (index < 0) { + index = comboBox->currentIndex(); + } + return static_cast(comboBox->model())->entry(index); + } + + QString descriptionString(const ThemeInfoListModel::Entry &entry) + { + QString res = entry.info.localizedDescription() + QLatin1Char('\n'); + if (entry.filePath.startsWith(QLatin1String(":/"))) // this theme is located in resources + res += QObject::tr("Built-in theme"); + else + res += QObject::tr("Located in: ") + entry.filePath; + return res; + } +} + // Constructor OptionsDialog::OptionsDialog(QWidget *parent) : QDialog(parent) @@ -86,7 +193,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) #endif // Icons - m_ui->tabSelection->item(TAB_UI)->setIcon(GuiIconProvider::instance()->getIcon("preferences-desktop")); + m_ui->tabSelection->item(TAB_APPEARANCE)->setIcon(GuiIconProvider::instance()->getIcon("preferences-desktop-theme")); + m_ui->tabSelection->item(TAB_BEHAVIOUR)->setIcon(GuiIconProvider::instance()->getIcon("preferences-desktop")); m_ui->tabSelection->item(TAB_BITTORRENT)->setIcon(GuiIconProvider::instance()->getIcon("preferences-system-network")); m_ui->tabSelection->item(TAB_CONNECTION)->setIcon(GuiIconProvider::instance()->getIcon("network-wired")); m_ui->tabSelection->item(TAB_DOWNLOADS)->setIcon(GuiIconProvider::instance()->getIcon("folder-download")); @@ -102,6 +210,8 @@ OptionsDialog::OptionsDialog(QWidget *parent) // uniform size for all icons m_ui->tabSelection->item(i)->setSizeHint(QSize(std::numeric_limits::max(), 62)); } + m_ui->labelRateLimitIcon->setPixmap(GuiIconProvider::instance()->getIcon(QLatin1String("speedometer")).pixmap(32, 32)); + m_ui->labelAltRateLimitIcon->setPixmap(GuiIconProvider::instance()->getIcon(QLatin1String("speedometer-flipped")).pixmap(32, 32)); m_ui->IpFilterRefreshBtn->setIcon(GuiIconProvider::instance()->getIcon("view-refresh")); @@ -132,6 +242,25 @@ OptionsDialog::OptionsDialog(QWidget *parent) } } +#if !((defined(Q_OS_UNIX) && !defined(Q_OS_MAC))) + // system icon theme is supported on Linux only + m_ui->comboIconTheme->removeItem(static_cast(GuiIconProvider::IconSet::SystemTheme)); +#endif + + // Hide the Appearance/Desktop section if it doesn't contain any visible children. + m_ui->groupBoxAppearanceDesktop->setHidden(m_ui->groupBoxAppearanceDesktop->childrenRect().isEmpty()); + + ThemeInfoListModel *colorThemeModel = new ThemeInfoListModel(Theme::Kind::Color, this); + m_ui->comboBoxColorTheme->setModel(colorThemeModel); + const Theme::ThemeInfo currentColorTheme = Theme::ThemeProvider::instance().currentTheme(Theme::Kind::Color); + m_ui->comboBoxColorTheme->setCurrentIndex(colorThemeModel->indexForTheme(currentColorTheme)); + m_ui->labelColorThemeDescription->setText(descriptionString(themeEntry(m_ui->comboBoxColorTheme, -1))); + ThemeInfoListModel *fontThemeModel = new ThemeInfoListModel(Theme::Kind::Font, this); + m_ui->comboBoxFontTheme->setModel(fontThemeModel); + const Theme::ThemeInfo currentFontTheme = Theme::ThemeProvider::instance().currentTheme(Theme::Kind::Font); + m_ui->comboBoxFontTheme->setCurrentIndex(fontThemeModel->indexForTheme(currentFontTheme)); + m_ui->labelFontThemeDescription->setText(descriptionString(themeEntry(m_ui->comboBoxFontTheme, -1))); + m_ui->scanFoldersView->header()->setSectionResizeMode(QHeaderView::ResizeToContents); m_ui->scanFoldersView->setModel(ScanFoldersModel::instance()); m_ui->scanFoldersView->setItemDelegate(new ScanFoldersDelegate(this, m_ui->scanFoldersView)); @@ -177,12 +306,14 @@ OptionsDialog::OptionsDialog(QWidget *parent) // Shortcuts for frequently used signals that have more than one overload. They would require // type casts and that is why we declare required member pointer here instead. void (QComboBox::*qComboBoxCurrentIndexChanged)(int) = &QComboBox::currentIndexChanged; + void (QComboBox::*qComboBoxActivated)(int) = &QComboBox::activated; void (QSpinBox::*qSpinBoxValueChanged)(int) = &QSpinBox::valueChanged; connect(m_ui->comboProxyType, qComboBoxCurrentIndexChanged, this, &ThisType::enableProxy); connect(m_ui->checkRandomPort, &QAbstractButton::toggled, m_ui->spinPort, &ThisType::setDisabled); // Apply button is activated when a value is changed + // General tab connect(m_ui->comboI18n, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); connect(m_ui->confirmDeletion, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); @@ -219,6 +350,16 @@ OptionsDialog::OptionsDialog(QWidget *parent) connect(m_ui->spinFileLogSize, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->spinFileLogAge, qSpinBoxValueChanged, this, &ThisType::enableApplyButton); connect(m_ui->comboFileLogAgeType, qComboBoxCurrentIndexChanged, this, &ThisType::enableApplyButton); + + // Appearance tab + connect(m_ui->comboIconTheme, qComboBoxActivated, this, &ThisType::enableApplyButton); + connect(m_ui->comboBoxColorTheme, qComboBoxActivated, this, &ThisType::colorThemeActivated); + connect(m_ui->comboBoxFontTheme, qComboBoxActivated, this, &ThisType::fontThemeActivated); + connect(m_ui->toolButtonExportColorTheme, &QToolButton::clicked, this, &ThisType::exportColorTheme); + connect(m_ui->toolButtonExportFontTheme, &QToolButton::clicked, this, &ThisType::exportFontTheme); + connect(m_ui->checkColorizeStateIcons, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); + connect(m_ui->checkColorizeTransferList, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); + // Downloads tab connect(m_ui->textSavePath, &FileSystemPathEdit::selectedPathChanged, this, &ThisType::enableApplyButton); connect(m_ui->checkUseSubcategories, &QAbstractButton::toggled, this, &ThisType::enableApplyButton); @@ -491,10 +632,63 @@ void OptionsDialog::saveOptions() qApp->installTranslator(translator); } + // Appearance preferences + + GuiIconProvider::setIconSet(static_cast(m_ui->comboIconTheme->currentIndex())); + + const auto applyTheme = [this](Theme::Kind kind, const QString &themeName) + { + + const auto themeKindToString = [](Theme::Kind kind) + { + switch (kind) { + case Theme::Kind::Color: + return tr("color", "e.g. 'color theme'"); + case Theme::Kind::Font: + return tr("font", "e.g. 'font theme'"); + default: + return tr("unknown", "e.g. 'unknown theme'"); + } + }; + + using namespace Theme::Serialization; + QString errorMessage; + try { + Theme::ThemeProvider::instance().setCurrentTheme(kind, themeName); + return; + } + catch (ThemeElementMissing &ex) { + errorMessage = tr("theme element '%1' is missing").arg(ex.elementName()); + } + catch (ValueParsingError &ex) { + errorMessage = tr("could not parse element value '%1'").arg(ex.valueString()); + } + catch (ParsingError &ex) { + errorMessage = tr("Could not parse element '%1'").arg(ex.serializedValue()); + } + catch (UnknownProvider &ex) { + errorMessage = tr("theme element contains unknown scheme '%1'").arg(ex.profiderName()); + } + catch (DeserializationError &ex) { + errorMessage = tr("theme loading error '%1'").arg(QString::fromUtf8(ex.what())); + } + QMessageBox::warning(this, tr("Theme applying failed"), + tr("Could not apply %1 theme due to following error:\n%2") + .arg(themeKindToString(kind), errorMessage)); + }; + + applyTheme(Theme::Kind::Color, themeEntry(m_ui->comboBoxColorTheme).info.name()); + applyTheme(Theme::Kind::Font, themeEntry(m_ui->comboBoxFontTheme).info.name()); + + GuiIconProvider::setStateIconsAreColorized(m_ui->checkColorizeStateIcons->isChecked()); + TorrentModel::setTextIsColorized(m_ui->checkColorizeTransferList->isChecked()); + // end Appearance preferences + // General preferences pref->setLocale(locale); pref->setConfirmTorrentDeletion(m_ui->confirmDeletion->isChecked()); pref->setAlternatingRowColors(m_ui->checkAltRowColors->isChecked()); + pref->setHideZeroValues(m_ui->checkHideZero->isChecked()); pref->setHideZeroComboValues(m_ui->comboHideZero->currentIndex()); #ifndef Q_OS_MAC @@ -757,6 +951,12 @@ void OptionsDialog::loadOptions() m_ui->comboFileLogAgeType->setCurrentIndex(app->fileLoggerAgeType()); // End General preferences + // Appearance preferences + m_ui->comboIconTheme->setCurrentIndex(static_cast(GuiIconProvider::iconSet())); + m_ui->checkColorizeStateIcons->setChecked(GuiIconProvider::stateIconsAreColorized()); + m_ui->checkColorizeTransferList->setChecked(TorrentModel::textIsColorized()); + // end Appearance preferences + m_ui->checkRSSEnable->setChecked(RSS::Session::instance()->isProcessingEnabled()); m_ui->checkRSSAutoDownloaderEnable->setChecked(RSS::AutoDownloader::instance()->isProcessingEnabled()); m_ui->spinRSSRefreshInterval->setValue(RSS::Session::instance()->refreshInterval()); @@ -1401,6 +1601,49 @@ void OptionsDialog::setLocale(const QString &localeStr) m_ui->comboI18n->setCurrentIndex(index); } +void OptionsDialog::colorThemeActivated(int index) +{ + m_ui->labelColorThemeDescription->setText( + descriptionString(themeEntry(m_ui->comboBoxColorTheme, index))); + enableApplyButton(); +} + +void OptionsDialog::fontThemeActivated(int index) +{ + m_ui->labelFontThemeDescription->setText( + descriptionString(themeEntry(m_ui->comboBoxFontTheme, index))); + enableApplyButton(); +} + +void OptionsDialog::exportColorTheme() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save theme as..."), + QDir::homePath(), + tr("qBittorrent color theme") + + QLatin1String(" (*") + Theme::ThemeProvider::colorThemeFileExtension + QLatin1Char(')')); + if (!fileName.isEmpty()) { + Theme::ThemeProvider::instance().exportTheme(Theme::Kind::Color, + themeEntry(m_ui->comboBoxColorTheme, -1).info.name(), fileName); + } +} + +void OptionsDialog::exportFontTheme() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Save theme as..."), + QDir::homePath(), + tr("qBittorrent font theme") + + QLatin1String(" (*") + Theme::ThemeProvider::fontThemeFileExtension + QLatin1Char(')')); + if (!fileName.isEmpty()) { + const bool explicitFontStrings = QMessageBox::question(this, tr("Exporting theme"), + tr("Font theme might contain references to default fonts. Do you want to expand them" + " to their actual values?")) == QMessageBox::Yes; + Theme::ThemeProvider::instance().exportTheme(Theme::Kind::Font, + themeEntry(m_ui->comboBoxFontTheme, -1).info.name(), fileName, + explicitFontStrings ? Theme::ThemeProvider::ExportOption::WriteExplicitValues + : Theme::ThemeProvider::ExportOption::NoOptions); + } +} + QString OptionsDialog::getTorrentExportDir() const { if (m_ui->checkExportDir->isChecked()) @@ -1666,11 +1909,11 @@ bool OptionsDialog::setSslKey(const QByteArray &key) // try different formats const bool isKeyValid = (!QSslKey(key, QSsl::Rsa).isNull() || !QSslKey(key, QSsl::Ec).isNull()); if (isKeyValid) { - m_ui->lblSslKeyStatus->setPixmap(QPixmap(":/icons/qbt-theme/security-high.png").scaledToHeight(20, Qt::SmoothTransformation)); + m_ui->lblSslKeyStatus->setPixmap(GuiIconProvider::instance()->getIcon(QLatin1String("security-high")).pixmap(20, 20)); m_sslKey = key; } else { - m_ui->lblSslKeyStatus->setPixmap(QPixmap(":/icons/qbt-theme/security-low.png").scaledToHeight(20, Qt::SmoothTransformation)); + m_ui->lblSslKeyStatus->setPixmap(GuiIconProvider::instance()->getIcon(QLatin1String("security-low")).pixmap(20, 20)); m_sslKey.clear(); } return isKeyValid; @@ -1685,11 +1928,11 @@ bool OptionsDialog::setSslCertificate(const QByteArray &cert) #ifndef QT_NO_OPENSSL const bool isCertValid = !QSslCertificate(cert).isNull(); if (isCertValid) { - m_ui->lblSslCertStatus->setPixmap(QPixmap(":/icons/qbt-theme/security-high.png").scaledToHeight(20, Qt::SmoothTransformation)); + m_ui->lblSslCertStatus->setPixmap(GuiIconProvider::instance()->getIcon(QLatin1String("security-high")).pixmap(20, 20)); m_sslCert = cert; } else { - m_ui->lblSslCertStatus->setPixmap(QPixmap(":/icons/qbt-theme/security-low.png").scaledToHeight(20, Qt::SmoothTransformation)); + m_ui->lblSslCertStatus->setPixmap(GuiIconProvider::instance()->getIcon(QLatin1String("security-low")).pixmap(20, 20)); m_sslCert.clear(); } return isCertValid; diff --git a/src/gui/optionsdlg.h b/src/gui/optionsdlg.h index efaaddf95c..67aadbaf85 100644 --- a/src/gui/optionsdlg.h +++ b/src/gui/optionsdlg.h @@ -65,7 +65,8 @@ class OptionsDialog: public QDialog private: enum Tabs { - TAB_UI, + TAB_APPEARANCE, + TAB_BEHAVIOUR, TAB_DOWNLOADS, TAB_CONNECTION, TAB_SPEED, @@ -105,7 +106,11 @@ private slots: void on_btnWebUiCrt_clicked(); void on_btnWebUiKey_clicked(); void on_registerDNSBtn_clicked(); - void setLocale(const QString &localeStr); + void setLocale(const QString &locale); + void colorThemeActivated(int index); + void fontThemeActivated(int index); + void exportColorTheme(); + void exportFontTheme(); private: // Methods diff --git a/src/gui/optionsdlg.ui b/src/gui/optionsdlg.ui index e617eeb98d..4b6cc65012 100644 --- a/src/gui/optionsdlg.ui +++ b/src/gui/optionsdlg.ui @@ -47,6 +47,11 @@ -1 + + + Appearance + + Behavior @@ -90,10 +95,10 @@ - 0 + 4 - - + + 0 @@ -107,7 +112,7 @@ 0 - + 0 @@ -117,16 +122,16 @@ true - + 0 0 - 501 - 893 + 549 + 540 - + @@ -192,25 +197,266 @@ - + + + Desktop + + + + + + Icon theme: + + + + + + + + + + Default + + + + + Monochrome + + + + + System theme + + + + + + + + + true + + + + (Requires restart) + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + + + + + Transfer List - + - + - Confirm when deleting torrents + Use alternating row colors + + + true + + + + + + + + + + Themes + + + + + + Color theme: + + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + TextLabel + + + + + + + Export color theme into a file + + + ... + + + + .. + + + + + + + + + Colorize text in transfer list true + + + + Colorize state icons + + + + + + + Font theme: + + + + + + + + + + + 0 + 0 + + + + + + + + + 0 + 0 + + + + TextLabel + + + + + + + Export font theme into a file + + + ... + + + + .. + + + + + + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + 0 + 0 + + + + true + + + + + 0 + 0 + 448 + 887 + + + + + + + Transfer List + + - + - Use alternating row colors + Confirm when deleting torrents true @@ -671,8 +917,8 @@ 0 0 - 591 - 1138 + 623 + 1213 @@ -1245,8 +1491,8 @@ 0 0 - 501 - 745 + 535 + 816 @@ -1748,8 +1994,8 @@ 0 0 - 516 - 525 + 549 + 540 @@ -1813,9 +2059,9 @@ - + - :/icons/slow_off.png + :/icons/qbt-theme/speedometer.svg @@ -1960,9 +2206,9 @@ - + - :/icons/slow.png + :/icons/qbt-theme/speedometer.svg @@ -2115,8 +2361,8 @@ 0 0 - 513 - 679 + 552 + 680 @@ -2590,8 +2836,8 @@ 0 0 - 516 - 525 + 430 + 259 @@ -2740,8 +2986,8 @@ 0 0 - 501 - 636 + 470 + 616 @@ -3165,7 +3411,7 @@ Use ';' to split multiple entries. Can use wildcard '*'. tabOption - comboI18n + textSavePath checkStartPaused spinPort checkUPnP diff --git a/src/gui/properties/downloadedpiecesbar.cpp b/src/gui/properties/downloadedpiecesbar.cpp index 4ecdb82f90..87465f5df2 100644 --- a/src/gui/properties/downloadedpiecesbar.cpp +++ b/src/gui/properties/downloadedpiecesbar.cpp @@ -34,6 +34,8 @@ #include +#include "theme/colortheme.h" + DownloadedPiecesBar::DownloadedPiecesBar(QWidget *parent) : base {parent} , m_dlPieceColor {0, 0xd0, 0} @@ -174,7 +176,15 @@ void DownloadedPiecesBar::clear() QString DownloadedPiecesBar::simpleToolTipText() const { - return tr("White: Missing pieces") + '\n' - + tr("Green: Partial pieces") + '\n' - + tr("Blue: Completed pieces") + '\n'; + return QString(QLatin1String(R"()" + R"()" + R"()" + R"()" + R"(
     : %2
     : %4
     : %6
)")) + .arg(Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Background).name()) + .arg(tr("Missing pieces")) + .arg(Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Incomplete).name()) + .arg(tr("Partial pieces")) + .arg(Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Complete).name()) + .arg(tr("Completed pieces")); } diff --git a/src/gui/properties/pieceavailabilitybar.cpp b/src/gui/properties/pieceavailabilitybar.cpp index 0ce3f2618c..1b91f55c0c 100644 --- a/src/gui/properties/pieceavailabilitybar.cpp +++ b/src/gui/properties/pieceavailabilitybar.cpp @@ -34,6 +34,8 @@ #include +#include "theme/colortheme.h" + PieceAvailabilityBar::PieceAvailabilityBar(QWidget *parent) : base {parent} { @@ -160,8 +162,14 @@ void PieceAvailabilityBar::clear() QString PieceAvailabilityBar::simpleToolTipText() const { - return tr("White: Unavailable pieces") + '\n' - + tr("Blue: Available pieces") + '\n'; + return QString(QLatin1String(R"()" + R"()" + R"()" + R"(
     : %2
     : %4
)")) + .arg(Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Background).name()) + .arg(tr("Unavailable pieces")) + .arg(Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Complete).name()) + .arg(tr("Available pieces")); } bool PieceAvailabilityBar::isFileNameCorrectionNeeded() const diff --git a/src/gui/properties/propertieswidget.cpp b/src/gui/properties/propertieswidget.cpp index 16ed3be331..af8b056242 100644 --- a/src/gui/properties/propertieswidget.cpp +++ b/src/gui/properties/propertieswidget.cpp @@ -53,6 +53,9 @@ #include "downloadedpiecesbar.h" #include "guiiconprovider.h" #include "lineedit.h" +#include "theme/colortheme.h" +#include "theme/fonttheme.h" +#include "theme/themeprovider.h" #include "mainwindow.h" #include "messageboxraised.h" #include "peerlistwidget.h" @@ -168,6 +171,14 @@ PropertiesWidget::PropertiesWidget(QWidget *parent, MainWindow *main_window, Tra connect(deleteHotkeyWeb, SIGNAL(activated()), SLOT(deleteSelectedUrlSeeds())); openHotkeyFile = new QShortcut(Qt::Key_Return, m_ui->filesList, 0, 0, Qt::WidgetShortcut); connect(openHotkeyFile, SIGNAL(activated()), SLOT(openSelectedFile())); + + applyColorTheme(); + applyFontTheme(); + + connect(&Theme::ThemeProvider::instance(), &Theme::ThemeProvider::colorThemeChanged, + this, &PropertiesWidget::applyColorTheme); + connect(&Theme::ThemeProvider::instance(), &Theme::ThemeProvider::fontThemeChanged, + this, &PropertiesWidget::applyFontTheme); } PropertiesWidget::~PropertiesWidget() @@ -895,3 +906,26 @@ void PropertiesWidget::filterText(const QString &filter) m_ui->filesList->expandAll(); } } + +void PropertiesWidget::applyColorTheme() +{ + downloaded_pieces->setColors( + Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Background), + Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Border), + Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Complete), + Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Incomplete)); + pieces_availability->setColors( + Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Background), + Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Border), + Theme::ColorTheme::current().downloadProgressBarColor(Theme::DownloadProgressBarElement::Complete)); +} + +void PropertiesWidget::applyFontTheme() +{ + QFont font = Theme::FontTheme::current().font(Theme::FontThemeElement::TorrentProperties); + foreach (QWidget *widget, findChildren()) + widget->setFont(font); + + foreach (QWidget *widget, findChildren()) + widget->setFont(font); +} diff --git a/src/gui/properties/propertieswidget.h b/src/gui/properties/propertieswidget.h index 9bfefe10c2..05eec1685d 100644 --- a/src/gui/properties/propertieswidget.h +++ b/src/gui/properties/propertieswidget.h @@ -111,6 +111,10 @@ protected slots: void renameSelectedFile(); void openSelectedFile(); +private slots: + void applyColorTheme(); + void applyFontTheme(); + private: void openFile(const QModelIndex &index); void openFolder(const QModelIndex &index, bool containing_folder); diff --git a/src/gui/search/searchtab.cpp b/src/gui/search/searchtab.cpp index 786da6fd52..838e283ff4 100644 --- a/src/gui/search/searchtab.cpp +++ b/src/gui/search/searchtab.cpp @@ -277,15 +277,15 @@ QString SearchTab::statusIconName(SearchTab::Status st) { switch (st) { case Status::Ongoing: - return QLatin1String("task-ongoing"); + return QLatin1String("state-sync"); case Status::Finished: - return QLatin1String("task-complete"); + return QLatin1String("state-ok"); case Status::Aborted: - return QLatin1String("task-reject"); + return QLatin1String("state-warning"); case Status::Error: - return QLatin1String("task-attention"); + return QLatin1String("state-error"); case Status::NoResults: - return QLatin1String("task-attention"); + return QLatin1String("state-offline"); default: return QString(); } diff --git a/src/gui/statusbar.cpp b/src/gui/statusbar.cpp index 20927f928b..19afd82f81 100644 --- a/src/gui/statusbar.cpp +++ b/src/gui/statusbar.cpp @@ -38,6 +38,7 @@ #include "base/bittorrent/session.h" #include "base/bittorrent/sessionstatus.h" +#include "base/bittorrent/torrenthandle.h" #include "base/utils/misc.h" #include "guiiconprovider.h" #include "speedlimitdlg.h" @@ -70,7 +71,7 @@ StatusBar::StatusBar(QWidget *parent) connect(m_connecStatusLblIcon, &QAbstractButton::clicked, this, &StatusBar::connectionButtonClicked); m_dlSpeedLbl = new QPushButton(this); - m_dlSpeedLbl->setIcon(QIcon(":/icons/skin/download.png")); + m_dlSpeedLbl->setIcon(GuiIconProvider::instance()->getIcon(QLatin1String("cloud-download"))); connect(m_dlSpeedLbl, &QAbstractButton::clicked, this, &StatusBar::capDownloadSpeed); m_dlSpeedLbl->setFlat(true); m_dlSpeedLbl->setFocusPolicy(Qt::NoFocus); @@ -79,7 +80,7 @@ StatusBar::StatusBar(QWidget *parent) m_dlSpeedLbl->setMinimumWidth(200); m_upSpeedLbl = new QPushButton(this); - m_upSpeedLbl->setIcon(QIcon(":/icons/skin/seeding.png")); + m_upSpeedLbl->setIcon(GuiIconProvider::instance()->getIcon(QLatin1String("cloud-upload"))); connect(m_upSpeedLbl, &QAbstractButton::clicked, this, &StatusBar::capUploadSpeed); m_upSpeedLbl->setFlat(true); m_upSpeedLbl->setFocusPolicy(Qt::NoFocus); @@ -174,17 +175,17 @@ void StatusBar::updateConnectionStatus() const BitTorrent::SessionStatus &sessionStatus = BitTorrent::Session::instance()->status(); if (!BitTorrent::Session::instance()->isListening()) { - m_connecStatusLblIcon->setIcon(QIcon(QLatin1String(":/icons/skin/disconnected.png"))); + m_connecStatusLblIcon->setIcon(GuiIconProvider::instance()->getIcon(QLatin1String("state-offline"))); m_connecStatusLblIcon->setToolTip(QLatin1String("") + tr("Connection Status:") + QLatin1String("
") + tr("Offline. This usually means that qBittorrent failed to listen on the selected port for incoming connections.")); } else { if (sessionStatus.hasIncomingConnections) { // Connection OK - m_connecStatusLblIcon->setIcon(QIcon(QLatin1String(":/icons/skin/connected.png"))); + m_connecStatusLblIcon->setIcon(GuiIconProvider::instance()->getIcon(QLatin1String("state-ok"))); m_connecStatusLblIcon->setToolTip(QLatin1String("") + tr("Connection Status:") + QLatin1String("
") + tr("Online")); } else { - m_connecStatusLblIcon->setIcon(QIcon(QLatin1String(":/icons/skin/firewalled.png"))); + m_connecStatusLblIcon->setIcon(GuiIconProvider::instance()->getIcon(QLatin1String("state-warning"))); m_connecStatusLblIcon->setToolTip(QLatin1String("") + tr("Connection status:") + QLatin1String("
") + QLatin1String("") + tr("No direct connections. This may indicate network configuration problems.") + QLatin1String("")); } } @@ -230,12 +231,12 @@ void StatusBar::refresh() void StatusBar::updateAltSpeedsBtn(bool alternative) { if (alternative) { - m_altSpeedsBtn->setIcon(QIcon(":/icons/slow.png")); + m_altSpeedsBtn->setIcon(GuiIconProvider::instance()->getIcon(QLatin1String("speedometer-flipped"))); m_altSpeedsBtn->setToolTip(tr("Click to switch to regular speed limits")); m_altSpeedsBtn->setDown(true); } else { - m_altSpeedsBtn->setIcon(QIcon(":/icons/slow_off.png")); + m_altSpeedsBtn->setIcon(GuiIconProvider::instance()->getIcon(QLatin1String("speedometer"))); m_altSpeedsBtn->setToolTip(tr("Click to switch to alternative speed limits")); m_altSpeedsBtn->setDown(false); } diff --git a/src/gui/theme/builtinthemes.qrc b/src/gui/theme/builtinthemes.qrc new file mode 100644 index 0000000000..7e2e3f57f5 --- /dev/null +++ b/src/gui/theme/builtinthemes.qrc @@ -0,0 +1,7 @@ + + + qBittorrentLight.colortheme + qBittorrentDark.colortheme + qBittorrent.fonttheme + + diff --git a/src/gui/theme/colorprovider_p.cpp b/src/gui/theme/colorprovider_p.cpp new file mode 100644 index 0000000000..e312b0de06 --- /dev/null +++ b/src/gui/theme/colorprovider_p.cpp @@ -0,0 +1,69 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "colorprovider_p.h" + +#include + +// -------------------------- Color ----------------------------------------------- + +QString Theme::Serialization::Color::explicitSerializedValue() const +{ + return value().name(QColor::HexRgb); +} + +// ------------------------- ColorsProviderRegistry ------------------------------ + +namespace +{ + class ColorsProviderRegistrySignletonImpl: public Theme::Serialization::ColorsProviderRegistry + { + public: + ~ColorsProviderRegistrySignletonImpl() = default; + }; + + Q_GLOBAL_STATIC(ColorsProviderRegistrySignletonImpl, colorsProviderRegistrySignletonImpl) +} + +Theme::Serialization::ColorsProviderRegistry &Theme::Serialization::ColorsProviderRegistry::instance() +{ + return *colorsProviderRegistrySignletonImpl(); +} + +void Theme::Serialization::ColorsProviderRegistry::applicationPaletteChanged() const +{ + for (const auto& providerPair: providers()) { + providerPair.second->applicationPaletteChanged(); + } +} + +// ------------------------- ColorProvider ------------------------------ + +void Theme::Serialization::ColorProvider::applicationPaletteChanged() const +{ +} diff --git a/src/gui/theme/colorprovider_p.h b/src/gui/theme/colorprovider_p.h new file mode 100644 index 0000000000..3f900c9c31 --- /dev/null +++ b/src/gui/theme/colorprovider_p.h @@ -0,0 +1,78 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_COLORPROVIDER_P_H +#define QBT_THEME_COLORPROVIDER_P_H + +#include "provider_p.h" + +#include +#include +#include +#include + +#include +#include +#include +#include + +namespace Theme +{ + namespace Serialization + { + class ColorDeserializationError: public DeserializationError + { + public: + using DeserializationError::DeserializationError; + }; + + class Color : public Entity + { + public: + QString explicitSerializedValue() const override; + }; + + class ColorProvider: public Provider + { + public: + using Provider::Provider; + virtual void applicationPaletteChanged() const; + }; + + class ColorsProviderRegistry: public ProviderRegistry + { + public: + static const Kind kind = Kind::Color; + + static ColorsProviderRegistry &instance(); + void applicationPaletteChanged() const; + }; + } +} + +#endif // QBT_COLORPROVIDER_P_H diff --git a/src/gui/theme/colorproviders.cpp b/src/gui/theme/colorproviders.cpp new file mode 100644 index 0000000000..e8b462ba31 --- /dev/null +++ b/src/gui/theme/colorproviders.cpp @@ -0,0 +1,193 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "colorproviders.h" + +#include + +#ifdef PLASMA_INTEGRATION +#include "plasmacolorprovider.h" +#endif + +namespace +{ + struct ColorProvidersRegistrator + { + ColorProvidersRegistrator() + { + using Registry = Theme::Serialization::ColorsProviderRegistry; + using ProviderUPtr = Registry::ProviderUPtr; + + Registry::instance().registerProvider(ProviderUPtr(new Theme::Serialization::ExplicitColorProvider())); + Registry::instance().registerProvider(ProviderUPtr(new Theme::Serialization::PaletteColorProvider())); + } + }; + + const QMetaEnum &QPaletteGroupMeta() + { + static QMetaEnum res = QMetaEnum::fromType(); + return res; + } + + const QMetaEnum &QPaletteRoleMeta() + { + static QMetaEnum res = QMetaEnum::fromType(); + return res; + } +} + +void Theme::Serialization::registerColorProviders() +{ + static ColorProvidersRegistrator colorProvidersRegistrator; +#ifdef PLASMA_INTEGRATION + registerPlasmaColorProviders(); +#endif +} + +Theme::Serialization::ExplicitColor::ExplicitColor(const QColor &color) + : m_value {color} +{ +} + +/* ExplicitColor is just what it means: a single QColor value. + * It is serialized into a string formatted as follows: ,,, e.g.: 127,234,12 + */ +Theme::Serialization::ExplicitColor::ExplicitColor(const QString &serialized) + : m_value {QColor(serialized)} +{ + if (!m_value.isValid()) { + throw ValueParsingError("QColor could not recognize color string", serialized); + } +} + +QColor Theme::Serialization::ExplicitColor::value() const +{ + return m_value; +} + +QString Theme::Serialization::ExplicitColor::serializedValue() const +{ + return explicitSerializedValue(); +} + +QString Theme::Serialization::ExplicitColor::serializationKey() const +{ + return QLatin1String("RGB"); +} + +QPalette Theme::Serialization::PaletteColor::m_palette; + +Theme::Serialization::PaletteColor::PaletteColor(QPalette::ColorGroup group, QPalette::ColorRole role) + : m_group(group) + , m_role(role) +{ +} + +Theme::Serialization::PaletteColor::PaletteColor(const QString &serialized) +{ + const auto list = serialized.split(QLatin1Char(':')); + if (list.size() != 2) + throw ValueParsingError("QPalette color notation should have 2 components", serialized); + + bool parsedOk = false; + const int groupRawVal = QPaletteGroupMeta().keyToValue(list[0].toLatin1().constData(), &parsedOk); + if (!parsedOk) { + throw ValueParsingError("Could not parse QPalette group name", serialized); + } + m_group = static_cast(groupRawVal); + + const int roleRawVal = QPaletteRoleMeta().keyToValue(list[1].toLatin1().constData(), &parsedOk); + if (!parsedOk) { + throw ValueParsingError("Could not parse QPalette role name", serialized); + } + m_role = static_cast(roleRawVal); +} + +void Theme::Serialization::PaletteColor::reloadDefaultPalette() +{ + m_palette = QPalette(); // which asks QApplication instance for the current default palette +} + +QColor Theme::Serialization::PaletteColor::value() const +{ + return m_palette.color(m_group, m_role); +} + +QString Theme::Serialization::PaletteColor::serializedValue() const +{ + return QString(QLatin1String("%1:%2")) + .arg(QPaletteGroupMeta().key(m_group)).arg(QPaletteRoleMeta().key(m_role)); +} + +QString Theme::Serialization::PaletteColor::serializationKey() const +{ + return QLatin1String("QPalette"); +} + +void Theme::Serialization::PaletteColorProvider::applicationPaletteChanged() const +{ + PaletteColor::reloadDefaultPalette(); +} + +Theme::Serialization::ExplicitColorProvider::ExplicitColorProvider() + : ColorProvider(ExplicitColor(QColor(Qt::black)).serializationKey()) +{ +} + +Theme::Serialization::ExplicitColorProvider::EntityUPtr +Theme::Serialization::ExplicitColorProvider::load(const QString& serialized) const +{ + return EntityUPtr(new ExplicitColor(serialized)); +} + +Theme::Serialization::PaletteColorProvider::PaletteColorProvider() + : ColorProvider(PaletteColor(QPalette::Active, QPalette::Window).serializationKey()) +{ +} + +Theme::Serialization::PaletteColorProvider::EntityUPtr +Theme::Serialization::PaletteColorProvider::load(const QString& serialized) const +{ + return EntityUPtr(new PaletteColor(serialized)); +} + +Theme::Serialization::ColorProviderWidgetUI::~ColorProviderWidgetUI() = default; + +Theme::Serialization::ColorPickingWidget * +Theme::Serialization::ExplicitColorProvider::createEditorWidget(QWidget* parentWidget) const +{ + Q_UNUSED(parentWidget); + throw std::logic_error("Not implemented"); +} + +Theme::Serialization::ColorPickingWidget * +Theme::Serialization::PaletteColorProvider::createEditorWidget(QWidget* parentWidget) const +{ + Q_UNUSED(parentWidget); + throw std::logic_error("Not implemented"); +} diff --git a/src/gui/theme/colorproviders.h b/src/gui/theme/colorproviders.h new file mode 100644 index 0000000000..27b55dc653 --- /dev/null +++ b/src/gui/theme/colorproviders.h @@ -0,0 +1,130 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_COLORPROVIDERS_H +#define QBT_THEME_COLORPROVIDERS_H + +#include + +#include "colorprovider_p.h" + +namespace Theme +{ + namespace Serialization + { + void registerColorProviders(); + + class ExplicitColor: public Color + { + public: + explicit ExplicitColor(const QColor &color); + explicit ExplicitColor(const QString &serialized); + + QColor value() const override; + QString serializedValue() const override; + QString serializationKey() const override; + + private: + QColor m_value; + }; + + // links to a color from app default palette; + class PaletteColor: public Color + { + public: + explicit PaletteColor(QPalette::ColorGroup group, QPalette::ColorRole role); + explicit PaletteColor(const QString &serialized); + + QColor value() const override; + QString serializedValue() const override; + QString serializationKey() const override; + + static void reloadDefaultPalette(); + + private: + static QPalette m_palette; + QPalette::ColorGroup m_group; + QPalette::ColorRole m_role; + }; + + class ColorPickingWidget: public QWidget + { + public: + ColorPickingWidget(QWidget *parent); + + /** + * @brief ... + * + * @return may return nullptr + */ + virtual ColorProvider::EntityUPtr selectedColor() const = 0; + virtual void selectColor(const Color &color) = 0; + }; + + /*! \brief Interface provides Widget-based UI for color providers + * + * This interface may be used when a color theme is edited and user wants to + * select one of the supported colors using QWidget-based UI + */ + class ColorProviderWidgetUI + { + public: + virtual ~ColorProviderWidgetUI(); + virtual ColorPickingWidget *createEditorWidget(QWidget *parentWidget) const = 0; + }; + + namespace impl + { + class ExplicitColorWidget; + class PaletteColorWidget; + } + + class ExplicitColorProvider: public ColorProvider, public ColorProviderWidgetUI + { + public: + ExplicitColorProvider(); + + private: + ColorPickingWidget *createEditorWidget(QWidget *parentWidget) const override; + ColorProvider::EntityUPtr load(const QString &serialized) const override; + }; + + class PaletteColorProvider: public ColorProvider, public ColorProviderWidgetUI + { + public: + PaletteColorProvider(); + + private: + ColorPickingWidget *createEditorWidget(QWidget *parentWidget) const override; + ColorProvider::EntityUPtr load(const QString &serialized) const override; + void applicationPaletteChanged() const override; + }; + } +} + +#endif // QBT_THEME_COLORPROVIDERS_H diff --git a/src/gui/theme/colortheme.cpp b/src/gui/theme/colortheme.cpp new file mode 100644 index 0000000000..0759ded817 --- /dev/null +++ b/src/gui/theme/colortheme.cpp @@ -0,0 +1,38 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "colortheme.h" + +#include "themeprovider.h" + +Theme::ColorTheme::~ColorTheme() = default; + +const Theme::ColorTheme &Theme::ColorTheme::current() +{ + return ThemeProvider::instance().colorTheme(); +} diff --git a/src/gui/theme/colortheme.h b/src/gui/theme/colortheme.h new file mode 100644 index 0000000000..0efebaf5d2 --- /dev/null +++ b/src/gui/theme/colortheme.h @@ -0,0 +1,81 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_COLORTHEME_H +#define QBT_THEME_COLORTHEME_H + +#include + +#include +#include + +class QColor; +class QSettings; + +namespace BitTorrent +{ + enum class TorrentState; +} + +namespace Log +{ + enum MsgType: int; +} + +namespace Theme +{ + enum class DownloadProgressBarElement + { + Background, + Border, + Complete, + Incomplete + }; + + class ThemeInfo; + + /*! \brief qBittorrent color theme + * + * Contains all colors that are needed to render a qBt UI, either using widgets or Web + */ + class ColorTheme + { + public: + virtual ~ColorTheme(); + + virtual ThemeInfo info() const = 0; + + virtual QColor torrentStateColor(const BitTorrent::TorrentState state) const = 0; + virtual QColor logMessageColor(const Log::MsgType messageType) const = 0; + virtual QColor downloadProgressBarColor(const DownloadProgressBarElement element) const = 0; + + static const ColorTheme ¤t(); + }; +} + +#endif // QBT_THEME_COLORTHEME_H diff --git a/src/gui/theme/fontprovider_p.cpp b/src/gui/theme/fontprovider_p.cpp new file mode 100644 index 0000000000..4523655fa5 --- /dev/null +++ b/src/gui/theme/fontprovider_p.cpp @@ -0,0 +1,48 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "fontprovider_p.h" + +#include + +// ------------------------- FontProviderRegistry ------------------------------ +namespace +{ + class FontProviderRegistrySignletonImpl: public Theme::Serialization::FontProviderRegistry + { + public: + ~FontProviderRegistrySignletonImpl() = default; + }; + + Q_GLOBAL_STATIC(FontProviderRegistrySignletonImpl, fontProviderRegistrySignletonImpl) +} + +Theme::Serialization::FontProviderRegistry &Theme::Serialization::FontProviderRegistry::instance() +{ + return *fontProviderRegistrySignletonImpl(); +} diff --git a/src/gui/theme/fontprovider_p.h b/src/gui/theme/fontprovider_p.h new file mode 100644 index 0000000000..ebe5d80b14 --- /dev/null +++ b/src/gui/theme/fontprovider_p.h @@ -0,0 +1,63 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_FONTPROVIDER_P_H +#define QBT_THEME_FONTPROVIDER_P_H + +#include "provider_p.h" + +#include + +namespace Theme +{ + namespace Serialization + { + class FontDeserializationError: public DeserializationError + { + public: + using DeserializationError::DeserializationError; + }; + + using Font = Entity; + + class FontProvider: public Provider + { + public: + using Provider::Provider; + }; + + class FontProviderRegistry: public ProviderRegistry + { + public: + static const Kind kind = Kind::Font; + static FontProviderRegistry &instance(); + }; + } +} + +#endif // QBT_THEME_FONTPROVIDER_P_H diff --git a/src/gui/theme/fontproviders.cpp b/src/gui/theme/fontproviders.cpp new file mode 100644 index 0000000000..b82554d606 --- /dev/null +++ b/src/gui/theme/fontproviders.cpp @@ -0,0 +1,107 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "fontproviders.h" + +#include "themeexceptions.h" + +namespace +{ + struct FontProvidersRegistrator + { + FontProvidersRegistrator() + { + using Registry = Theme::Serialization::FontProviderRegistry; + using ProviderUPtr = Registry::ProviderUPtr; + + Registry::instance().registerProvider(ProviderUPtr(new Theme::Serialization::ExplicitFontProvider())); + } + }; +} + +void Theme::Serialization::registerFontProviders() +{ + static FontProvidersRegistrator fontProvidersRegistrator; +} + +Theme::Serialization::ExplicitFont::ExplicitFont(const QFont &color) + : m_value {color} +{ +} + +/* ExplicitFont is just what it means: a single QFont value. + * It is serialized into a string formatted as QFont::toString() + */ +Theme::Serialization::ExplicitFont::ExplicitFont(const QString &serialized) + : m_value {fromString(serialized)} +{ +} + +QFont Theme::Serialization::ExplicitFont::fromString(const QString &str) +{ + if (str.isEmpty()) { + // they want default app font + return QFont(); + } + QFont res; + if (!res.fromString(str)) { + throw ValueParsingError("QFont could not recognize font string", str); + } + return res; +} + +QFont Theme::Serialization::ExplicitFont::value() const +{ + return m_value; +} + +QString Theme::Serialization::ExplicitFont::explicitSerializedValue() const +{ + return m_value.toString(); +} + +QString Theme::Serialization::ExplicitFont::serializedValue() const +{ + return explicitSerializedValue(); +} + +QString Theme::Serialization::ExplicitFont::serializationKey() const +{ + return QLatin1String("QFont"); +} + +Theme::Serialization::ExplicitFontProvider::ExplicitFontProvider() + : FontProvider(ExplicitFont(QFont()).serializationKey()) +{ +} + +Theme::Serialization::ExplicitFontProvider::EntityUPtr +Theme::Serialization::ExplicitFontProvider::load(const QString& serialized) const +{ + return EntityUPtr(new ExplicitFont(serialized)); +} diff --git a/src/gui/theme/fontproviders.h b/src/gui/theme/fontproviders.h new file mode 100644 index 0000000000..5570777e8f --- /dev/null +++ b/src/gui/theme/fontproviders.h @@ -0,0 +1,67 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_GUIFONTPROVIDERS_H +#define QBT_THEME_GUIFONTPROVIDERS_H + +#include "fontprovider_p.h" + +namespace Theme +{ + namespace Serialization + { + void registerFontProviders(); + + class ExplicitFont: public Font + { + public: + explicit ExplicitFont(const QFont &color); + explicit ExplicitFont(const QString &serialized); + + QFont value() const override; + QString serializedValue() const override; + QString explicitSerializedValue() const override; + QString serializationKey() const override; + + private: + static QFont fromString(const QString &str); + QFont m_value; + }; + + class ExplicitFontProvider: public FontProvider + { + public: + ExplicitFontProvider(); + + private: + FontProvider::EntityUPtr load(const QString &serialized) const override; + }; + } +} + +#endif // QBT_THEME_GUIFONTPROVIDERS_H diff --git a/src/gui/theme/fonttheme.cpp b/src/gui/theme/fonttheme.cpp new file mode 100644 index 0000000000..a4893371bb --- /dev/null +++ b/src/gui/theme/fonttheme.cpp @@ -0,0 +1,38 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "fonttheme.h" + +#include "themeprovider.h" + +Theme::FontTheme::~FontTheme() = default; + +const Theme::FontTheme &Theme::FontTheme::current() +{ + return ThemeProvider::instance().fontTheme(); +} diff --git a/src/gui/theme/fonttheme.h b/src/gui/theme/fonttheme.h new file mode 100644 index 0000000000..1e64df777a --- /dev/null +++ b/src/gui/theme/fonttheme.h @@ -0,0 +1,59 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_FONTTHEME_H +#define QBT_THEME_FONTTHEME_H + +#include + +class QFont; + +namespace Theme +{ + class ThemeInfo; + + enum class FontThemeElement + { + TransferList, + TorrentProperties, + ExecutionLog + }; + + class FontTheme + { + public: + virtual ~FontTheme(); + + virtual const ThemeInfo &info() const = 0; + virtual const QFont &font(FontThemeElement element) const = 0; + + static const FontTheme ¤t(); + }; +} + +#endif // QBT_THEME_FONTTHEME_H diff --git a/src/gui/theme/plasmacolorprovider.cpp b/src/gui/theme/plasmacolorprovider.cpp new file mode 100644 index 0000000000..dec3fa1258 --- /dev/null +++ b/src/gui/theme/plasmacolorprovider.cpp @@ -0,0 +1,192 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "plasmacolorprovider.h" + +#include + +#include "utils/colorutils.h" +#include "utils/plasmacolorscheme.h" + +namespace +{ + struct ColorProvidersRegistrator + { + ColorProvidersRegistrator() + { + using Registry = Theme::Serialization::ColorsProviderRegistry; + using ProviderUPtr = Registry::ProviderUPtr; + + Registry::instance().registerProvider(ProviderUPtr(new Theme::Serialization::PlasmaColorProvider())); + } + }; + + const QMetaEnum &QPaletteGroupMeta() + { + static QMetaEnum res = QMetaEnum::fromType(); + return res; + } + + const QMetaEnum &plasmaColorSetMeta() + { + static QMetaEnum res = QMetaEnum::fromType(); + return res; + } + + const QMetaEnum &plasmaColorRoleMeta() + { + static QMetaEnum res = QMetaEnum::fromType(); + return res; + } + + const QMetaEnum &enhancementMeta() + { + static QMetaEnum res = QMetaEnum::fromType(); + return res; + } + + QColor lighten(const QColor &c) + { + return Utils::Color::lighten(c, 0.25); + } + + QColor darken(const QColor &c) + { + return Utils::Color::darken(c, 0.25); + } +} + +std::unique_ptr Theme::Serialization::PlasmaColor::m_colorSheme; +Theme::Serialization::PlasmaColor::ColorCorrectionFunc Theme::Serialization::PlasmaColor::m_intencifyColor; +Theme::Serialization::PlasmaColor::ColorCorrectionFunc Theme::Serialization::PlasmaColor::m_reduceColor; + +void Theme::Serialization::registerPlasmaColorProviders() +{ + static ColorProvidersRegistrator registrator; +} + +Theme::Serialization::PlasmaColor::PlasmaColor(QPalette::ColorGroup group, + PlasmaColorScheme::ColorSet set, + PlasmaColorScheme::ColorRole role) + : m_group {group} + , m_set {set} + , m_role {role} +{ +} + +namespace { + template + Enum stringToEnum(const QString &str, const QMetaEnum &meta) + { + bool ok = false; + const int rawVal = meta.keyToValue(str.toLatin1().constData(), &ok); + if (!ok) { + throw std::runtime_error("Can not parse " + str.toStdString() + " into " + meta.name()); + } + return static_cast(rawVal); + } +} + +Theme::Serialization::PlasmaColor::PlasmaColor(const QString& serialized) +{ + const auto list = serialized.split(QLatin1Char(':')); + if (list.size() < 1 || list.size() > 4) + throw ValueParsingError("Plasma color notation should have 1 or 4 components", serialized); + + try { + m_role = stringToEnum(list[0], plasmaColorRoleMeta()); + m_group = list.size() > 1 ? stringToEnum(list[1], QPaletteGroupMeta()) + : QPalette::Active; + m_set = list.size() > 2 ? stringToEnum(list[2], plasmaColorSetMeta()) + : PlasmaColorScheme::View; + m_enhancement = list.size() > 3 ? stringToEnum(list[3], enhancementMeta()) + : Enhancement::None; + } + catch (std::runtime_error&) { + throw ValueParsingError("Could not parse Plasma color", serialized); + } +} + +void Theme::Serialization::PlasmaColor::reloadDefaultPalette() +{ + if (!m_colorSheme) { + m_colorSheme.reset(new PlasmaColorScheme()); + } + else { + m_colorSheme->reload(); + } + const bool isDarkTheme = Theme::isDarkTheme(); + m_intencifyColor = isDarkTheme ? &lighten : &darken; + m_reduceColor = isDarkTheme ? &darken : &lighten; +} + +QString Theme::Serialization::PlasmaColor::serializationKey() const +{ + return QLatin1String("Plasma"); +} + +QString Theme::Serialization::PlasmaColor::serializedValue() const +{ + return QString(QLatin1String("%1:%2:%3:4%")) + .arg(plasmaColorRoleMeta().key(m_role)) + .arg(QPaletteGroupMeta().key(m_group)) + .arg(plasmaColorSetMeta().key(m_set)) + .arg(enhancementMeta().key(static_cast(m_enhancement))); +} + +QColor Theme::Serialization::PlasmaColor::value() const +{ + QColor c = m_colorSheme->color(m_role, m_group, m_set); + switch (m_enhancement) { + case Enhancement::None: + return c; + case Enhancement::Intensify: + return m_intencifyColor(c); + case Enhancement::Reduce: + return m_reduceColor(c); + default: + throw std::runtime_error("Unexpected value of m_enhancement"); + } +} + +Theme::Serialization::PlasmaColorProvider::PlasmaColorProvider() + : ColorProvider(PlasmaColor(QPalette::Active, PlasmaColorScheme::View, PlasmaColorScheme::ForegroundActive).serializationKey()) +{ + PlasmaColor::reloadDefaultPalette(); +} + +void Theme::Serialization::PlasmaColorProvider::applicationPaletteChanged() const +{ + PlasmaColor::reloadDefaultPalette(); +} + +Theme::Serialization::ColorProvider::EntityUPtr +Theme::Serialization::PlasmaColorProvider::load(const QString& serialized) const +{ + return EntityUPtr(new PlasmaColor(serialized)); +} diff --git a/src/gui/theme/plasmacolorprovider.h b/src/gui/theme/plasmacolorprovider.h new file mode 100644 index 0000000000..9cbe51d950 --- /dev/null +++ b/src/gui/theme/plasmacolorprovider.h @@ -0,0 +1,93 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_PLASMACOLORPROVIDERS_H +#define QBT_THEME_PLASMACOLORPROVIDERS_H + +#include "colorprovider_p.h" +#include "utils/plasmacolorscheme.h" + +class QWidget; + +namespace Theme +{ + namespace Serialization + { + void registerPlasmaColorProviders(); + + /** + * @brief Provides colors from Plasma color scheme + * + */ + class PlasmaColor: public Color + { + Q_GADGET + public: + enum class Enhancement + { + None, + Intensify, + Reduce + }; + + Q_ENUM(Enhancement) + + explicit PlasmaColor(QPalette::ColorGroup group, PlasmaColorScheme::ColorSet set, PlasmaColorScheme::ColorRole role); + explicit PlasmaColor(const QString &serialized); + + QColor value() const override; + QString serializedValue() const override; + QString serializationKey() const override; + + static void reloadDefaultPalette(); + + private: + using ColorCorrectionFunc = QColor (*)(const QColor&); + static ColorCorrectionFunc m_intencifyColor; + static ColorCorrectionFunc m_reduceColor; + + static std::unique_ptr m_colorSheme; + QPalette::ColorGroup m_group; + PlasmaColorScheme::ColorSet m_set; + PlasmaColorScheme::ColorRole m_role; + Enhancement m_enhancement; + }; + + class PlasmaColorProvider: public ColorProvider + { + public: + PlasmaColorProvider(); + + private: + ColorProvider::EntityUPtr load(const QString &serialized) const override; + void applicationPaletteChanged() const override; + }; + } +} + +#endif // QBT_THEME_PLASMACOLORPROVIDERS_H diff --git a/src/gui/theme/provider_p.cpp b/src/gui/theme/provider_p.cpp new file mode 100644 index 0000000000..966d302f1b --- /dev/null +++ b/src/gui/theme/provider_p.cpp @@ -0,0 +1,30 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "provider_p.h" + diff --git a/src/gui/theme/provider_p.h b/src/gui/theme/provider_p.h new file mode 100644 index 0000000000..8ffd5499e3 --- /dev/null +++ b/src/gui/theme/provider_p.h @@ -0,0 +1,168 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_PROVIDER_P_H +#define QBT_THEME_PROVIDER_P_H + +#include +#include +#include +#include + +#include + +#include "themecommon.h" +#include "themeexceptions.h" + +namespace Theme +{ + namespace Serialization + { + /* + * Serialization: each serializable object shall be able to serialize itself into a QString value. + * To freely mix objects from different sources within a theme, they are serialized as ID:value pairs. + * For example: + * + * Background=RGB:128,128,125 + * Stalled=QPalette:Inactive,Midlight + * + * The value format is determined by the object, while : part is written and read + * by the managing code. + */ + + template + class Entity + { + public: + using ValueType = Value; + + virtual ~Entity() = default; + virtual Value value() const = 0; + virtual QString serializedValue() const = 0; + virtual QString explicitSerializedValue() const = 0; + virtual QString serializationKey() const = 0; + }; + + template + class Provider + { + public: + using EntityType = Entity; + using EntityUPtr = std::unique_ptr; + virtual ~Provider() = default; + QString name() const; + virtual EntityUPtr load(const QString &serialized) const = 0; + + protected: + Provider(const QString &name); + + private: + QString m_name; + }; + + template + class ProviderRegistry + { + public: + using ProviderType = Provider; + using ProviderUPtr = std::unique_ptr; + using EntityUPtr = typename ProviderType::EntityUPtr; + + void registerProvider(ProviderUPtr creator); + const ProviderType *provider(const QString &name) const; + EntityUPtr load(const QString &serializedWithKey) const; + + void applicationPaletteChanged() const; + + protected: + ~ProviderRegistry() = default; + + using ProvidersMap = std::map; + const ProvidersMap &providers() const; + + private: + ProvidersMap m_providers; + }; + + // ------------------------- implementation ------------------------------ + template + inline Provider::Provider(const QString &name) + : m_name(name) + { + } + + template + inline QString Provider::name() const + { + return m_name; + } + + template + inline void ProviderRegistry::registerProvider(ProviderUPtr creator) + { + const QString id = creator->name(); + Q_ASSERT_X(m_providers.count(id) == 0, Q_FUNC_INFO, + "Attempted to register the same theme color type twice"); + m_providers[id] = std::move(creator); + } + + template + inline const typename ProviderRegistry::ProviderType * + ProviderRegistry::provider(const QString &name) const + { + const auto i = m_providers.find(name); + if (i != m_providers.end()) + return i->second.get(); + return nullptr; + } + + template + inline typename ProviderRegistry::EntityUPtr + ProviderRegistry::load(const QString &serializedWithKey) const + { + const int keyValueSeparatorPos = serializedWithKey.indexOf(QLatin1Char(':'), 0); + if (keyValueSeparatorPos != -1) { + const QString id = serializedWithKey.left(keyValueSeparatorPos); + const auto *provider = this->provider(id); + if (provider) + return provider->load(serializedWithKey.right(serializedWithKey.size() - keyValueSeparatorPos - 1)); + throw UnknownProvider(serializedWithKey); + } + qCInfo(theme, "Failed to parse serialized value '%s'", qPrintable(serializedWithKey)); + throw ParsingError(serializedWithKey); + } + + template + const typename ProviderRegistry::ProvidersMap &ProviderRegistry::providers() const + { + return m_providers; + } + } +} + +#endif // QBT_THEME_PROVIDER_P_H diff --git a/src/gui/theme/qBittorrent.fonttheme b/src/gui/theme/qBittorrent.fonttheme new file mode 100644 index 0000000000..4a920d119a --- /dev/null +++ b/src/gui/theme/qBittorrent.fonttheme @@ -0,0 +1,8 @@ +[Info] +Name=Default +Description=Default qBittorrent font theme. + +[Fonts] +TransferList=QFont: +TorrentProperties=QFont: +ExecutionLog=QFont: diff --git a/src/gui/theme/qBittorrentDark.colortheme b/src/gui/theme/qBittorrentDark.colortheme new file mode 100644 index 0000000000..95c866f520 --- /dev/null +++ b/src/gui/theme/qBittorrentDark.colortheme @@ -0,0 +1,27 @@ +[Info] +Name=Default (Dark) +Description=Default qBittorrent color theme. Dark flavour. +Inherits=Default (Light) + +[TorrentState] +Uploading=RGB:#63b8ff +PausedUploading=RGB:#4f94cd +QueuedUploading=RGB:#00cdcd +StalledUploading=RGB:#cccccc +CheckingUploading=RGB:#00cdcd +ForcedUploading=RGB:#63b8ff +Allocating=RGB:#cccccc +Downloading=RGB:#32cd32 +DownloadingMetadata=RGB:#32cd32 +PausedDownloading=RGB:#fa8072 +QueuedDownloading=RGB:#00cdcd +StalledDownloading=RGB:#cccccc +CheckingDownloading=RGB:#00cdcd +ForcedDownloading=RGB:#32cd32 +QueuedForChecking=RGB:#00cdcd +CheckingResumeData=RGB:#00cdcd + +[LogMessageType] +INFO=RGB:lightblue +WARNING=RGB:lightsalmon +CRITICAL=RGB:red diff --git a/src/gui/theme/qBittorrentLight.colortheme b/src/gui/theme/qBittorrentLight.colortheme new file mode 100644 index 0000000000..c2590e1a1b --- /dev/null +++ b/src/gui/theme/qBittorrentLight.colortheme @@ -0,0 +1,37 @@ +[Info] +Name=Default (Light) +Description=Default qBittorrent color theme. Light flavour. + +[TorrentState] +Unknown=RGB:#ff0000 +Error=RGB:#ff0000 +MissingFiles=RGB:#ff0000 +Uploading=RGB:#4169e1 +PausedUploading=RGB:#000081 +QueuedUploading=RGB:#008080 +StalledUploading=RGB:#000000 +CheckingUploading=RGB:#008080 +ForcedUploading=RGB:#4169e1 +Allocating=RGB:#000000 +Downloading=RGB:#228b22 +DownloadingMetadata=RGB:#228b22 +PausedDownloading=RGB:#fa8072 +QueuedDownloading=RGB:#008080 +StalledDownloading=RGB:#000000 +CheckingDownloading=RGB:#008080 +ForcedDownloading=RGB:#228b22 +QueuedForChecking=RGB:#008080 +CheckingResumeData=RGB:#008080 + +[LogMessageType] +ALL=QPalette:Active:WindowText +NORMAL=QPalette:Active:WindowText +INFO=RGB:blue +WARNING=RGB:orange +CRITICAL=RGB:red + +[DownloadProgressBar] +Background=RGB:white +Border=QPalette:Active:Dark +Complete=RGB:blue +Incomplete=RGB:#00d000 diff --git a/src/gui/theme/serializablecolortheme.cpp b/src/gui/theme/serializablecolortheme.cpp new file mode 100644 index 0000000000..6bac29b650 --- /dev/null +++ b/src/gui/theme/serializablecolortheme.cpp @@ -0,0 +1,310 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "serializablecolortheme.h" + +#include +#include +#include + +#include + +#include "base/bittorrent/torrenthandle.h" +#include "base/logger.h" + +#include "colorprovider_p.h" +#include "serializabletheme.h" +#include "themeprovider.h" + +namespace +{ + const QByteArray torrentStateColorsGroup("TorrentState/"); + const QByteArray msgTypeColorsGroup("LogMessageType/"); + const QByteArray downloadProggressBarColorsGroup("DownloadProgressBar/"); + + const QLatin1String defaultLightColorThemeName ("Default (Light)"); + const QLatin1String defaultDarkColorThemeName ("Default (Dark)"); + + Theme::ColorThemeElement toThemeElement(Theme::DownloadProgressBarElement element) + { + switch (element) { + case Theme::DownloadProgressBarElement::Background: + return Theme::ColorThemeElement::DownloadProgressBarElementBackground; + case Theme::DownloadProgressBarElement::Border: + return Theme::ColorThemeElement::DownloadProgressBarElementBorder; + case Theme::DownloadProgressBarElement::Complete: + return Theme::ColorThemeElement::DownloadProgressBarElementComplete; + case Theme::DownloadProgressBarElement::Incomplete: + return Theme::ColorThemeElement::DownloadProgressBarElementIncomplete; + default: + throw std::logic_error("Unexpected DownloadProgressBarElement value"); + } + } + + Theme::ColorThemeElement toThemeElement(Log::MsgType messageType) + { + switch (messageType) { + case Log::MsgType::ALL: + return Theme::ColorThemeElement::MsgTypeALL; + case Log::MsgType::NORMAL: + return Theme::ColorThemeElement::MsgTypeNORMAL; + case Log::MsgType::INFO: + return Theme::ColorThemeElement::MsgTypeINFO; + case Log::MsgType::WARNING: + return Theme::ColorThemeElement::MsgTypeWARNING; + case Log::MsgType::CRITICAL: + return Theme::ColorThemeElement::MsgTypeCRITICAL; + default: + throw std::logic_error("Unexpected Log::MsgType value"); + } + } + + Theme::ColorThemeElement toThemeElement(BitTorrent::TorrentState state) + { + switch (state) { + case BitTorrent::TorrentState::Unknown: + return Theme::ColorThemeElement::TorrentStateUnknown; + case BitTorrent::TorrentState::Error: + return Theme::ColorThemeElement::TorrentStateError; + case BitTorrent::TorrentState::MissingFiles: + return Theme::ColorThemeElement::TorrentStateMissingFiles; + case BitTorrent::TorrentState::Uploading: + return Theme::ColorThemeElement::TorrentStateUploading; + case BitTorrent::TorrentState::PausedUploading: + return Theme::ColorThemeElement::TorrentStatePausedUploading; + case BitTorrent::TorrentState::QueuedUploading: + return Theme::ColorThemeElement::TorrentStateQueuedUploading; + case BitTorrent::TorrentState::StalledUploading: + return Theme::ColorThemeElement::TorrentStateStalledUploading; + case BitTorrent::TorrentState::CheckingUploading: + return Theme::ColorThemeElement::TorrentStateCheckingUploading; + case BitTorrent::TorrentState::ForcedUploading: + return Theme::ColorThemeElement::TorrentStateForcedUploading; + case BitTorrent::TorrentState::Allocating: + return Theme::ColorThemeElement::TorrentStateAllocating; + case BitTorrent::TorrentState::Downloading: + return Theme::ColorThemeElement::TorrentStateDownloading; + case BitTorrent::TorrentState::DownloadingMetadata: + return Theme::ColorThemeElement::TorrentStateDownloadingMetadata; + case BitTorrent::TorrentState::PausedDownloading: + return Theme::ColorThemeElement::TorrentStatePausedDownloading; + case BitTorrent::TorrentState::QueuedDownloading: + return Theme::ColorThemeElement::TorrentStateQueuedDownloading; + case BitTorrent::TorrentState::StalledDownloading: + return Theme::ColorThemeElement::TorrentStateStalledDownloading; + case BitTorrent::TorrentState::CheckingDownloading: + return Theme::ColorThemeElement::TorrentStateCheckingDownloading; + case BitTorrent::TorrentState::ForcedDownloading: + return Theme::ColorThemeElement::TorrentStateForcedDownloading; + #if LIBTORRENT_VERSION_NUM < 10100 + case BitTorrent::TorrentState::QueuedForChecking: + return Theme::ColorThemeElement::TorrentStateQueuedForChecking; + #endif + case BitTorrent::TorrentState::CheckingResumeData: + return Theme::ColorThemeElement::TorrentStateCheckingResumeData; + default: + throw std::logic_error("Unexpected TorrentState value"); + } + } +} + +Theme::SerializableColorTheme::SerializableColorTheme(const QString &name) + : BaseSerializableTheme(name, elementNames()) +{ +} + +void Theme::SerializableColorTheme::applicationPaletteChanged() +{ + updateCache(); +} + +Theme::SerializableColorTheme::BaseSerializableTheme::NamesMap +Theme::SerializableColorTheme::elementNames() +{ + const auto makeTorrentStateNamePair = [](BitTorrent::TorrentState st) + { + return std::make_pair(QByteArray(torrentStateColorsGroup + colorSaveName(st)), static_cast(toThemeElement(st))); + }; + + const auto makeMsgTypeNamePair = [](Log::MsgType mt) + { + return std::make_pair(QByteArray(msgTypeColorsGroup + colorSaveName(mt)), static_cast(toThemeElement(mt))); + }; + + const auto makeDownloadProgressBarNamePair = [](DownloadProgressBarElement e) + { + return std::make_pair(QByteArray(downloadProggressBarColorsGroup + colorSaveName(e)), static_cast(toThemeElement(e))); + }; + + return { + makeTorrentStateNamePair(BitTorrent::TorrentState::Unknown), + makeTorrentStateNamePair(BitTorrent::TorrentState::Error), + makeTorrentStateNamePair(BitTorrent::TorrentState::MissingFiles), + makeTorrentStateNamePair(BitTorrent::TorrentState::Uploading), + makeTorrentStateNamePair(BitTorrent::TorrentState::PausedUploading), + makeTorrentStateNamePair(BitTorrent::TorrentState::QueuedUploading), + makeTorrentStateNamePair(BitTorrent::TorrentState::StalledUploading), + makeTorrentStateNamePair(BitTorrent::TorrentState::CheckingUploading), + makeTorrentStateNamePair(BitTorrent::TorrentState::ForcedUploading), + makeTorrentStateNamePair(BitTorrent::TorrentState::Allocating), + makeTorrentStateNamePair(BitTorrent::TorrentState::Downloading), + makeTorrentStateNamePair(BitTorrent::TorrentState::DownloadingMetadata), + makeTorrentStateNamePair(BitTorrent::TorrentState::PausedDownloading), + makeTorrentStateNamePair(BitTorrent::TorrentState::QueuedDownloading), + makeTorrentStateNamePair(BitTorrent::TorrentState::StalledDownloading), + makeTorrentStateNamePair(BitTorrent::TorrentState::CheckingDownloading), + makeTorrentStateNamePair(BitTorrent::TorrentState::ForcedDownloading), +#if LIBTORRENT_VERSION_NUM < 10100 + makeTorrentStateNamePair(BitTorrent::TorrentState::QueuedForChecking), +#endif + makeTorrentStateNamePair(BitTorrent::TorrentState::CheckingResumeData), + + makeMsgTypeNamePair(Log::MsgType::ALL), + makeMsgTypeNamePair(Log::MsgType::NORMAL), + makeMsgTypeNamePair(Log::MsgType::INFO), + makeMsgTypeNamePair(Log::MsgType::WARNING), + makeMsgTypeNamePair(Log::MsgType::CRITICAL), + + makeDownloadProgressBarNamePair(DownloadProgressBarElement::Background), + makeDownloadProgressBarNamePair(DownloadProgressBarElement::Border), + makeDownloadProgressBarNamePair(DownloadProgressBarElement::Complete), + makeDownloadProgressBarNamePair(DownloadProgressBarElement::Incomplete) + }; +} + +Theme::ThemeInfo Theme::SerializableColorTheme::info() const +{ + return BaseSerializableTheme::info(); +} + +QByteArray Theme::SerializableColorTheme::colorSaveName(BitTorrent::TorrentState state) +{ + // TODO Qt 5.8+: use Q_NAMESPACE + Q_ENUM_NS + // static QMetaEnum meta = QMetaEnum::fromType(); + // return QString::fromLatin1(meta.key(static_cast(state))); + switch (state) { + case BitTorrent::TorrentState::Unknown: + return "Unknown"; + case BitTorrent::TorrentState::Error: + return "Error"; + case BitTorrent::TorrentState::MissingFiles: + return "MissingFiles"; + case BitTorrent::TorrentState::Uploading: + return "Uploading"; + case BitTorrent::TorrentState::PausedUploading: + return "PausedUploading"; + case BitTorrent::TorrentState::QueuedUploading: + return "QueuedUploading"; + case BitTorrent::TorrentState::StalledUploading: + return "StalledUploading"; + case BitTorrent::TorrentState::CheckingUploading: + return "CheckingUploading"; + case BitTorrent::TorrentState::ForcedUploading: + return "ForcedUploading"; + case BitTorrent::TorrentState::Allocating: + return "Allocating"; + case BitTorrent::TorrentState::Downloading: + return "Downloading"; + case BitTorrent::TorrentState::DownloadingMetadata: + return "DownloadingMetadata"; + case BitTorrent::TorrentState::PausedDownloading: + return "PausedDownloading"; + case BitTorrent::TorrentState::QueuedDownloading: + return "QueuedDownloading"; + case BitTorrent::TorrentState::StalledDownloading: + return "StalledDownloading"; + case BitTorrent::TorrentState::CheckingDownloading: + return "CheckingDownloading"; + case BitTorrent::TorrentState::ForcedDownloading: + return "ForcedDownloading"; +#if LIBTORRENT_VERSION_NUM < 10100 + case BitTorrent::TorrentState::QueuedForChecking: + return "QueuedForChecking"; +#endif + case BitTorrent::TorrentState::CheckingResumeData: + return "CheckingResumeData"; + default: + throw std::logic_error("Unexpected TorrentState value"); + } +} + +QByteArray Theme::SerializableColorTheme::colorSaveName(Log::MsgType msgType) +{ + // TODO Qt 5.8+: use Q_NAMESPACE + Q_ENUM_NS + // static QMetaEnum meta = QMetaEnum::fromType(); + // return QString::fromLatin1(meta.key(static_cast(msgType))); + switch (msgType) { + case Log::MsgType::ALL: + return "ALL"; + case Log::MsgType::NORMAL: + return "NORMAL"; + case Log::MsgType::INFO: + return "INFO"; + case Log::MsgType::WARNING: + return "WARNING"; + case Log::MsgType::CRITICAL: + return "CRITICAL"; + default: + throw std::logic_error("Unexpected Log::MsgType value"); + } +} + +QByteArray Theme::SerializableColorTheme::colorSaveName(DownloadProgressBarElement element) +{ + // TODO Qt 5.8+: use Q_NAMESPACE + Q_ENUM_NS + // static QMetaEnum meta = QMetaEnum::fromType(); + // return QString::fromLatin1(meta.key(static_cast(element))); + switch (element) { + case DownloadProgressBarElement::Background: + return "Background"; + case DownloadProgressBarElement::Border: + return "Border"; + case DownloadProgressBarElement::Complete: + return "Complete"; + case DownloadProgressBarElement::Incomplete: + return "Incomplete"; + default: + throw std::logic_error("Unexpected DownloadProgressBarElement value"); + } +} + +QColor Theme::SerializableColorTheme::downloadProgressBarColor(const DownloadProgressBarElement elem) const +{ + return element(toThemeElement(elem)); +} + +QColor Theme::SerializableColorTheme::logMessageColor(const Log::MsgType messageType) const +{ + return element(toThemeElement(messageType)); +} + +QColor Theme::SerializableColorTheme::torrentStateColor(const BitTorrent::TorrentState state) const +{ + return element(toThemeElement(state)); +} + diff --git a/src/gui/theme/serializablecolortheme.h b/src/gui/theme/serializablecolortheme.h new file mode 100644 index 0000000000..6bc307fe1e --- /dev/null +++ b/src/gui/theme/serializablecolortheme.h @@ -0,0 +1,99 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_SERIALIZABLECOLORTHEME_H +#define QBT_THEME_SERIALIZABLECOLORTHEME_H + +#include "colorprovider_p.h" +#include "colortheme.h" +#include "serializabletheme.h" + +namespace Theme +{ + enum class ColorThemeElement + { + TorrentStateUnknown, + TorrentStateForcedDownloading, + TorrentStateDownloading, + TorrentStateDownloadingMetadata, + TorrentStateAllocating, + TorrentStateStalledDownloading, + TorrentStateForcedUploading, + TorrentStateUploading, + TorrentStateStalledUploading, +#if LIBTORRENT_VERSION_NUM < 10100 + TorrentStateQueuedForChecking, +#endif + TorrentStateCheckingResumeData, + TorrentStateQueuedDownloading, + TorrentStateQueuedUploading, + TorrentStateCheckingUploading, + TorrentStateCheckingDownloading, + TorrentStatePausedDownloading, + TorrentStatePausedUploading, + TorrentStateMissingFiles, + TorrentStateError, + + MsgTypeALL, + MsgTypeNORMAL, + MsgTypeINFO, + MsgTypeWARNING, + MsgTypeCRITICAL, + + DownloadProgressBarElementBackground, + DownloadProgressBarElementBorder, + DownloadProgressBarElementComplete, + DownloadProgressBarElementIncomplete + }; + + class SerializableColorTheme: public ColorTheme, + protected SerializableTheme + { + public: + using BaseSerializableTheme = SerializableTheme; + SerializableColorTheme(const QString &name); + + using BaseSerializableTheme::save; + + ThemeInfo info() const override; + QColor torrentStateColor(const BitTorrent::TorrentState state) const override; + QColor logMessageColor(const Log::MsgType messageType) const override; + QColor downloadProgressBarColor(const DownloadProgressBarElement element) const override; + + void applicationPaletteChanged(); + + private: + static QByteArray colorSaveName(BitTorrent::TorrentState state); + static QByteArray colorSaveName(Log::MsgType msgType); + static QByteArray colorSaveName(DownloadProgressBarElement element); + + static BaseSerializableTheme::NamesMap elementNames(); + }; +} + +#endif // QBT_THEME_SERIALIZABLECOLORTHEME_H diff --git a/src/gui/theme/serializablefonttheme.cpp b/src/gui/theme/serializablefonttheme.cpp new file mode 100644 index 0000000000..986c05ec79 --- /dev/null +++ b/src/gui/theme/serializablefonttheme.cpp @@ -0,0 +1,57 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "serializablefonttheme.h" + +#include + +#include "themeexceptions.h" + +namespace +{ + const QByteArray fontsGroupName("Fonts/"); +} + +Theme::SerializableFontTheme::SerializableFontTheme(const QString& themeName) + : BaseSerializableTheme(themeName, elementNames()) +{ +} + +const Theme::ThemeInfo &Theme::SerializableFontTheme::info() const +{ + return BaseSerializableTheme::info(); +} + +Theme::SerializableFontTheme::BaseSerializableTheme::NamesMap Theme::SerializableFontTheme::elementNames() +{ + return { + {fontsGroupName + QByteArray("TransferList"), static_cast(FontThemeElement::TransferList)}, + {fontsGroupName + QByteArray("TorrentProperties"), static_cast(FontThemeElement::TorrentProperties)}, + {fontsGroupName + QByteArray("ExecutionLog"), static_cast(FontThemeElement::ExecutionLog)}, + }; +} diff --git a/src/gui/theme/serializablefonttheme.h b/src/gui/theme/serializablefonttheme.h new file mode 100644 index 0000000000..dbb17e9318 --- /dev/null +++ b/src/gui/theme/serializablefonttheme.h @@ -0,0 +1,58 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_SERIALIZABLEFONTTHEME_H +#define QBT_THEME_SERIALIZABLEFONTTHEME_H + +#include "fontprovider_p.h" +#include "fonttheme.h" +#include "serializabletheme.h" +#include "themeinfo.h" + +namespace Theme +{ + class SerializableFontTheme : public FontTheme, + protected SerializableTheme + { + public: + using BaseSerializableTheme = SerializableTheme; + SerializableFontTheme(const QString &themeName); + + using BaseSerializableTheme::save; + + const QFont &font(FontThemeElement e) const override { + return element(e); + } + + const Theme::ThemeInfo & info() const override; + private: + static BaseSerializableTheme::NamesMap elementNames(); + }; +} + +#endif // QBT_THEME_SERIALIZABLEFONTTHEME_H diff --git a/src/gui/theme/serializabletheme.cpp b/src/gui/theme/serializabletheme.cpp new file mode 100644 index 0000000000..7299a8094f --- /dev/null +++ b/src/gui/theme/serializabletheme.cpp @@ -0,0 +1,138 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "serializabletheme.h" + +#include + +#include + +#include "themeinfo.h" +#include "themeprovider.h" + +namespace +{ + void appendAbsentElements(Theme::ElementsMap &res, QSettings &settings) + { + // we ignore all keys in [Info] group + const QString infoPrefix = Theme::ThemeInfo::sectionName + QLatin1Char('/'); + const auto keys = settings.allKeys(); + for (const auto& key: keys) { + if (!key.startsWith(infoPrefix) && // ignore all keys in [Info] group + res.find(key) == res.end()) { + QVariant v = settings.value(key); + // if there were commas in the value, QVariant contains string list + // and we have to join it back + res[key] = v.type() == QVariant::StringList + ? v.toStringList().join(QLatin1Char(',')) + : v.toString(); + } + } + } + + struct LoadedTheme + { + Theme::ThemeInfo info; + Theme::ElementsMap elements; + }; + + /** + * @brief Loads serialized themes + * + * This function handles themes inheritance and takes care of appending default theme if needed + */ + LoadedTheme collectThemeElements(Theme::Kind kind, const QString& name, const QString& defaultInherited) + { + // we are going to load themes from inheritance hierarchy from descendant + // to ancestor, appending ancestor element to the dictionary if corresponding keys + // are not already present in it. + bool firstThemeWasLoaded = false; // we need info object from the first theme only + LoadedTheme res; + + QString themeName = name; + std::set loadedThemeNames; // this will be used to protect us against infinite + // inheritance loops + + Q_ASSERT(!themeName.isEmpty()); + do { + const QString themeFileName = Theme::ThemeProvider::instance().locateTheme(kind, themeName); + QSettings settings(themeFileName, QSettings::IniFormat); + Theme::ThemeInfo info = Theme::ThemeInfo(settings); + if (!firstThemeWasLoaded) { + res.info = info; + firstThemeWasLoaded = true; + } + appendAbsentElements(res.elements, settings); + + loadedThemeNames.insert(info.name()); + + themeName = info.inheritedTheme(); + if (themeName.isEmpty()) { + themeName = defaultInherited; + } + } while (loadedThemeNames.count(themeName) == 0); + + return res; + } +} + +Theme::ThemeSerializer::ThemeSerializer(Kind kind, const QString &themeName, + const NamesMap& names, ElementDeserializer deserializer) + : m_names {names} + , m_info {} + , m_kind {kind} +{ + const QString defaultThemeName = ThemeProvider::instance().defaultThemeName(m_kind); + QString nameToUse = themeName.isEmpty() ? defaultThemeName : themeName; + const LoadedTheme loaded = collectThemeElements(m_kind, nameToUse, defaultThemeName); + m_info = std::move(loaded.info); + for (const auto &p : loaded.elements) { + const auto it = m_names.find(p.first.toLatin1()); + if (it == m_names.end()) { + qCWarning(theme) << "Unexpected theme element '" << p.first << '\''; + continue; + } + deserializer(it->second, p.second); + } +} + +void Theme::ThemeSerializer::save(const QString& themeFileName, + bool explicitValues, ElementSerializer serializer) const +{ + QSettings settings(themeFileName, QSettings::IniFormat); + + m_info.save(settings); + for (const auto &p : m_names) { + settings.setValue(p.first, serializer(p.second, explicitValues)); + } +} + +const Theme::ThemeInfo &Theme::ThemeSerializer::info() const +{ + return m_info; +} diff --git a/src/gui/theme/serializabletheme.h b/src/gui/theme/serializabletheme.h new file mode 100644 index 0000000000..a357e0ac60 --- /dev/null +++ b/src/gui/theme/serializabletheme.h @@ -0,0 +1,156 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_SERIALIZABLE_THEME_H +#define QBT_THEME_SERIALIZABLE_THEME_H + +#include "themecommon.h" +#include "themeinfo.h" + +#include +#include + +#include +#include +#include + +namespace Theme +{ + using ElementsMap = std::map; + + class ThemeSerializer + { + public: + using NamesMap = std::map; + using ElementDeserializer = std::function; + using ElementSerializer = std::function; + + ThemeSerializer(Kind kind, const QString &themeName, + const NamesMap& names, ElementDeserializer deserializer); + + void save(const QString &themeFileName, bool explicitValues, ElementSerializer serializer) const; + + const ThemeInfo &info() const; + private: + + NamesMap m_names; + ThemeInfo m_info; + Kind m_kind; + }; + + template + class SerializableTheme + { + static_assert(std::is_same::type>::value, + "Element underlying type has to be int"); + + public: + using EntityProviderType = typename ProviderRegistry::ProviderType; + using EntityType = typename EntityProviderType::EntityType; + using ThemeObjectType = typename EntityType::ValueType; + using ElementType = Element; + using NamesMap = typename ThemeSerializer::NamesMap; + + using ThisType = SerializableTheme; + + const ThemeObjectType &element(Element e) const; + const ThemeInfo &info() const; + + protected: + SerializableTheme(const QString &themeName, const NamesMap& names); + void updateCache() const; + + void save(const QString &themeFileName, bool explicitValues) const; + + private: + QString serialize(int elementId, bool explicitValue) const; + void deserialize(int elementId, const QString &value); + + using ElementsMap = std::map; + using ObjectsMap = std::map; + + ElementsMap m_elements; + ThemeSerializer m_serializer; + mutable ObjectsMap m_cache; + }; +} + +template +const typename Theme::SerializableTheme::ThemeObjectType +&Theme::SerializableTheme::element(Element e) const +{ + return m_cache[e]; +} + +template +const Theme::ThemeInfo & Theme::SerializableTheme::info() const +{ + return m_serializer.info(); +} + +template +Theme::SerializableTheme::SerializableTheme( + const QString& themeName, const NamesMap& names) + : m_elements{} + , m_serializer(ProviderRegistry::kind, themeName, names, + std::bind(&SerializableTheme::deserialize, this, std::placeholders::_1, std::placeholders::_2)) +{ + updateCache(); +} + +template +void Theme::SerializableTheme::save(const QString& themeFileName, bool explicitValues) const +{ + m_serializer.save(themeFileName, explicitValues, + std::bind(&SerializableTheme::serialize, this, std::placeholders::_1, std::placeholders::_2)); +} + +template +QString Theme::SerializableTheme::serialize(int elementId, bool explicitValue) const +{ + const auto &element = m_elements.at(static_cast(elementId)); + return element->serializationKey() + QLatin1Char(':') + + (explicitValue ? element->explicitSerializedValue() : element->serializedValue()); +} + +template +void Theme::SerializableTheme::deserialize(int elementId, const QString &value) +{ + m_elements[static_cast(elementId)] = ProviderRegistry::instance().load(value); +} + +template +void Theme::SerializableTheme::updateCache() const +{ + for (const auto &p : m_elements) { + m_cache[p.first] = p.second->value(); + } +} + + +#endif // QBT_THEME_SERIALIZABLE_THEME_H diff --git a/src/gui/theme/themecommon.cpp b/src/gui/theme/themecommon.cpp new file mode 100644 index 0000000000..75827797c0 --- /dev/null +++ b/src/gui/theme/themecommon.cpp @@ -0,0 +1,39 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "themecommon.h" + +#include +#include + +bool Theme::isDarkTheme() +{ + return (QPalette().color(QPalette::Active, QPalette::Base).lightness() < 127); +} + +Q_LOGGING_CATEGORY(theme, "qbt.theme") diff --git a/src/gui/theme/themecommon.h b/src/gui/theme/themecommon.h new file mode 100644 index 0000000000..3b0c19cfd2 --- /dev/null +++ b/src/gui/theme/themecommon.h @@ -0,0 +1,50 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_THEMECOMMON_H +#define QBT_THEME_THEMECOMMON_H + +#include + +Q_DECLARE_LOGGING_CATEGORY(theme) + +class QColor; +class QTemporaryFile; + +namespace Theme +{ + enum class Kind + { + Color, + Font + }; + + bool isDarkTheme(); +} + +#endif // QBT_THEME_THEMECOMMON_H diff --git a/src/gui/theme/themeexceptions.cpp b/src/gui/theme/themeexceptions.cpp new file mode 100644 index 0000000000..d6710a17b7 --- /dev/null +++ b/src/gui/theme/themeexceptions.cpp @@ -0,0 +1,87 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "themeexceptions.h" + +Theme::ThemeNotFound::ThemeNotFound(const QString& themeName) + : ThemeError("Could not find theme named '" + themeName.toStdString() + "'") +{ +} + +Theme::Serialization::ThemeElementMissing::ThemeElementMissing(const QString& elementName) + : DeserializationError("Theme misses element '" + elementName.toStdString() + '\'') + , m_elementName(elementName) +{ +} + +const QString &Theme::Serialization::ThemeElementMissing::elementName() const +{ + return m_elementName; +} + +Theme::Serialization::ElementDeserializationError::ElementDeserializationError( + const std::string& message, const QString& serializedValue) + : DeserializationError("Could not deserialize theme object '" + serializedValue.toStdString() + "': " + message) + , m_serializedValue(serializedValue) +{ +} + +const QString &Theme::Serialization::ElementDeserializationError::serializedValue() const +{ + return m_serializedValue; +} + +Theme::Serialization::ParsingError::ParsingError(const QString& serializedValue) + : ElementDeserializationError("Could not find scheme part", serializedValue) +{ +} + +// we know that serializedValue follows scheme:value pattern, otherwise this exception is not applicable +Theme::Serialization::UnknownProvider::UnknownProvider(const QString& serializedValue) + : ElementDeserializationError("Could not find provider for scheme '" + + serializedValue.section(QLatin1Char(':'), 0, 1).toStdString() + '\'', serializedValue) +{ +} + +QString Theme::Serialization::UnknownProvider::profiderName() const +{ + // we know that serializedValue follows scheme:value pattern, otherwise this exception is not applicable + return this->serializedValue().section(QLatin1Char(':'), 0, 1); +} + +Theme::Serialization::ValueParsingError::ValueParsingError(const std::string& message, const QString& serializedValue) + : ElementDeserializationError("Could not parse element value: " + message, serializedValue) +{ +} + +QString Theme::Serialization::ValueParsingError::valueString() const +{ + // we know that serializedValue follows scheme:value pattern, otherwise this exception is not applicable + return this->serializedValue().section(QLatin1Char(':'), 1); +} + diff --git a/src/gui/theme/themeexceptions.h b/src/gui/theme/themeexceptions.h new file mode 100644 index 0000000000..b37051c6e2 --- /dev/null +++ b/src/gui/theme/themeexceptions.h @@ -0,0 +1,101 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEME_THEMEEXCEPTIONS_H +#define QBT_THEME_THEMEEXCEPTIONS_H + +#include + +#include + +namespace Theme +{ + + class ThemeError: public std::runtime_error + { + public: + using std::runtime_error::runtime_error; + }; + + class ThemeNotFound: public ThemeError + { + public: + ThemeNotFound(const QString &themeName); + }; + + namespace Serialization + { + class DeserializationError: public ThemeError + { + public: + using ThemeError::ThemeError; + }; + + class ThemeElementMissing: public DeserializationError + { + public: + ThemeElementMissing(const QString &elementName); + + const QString &elementName() const; + private: + QString m_elementName; + }; + + class ElementDeserializationError: public DeserializationError + { + public: + ElementDeserializationError(const std::string &message, const QString &serializedValue); + const QString &serializedValue() const; + + private: + QString m_serializedValue; + }; + + class ParsingError: public ElementDeserializationError + { + public: + ParsingError(const QString &serializedValue); + }; + + class UnknownProvider: public ElementDeserializationError + { + public: + UnknownProvider(const QString &serializedValue); + QString profiderName() const; + }; + + class ValueParsingError: public ElementDeserializationError + { + public: + ValueParsingError(const std::string &message, const QString &serializedValue); + QString valueString() const; + }; + } +} + +#endif // QBT_THEME_THEMEEXCEPTIONS_H diff --git a/src/gui/theme/themeinfo.cpp b/src/gui/theme/themeinfo.cpp new file mode 100644 index 0000000000..3fd2438787 --- /dev/null +++ b/src/gui/theme/themeinfo.cpp @@ -0,0 +1,156 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "themeinfo.h" + +#include +#include +#include + +namespace +{ +#define GROUP_NAME "Info" +#define FULL_SETTING_KEY(name) GROUP_NAME "/" name + const QLatin1String nameFullKey(FULL_SETTING_KEY("Name")); + const QLatin1String descriptionFullKey(FULL_SETTING_KEY("Description")); + const QLatin1String inheritsFullKey(FULL_SETTING_KEY("Inherits")); + + const QLatin1String nameKey = QLatin1String("Name"); + const QLatin1String descriptionKey = QLatin1String("Description"); +} + +const QString Theme::ThemeInfo::sectionName = QLatin1String(GROUP_NAME); + +Theme::ThemeInfo::ThemeInfo() = default; + +Theme::ThemeInfo::ThemeInfo(QSettings& settings) + : m_name {settings.value(nameFullKey, QLatin1Literal("Unnamed")).toString()} + , m_description {settings.value(descriptionFullKey, QString()).toString()} + , m_inheritedTheme {settings.value(inheritsFullKey, QString()).toString()} +{ + settings.beginGroup(QLatin1String(GROUP_NAME)); + m_localizedNames = readAffixedKeys(settings, QString(nameKey) + QLatin1Char('_')); + m_localizedDescriptions = readAffixedKeys(settings, QString(descriptionKey) + QLatin1Char('_')); + settings.endGroup(); +} + +void Theme::ThemeInfo::save(QSettings& settings) const +{ + settings.setValue(nameFullKey, m_name); + settings.setValue(descriptionFullKey, m_description); + settings.setValue(inheritsFullKey, m_inheritedTheme); + + settings.beginGroup(QLatin1String(GROUP_NAME)); + writeAffixedKeys(m_localizedNames, settings, QString(nameKey) + QLatin1Char('_')); + writeAffixedKeys(m_localizedDescriptions, settings, QString(descriptionKey) + QLatin1Char('_')); + settings.endGroup(); +} + +Theme::ThemeInfo::LocalizedStrings +Theme::ThemeInfo::readAffixedKeys(QSettings &settings, const QString &name) +{ + LocalizedStrings res; + const QStringList keys = settings.allKeys(); + for (const QString &key: keys) { + if (key.startsWith(name)) { + res[key.mid(name.size())] = settings.value(key).toString(); + } + } + return res; +} + +void Theme::ThemeInfo::writeAffixedKeys(const LocalizedStrings& values, QSettings& settings, const QString& name) +{ + for (const auto &pair: values) { + settings.setValue(name + pair.first, pair.second); + } +} + +QString Theme::ThemeInfo::name() const +{ + return m_name; +} + +QString Theme::ThemeInfo::description() const +{ + return m_description; +} + +QString Theme::ThemeInfo::inheritedTheme() const +{ + return m_inheritedTheme; +} + +void Theme::ThemeInfo::setName(const QString& name) +{ + m_name = name; +} + +void Theme::ThemeInfo::setDescription(const QString& description) +{ + m_description = description; +} + +void Theme::ThemeInfo::setInheritedTheme(const QString& name) +{ + m_inheritedTheme = name; +} + +QString Theme::ThemeInfo::findLocalizedString(const LocalizedStrings& strings) +{ + static QLocale loc; + auto i = strings.find(loc.name()); + if (i != strings.end()) { + return i->second; + } + + i = strings.find(QLocale::languageToString(loc.language())); + if (i != strings.end()) { + return i->second; + } + + return {}; +} + +QString Theme::ThemeInfo::localizedName() const +{ + const QString localized = findLocalizedString(m_localizedNames); + return localized.isEmpty() ? m_name : localized; +} + +QString Theme::ThemeInfo::localizedDescription() const +{ + const QString localized = findLocalizedString(m_localizedDescriptions); + return localized.isEmpty() ? m_description : localized; +} + +QDebug Theme::operator<<(QDebug debug, const Theme::ThemeInfo &info) +{ + debug << info.name() << " [" << info.description() << ']'; + return debug; +} diff --git a/src/gui/theme/themeinfo.h b/src/gui/theme/themeinfo.h new file mode 100644 index 0000000000..747606a642 --- /dev/null +++ b/src/gui/theme/themeinfo.h @@ -0,0 +1,112 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEMES_THEMEINFO_H +#define QBT_THEMES_THEMEINFO_H + +#include + +#include + +class QDebug; +class QSettings; + +namespace Theme +{ + /** + * @brief Utility class to encapsulate general theme attributes + * + * Contains name and description of the theme + * + * These data reside in section [Info] of the theme file: + * + * [Info] + * Name=Theme name + * Description=Description of the theme + * Inherits=Other theme name + * Name_de=Name in German + * Name_gr=Name in Greek + * Description_fr=Description in French + * Description_uk=Description in Ukrainian + * + */ + class ThemeInfo + { + public: + ThemeInfo(); + ThemeInfo(QSettings &settings); + + void save(QSettings &settings) const; + + QString name() const; + QString localizedName() const; + QString description() const; + QString localizedDescription() const; + QString inheritedTheme() const; + + void setName(const QString &name); + void setDescription(const QString &description); + void setInheritedTheme(const QString &name); + + static const QString sectionName; + + private: + QString m_name; + QString m_description; + QString m_inheritedTheme; + + using LocalizedStrings = std::map; + /** + * @brief Function reads key values whose names start with "${name}" + * @returns dictionary of affix -> value + */ + static LocalizedStrings readAffixedKeys(QSettings &settings, const QString &name); + static void writeAffixedKeys(const LocalizedStrings& values, QSettings &settings, const QString &name); + + static QString findLocalizedString(const LocalizedStrings &strings); + + LocalizedStrings m_localizedNames; + LocalizedStrings m_localizedDescriptions; + }; + + QDebug operator<<(QDebug debug, const ThemeInfo &info); + + // comparison operators: there may not be two themes with equal names of the same kind in the app + + inline bool operator < (const ThemeInfo &left, const ThemeInfo &right) + { + return left.name() < right.name(); + } + + inline bool operator == (const ThemeInfo &left, const ThemeInfo &right) + { + return left.name() == right.name(); + } +} + +#endif diff --git a/src/gui/theme/themeprovider.cpp b/src/gui/theme/themeprovider.cpp new file mode 100644 index 0000000000..59a85cca64 --- /dev/null +++ b/src/gui/theme/themeprovider.cpp @@ -0,0 +1,266 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "themeprovider.h" + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include "base/logger.h" +#include "base/profile.h" +#include "base/settingsstorage.h" +#include "colorprovider_p.h" +#include "serializablecolortheme.h" +#include "serializablefonttheme.h" +#include "themecommon.h" +#include "themeexceptions.h" +#include "themeinfo.h" + +const QLatin1String Theme::ThemeProvider::colorThemeFileExtension(".colortheme"); +const QLatin1String Theme::ThemeProvider::fontThemeFileExtension(".fonttheme"); + +namespace +{ + class ThemeProviderInstantiable: public Theme::ThemeProvider + { + public: + using ThemeProvider::ThemeProvider; + }; + +#define SETTINGS_KEY(name) "Appearance/" name + const QLatin1String selectedColorThemeKey (SETTINGS_KEY("ColorTheme")); + const QLatin1String selectedFontThemeKey (SETTINGS_KEY("FontTheme")); + + const QLatin1String defaultLightColorThemeName ("Default (Light)"); + const QLatin1String defaultDarkColorThemeName ("Default (Dark)"); + const QLatin1String defaultFontThemeName("Default"); +} + +Q_GLOBAL_STATIC(ThemeProviderInstantiable, themeProvider) + +Theme::ThemeProvider & Theme::ThemeProvider::instance() +{ + return *themeProvider(); +} + +Theme::ThemeProvider::ThemeProvider() +{ + connect(qGuiApp, &QGuiApplication::paletteChanged, this, &ThemeProvider::applicationPaletteChanged); +} + +Theme::ThemeProvider::~ThemeProvider() = default; + +void Theme::ThemeProvider::loadConfiguration() +{ + const QString colorThemeName = SettingsStorage::instance()->loadValue(selectedColorThemeKey, QString()).toString(); + try { + setCurrentTheme(Kind::Color, colorThemeName); + } catch (Serialization::DeserializationError &ex) { + qCWarning(theme, "Color theme '%s' loading failed (%s), loading default theme instead", + qPrintable(colorThemeName), ex.what()); + Logger::instance()->addMessage(tr("Could not load color theme '%1'. Loading default theme.") + .arg(colorThemeName)); + setCurrentTheme(Kind::Color, QString()); + } + + const QString fontThemeName = SettingsStorage::instance()->loadValue(selectedFontThemeKey, QString()).toString(); + try { + setCurrentTheme(Kind::Font, fontThemeName); + } catch (Serialization::DeserializationError &ex) { + qCWarning(theme, "Font theme '%s' loading failed (%s), loading default theme instead", + qPrintable(fontThemeName), ex.what()); + Logger::instance()->addMessage(tr("Could not load font theme '%1'. Loading default theme.") + .arg(fontThemeName)); + setCurrentTheme(Kind::Font, QString()); + } +} + +std::map Theme::ThemeProvider::availableThemes(Kind kind) const +{ + QDir themeDir; + switch (kind) { + case Kind::Color: + themeDir.setNameFilters({QStringLiteral("*") + colorThemeFileExtension}); + break; + case Kind::Font: + themeDir.setNameFilters({QStringLiteral("*") + fontThemeFileExtension}); + break; + } + QStringList themeSearchPaths = this->themeSearchPaths(); + std::map res; + std::set loadedThemeNames; + for (const QString &path: themeSearchPaths) { + themeDir.cd(path); + QStringList themeFiles = themeDir.entryList(); + for (const QString &themeFile: themeFiles) { + const QString absThemePath = themeDir.absoluteFilePath(themeFile); + QSettings theme(absThemePath, QSettings::IniFormat); + ThemeInfo info(theme); + if (!loadedThemeNames.count(info.name())) { + res[info] = absThemePath; + loadedThemeNames.insert(info.name()); + } + } + } + + return res; +} + +QString Theme::ThemeProvider::defaultThemeName(Theme::Kind kind) const +{ + switch (kind) { + case Kind::Color: + return isDarkTheme() ? defaultDarkColorThemeName : defaultLightColorThemeName; + case Kind::Font: + return defaultFontThemeName; + default: + throw std::logic_error("Unexpected theme kind"); + } +} + + +QString Theme::ThemeProvider::locateTheme(Kind kind, const QString& themeName) const +{ + const auto themes = availableThemes(kind); + ThemeInfo fake; + fake.setName(themeName); + auto i = themes.find(fake); + if (i != themes.end()) + return i->second; + + throw ThemeNotFound(themeName); +} + +void Theme::ThemeProvider::exportTheme(Theme::Kind kind, const QString &themeName, + const QString &fileName, ExportOptios options) +{ + switch (kind) { + case Kind::Color: + SerializableColorTheme(themeName).save(fileName, options.testFlag(ExportOption::WriteExplicitValues)); + break; + case Kind::Font: + SerializableFontTheme(themeName).save(fileName, options.testFlag(ExportOption::WriteExplicitValues)); + break; + } +} + +QStringList Theme::ThemeProvider::themeSearchPaths() const +{ + const QString themeDirName = QStringLiteral("theme"); + QStringList res; + // always support resources, they are first in the list for two reasons: + // 1) themes from this dir serve as fallback + // 2) user may not override them because of 1) + res << QLatin1String(":/") + themeDirName; + res << QDir(Profile::instance().location(SpecialFolder::Data)).absoluteFilePath(themeDirName); + // WARNING QStandardPaths::locateAll() returns a list with entries from home directory at the + // beginning, but this is not documented and might change. In that case this method will be incorrect. + res << QStandardPaths::locateAll(QStandardPaths::AppLocalDataLocation, themeDirName, QStandardPaths::LocateDirectory); +#if defined Q_OS_UNIX && !defined Q_OS_MACOS + QDir appDir = QCoreApplication::applicationDirPath(); + appDir.cdUp(); + const QString installPrefix = appDir.canonicalPath(); + const QString resourceDirInInstallPrefix = installPrefix + QLatin1String("/share/") + + QCoreApplication::applicationName() + QDir::separator() + themeDirName; + if (!res.contains(resourceDirInInstallPrefix)) + res << resourceDirInInstallPrefix; +#endif + return res; +} + +const Theme::ColorTheme &Theme::ThemeProvider::colorTheme() const +{ + return *m_currentColorTheme; +} + +const Theme::FontTheme & Theme::ThemeProvider::fontTheme() const +{ + return *m_currentFontTheme; +} + +void Theme::ThemeProvider::setCurrentTheme(Kind kind, const QString& themeName) +{ + switch (kind) { + case Kind::Color: + qCInfo(theme, "Loading color theme %s", qPrintable(themeName)); + try { + decltype(m_currentColorTheme) newTheme(new SerializableColorTheme(themeName)); + std::swap(m_currentColorTheme, newTheme); + emit colorThemeChanged(); + SettingsStorage::instance()->storeValue(selectedColorThemeKey, themeName); + } + catch (Serialization::DeserializationError &er) { + qCInfo(theme) << "Could not load color theme '" << themeName << "': " << er.what(); + throw; + } + break; + case Kind::Font: + qCInfo(theme, "Loading font theme %s", qPrintable(themeName)); + try { + decltype(m_currentFontTheme) newTheme(new SerializableFontTheme(themeName)); + std::swap(m_currentFontTheme, newTheme); + emit fontThemeChanged(); + SettingsStorage::instance()->storeValue(selectedFontThemeKey, themeName); + } + catch (Serialization::DeserializationError &er) { + qCInfo(theme) << "Could not load font theme '" << themeName << "': " << er.what(); + throw; + } + break; + } +} + +Theme::ThemeInfo Theme::ThemeProvider::currentTheme(Theme::Kind kind) const +{ + switch (kind) { + case Kind::Color: + return colorTheme().info(); + case Kind::Font: + return fontTheme().info(); + } + throw std::logic_error("Unexpected theme kind"); +} + +void Theme::ThemeProvider::applicationPaletteChanged(const QPalette&) +{ + Serialization::ColorsProviderRegistry::instance().applicationPaletteChanged(); + m_currentColorTheme->applicationPaletteChanged(); + emit colorThemeChanged(); +} + +void Theme::ThemeProvider::initInstance() +{ + instance().loadConfiguration(); +} diff --git a/src/gui/theme/themeprovider.h b/src/gui/theme/themeprovider.h new file mode 100644 index 0000000000..d2c87c9fbf --- /dev/null +++ b/src/gui/theme/themeprovider.h @@ -0,0 +1,123 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2017 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_THEMEPROVIDER_H +#define QBT_THEMEPROVIDER_H + +#include +#include + +#include + +#include "themecommon.h" + +class QPalette; +class QStringList; +class Application; + +namespace Theme +{ + class ColorTheme; + class FontTheme; + class SerializableColorTheme; + class SerializableFontTheme; + class ThemeInfo; + + class ThemeProvider: public QObject + { + Q_OBJECT + Q_DISABLE_COPY(ThemeProvider) + + public: + static ThemeProvider &instance(); + + /** + * @brief Collection of available color themes + * + * @returns map theme info object -> file path + */ + std::map availableThemes(Kind kind) const; + + QString defaultThemeName(Kind kind) const; + + const ColorTheme &colorTheme() const; + const FontTheme &fontTheme() const; + + void setCurrentTheme(Kind kind, const QString &themeName); + ThemeInfo currentTheme(Kind kind) const; + + /** + * @brief Locate file with given named theme + * + * @param themeName Name of the theme to search for + * @return File path + */ + QString locateTheme(Kind kind, const QString &themeName) const; + + static const QLatin1String colorThemeFileExtension; + static const QLatin1String fontThemeFileExtension; + + enum class ExportOption + { + NoOptions = 0x0, + WriteExplicitValues = 0x1 //!< write explicit values, i.e. RGB instead of palette members + }; + + Q_DECLARE_FLAGS(ExportOptios, ExportOption) + + void exportTheme(Kind kind, const QString &themeName, const QString &fileName, ExportOptios options = ExportOption::NoOptions); + + signals: + void colorThemeChanged(); + void fontThemeChanged(); + + protected: + ThemeProvider(); + ~ThemeProvider(); + + private slots: + void applicationPaletteChanged(const QPalette&); + + private: + friend class ::Application; + static void initInstance(); + void loadConfiguration(); + /** + * @brief Theme search paths arranged by priority + * + * @return QStringList with paths. The first one is of highest priority. + */ + QStringList themeSearchPaths() const; + + + std::unique_ptr m_currentColorTheme; + std::unique_ptr m_currentFontTheme; + }; +} + +#endif // QBT_THEMEPROVIDER_H diff --git a/src/gui/torrentmodel.cpp b/src/gui/torrentmodel.cpp index 4feef0897b..42234e5ea6 100644 --- a/src/gui/torrentmodel.cpp +++ b/src/gui/torrentmodel.cpp @@ -29,31 +29,22 @@ * Contact : chris@qbittorrent.org */ -#include +#include "torrentmodel.h" + #include -#include +#include +#include #include +#include +#include #include "base/bittorrent/session.h" #include "base/bittorrent/torrenthandle.h" +#include "base/settingvalue.h" #include "base/torrentfilter.h" #include "base/utils/fs.h" -#include "torrentmodel.h" - -static QIcon getIconByState(BitTorrent::TorrentState state); -static QColor getColorByState(BitTorrent::TorrentState state); - -static QIcon getPausedIcon(); -static QIcon getQueuedIcon(); -static QIcon getDownloadingIcon(); -static QIcon getStalledDownloadingIcon(); -static QIcon getUploadingIcon(); -static QIcon getStalledUploadingIcon(); -static QIcon getCompletedIcon(); -static QIcon getCheckingIcon(); -static QIcon getErrorIcon(); - -static bool isDarkTheme(); +#include "guiiconprovider.h" +#include "theme/colortheme.h" // TorrentModel @@ -166,10 +157,12 @@ QVariant TorrentModel::data(const QModelIndex &index, int role) const if (!torrent) return QVariant(); if ((role == Qt::DecorationRole) && (index.column() == TR_NAME)) - return getIconByState(torrent->state()); + return GuiIconProvider::instance()->icon(torrent->state()); if (role == Qt::ForegroundRole) - return getColorByState(torrent->state()); + return textIsColorized() + ? Theme::ColorTheme::current().torrentStateColor(torrent->state()) + : QGuiApplication::palette().color(QPalette::WindowText); if ((role != Qt::DisplayRole) && (role != Qt::UserRole)) return QVariant(); @@ -296,6 +289,16 @@ BitTorrent::TorrentHandle *TorrentModel::torrentHandle(const QModelIndex &index) return m_torrents.value(index.row()); } +bool TorrentModel::textIsColorized() +{ + return textIsColorizedSetting(); +} + +void TorrentModel::setTextIsColorized(bool v) +{ + textIsColorizedSetting() = v; +} + void TorrentModel::handleTorrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent) { const int row = m_torrents.indexOf(torrent); @@ -318,161 +321,8 @@ void TorrentModel::handleTorrentsUpdated() emit dataChanged(index(0, 0), index(rowCount() - 1, columnCount() - 1)); } -// Static functions - -QIcon getIconByState(BitTorrent::TorrentState state) -{ - switch (state) { - case BitTorrent::TorrentState::Downloading: - case BitTorrent::TorrentState::ForcedDownloading: - case BitTorrent::TorrentState::DownloadingMetadata: - return getDownloadingIcon(); - case BitTorrent::TorrentState::Allocating: - case BitTorrent::TorrentState::StalledDownloading: - return getStalledDownloadingIcon(); - case BitTorrent::TorrentState::StalledUploading: - return getStalledUploadingIcon(); - case BitTorrent::TorrentState::Uploading: - case BitTorrent::TorrentState::ForcedUploading: - return getUploadingIcon(); - case BitTorrent::TorrentState::PausedDownloading: - return getPausedIcon(); - case BitTorrent::TorrentState::PausedUploading: - return getCompletedIcon(); - case BitTorrent::TorrentState::QueuedDownloading: - case BitTorrent::TorrentState::QueuedUploading: - return getQueuedIcon(); - case BitTorrent::TorrentState::CheckingDownloading: - case BitTorrent::TorrentState::CheckingUploading: -#if LIBTORRENT_VERSION_NUM < 10100 - case BitTorrent::TorrentState::QueuedForChecking: -#endif - case BitTorrent::TorrentState::CheckingResumeData: - return getCheckingIcon(); - case BitTorrent::TorrentState::Unknown: - case BitTorrent::TorrentState::MissingFiles: - case BitTorrent::TorrentState::Error: - return getErrorIcon(); - default: - Q_ASSERT(false); - return getErrorIcon(); - } -} - -QColor getColorByState(BitTorrent::TorrentState state) -{ - // Color names taken from http://cloford.com/resources/colours/500col.htm - bool dark = isDarkTheme(); - - switch (state) { - case BitTorrent::TorrentState::Downloading: - case BitTorrent::TorrentState::ForcedDownloading: - case BitTorrent::TorrentState::DownloadingMetadata: - if (!dark) - return QColor(34, 139, 34); // Forest Green - else - return QColor(50, 205, 50); // Lime Green - case BitTorrent::TorrentState::Allocating: - case BitTorrent::TorrentState::StalledDownloading: - case BitTorrent::TorrentState::StalledUploading: - if (!dark) - return QColor(0, 0, 0); // Black - else - return QColor(204, 204, 204); // Gray 80 - case BitTorrent::TorrentState::Uploading: - case BitTorrent::TorrentState::ForcedUploading: - if (!dark) - return QColor(65, 105, 225); // Royal Blue - else - return QColor(99, 184, 255); // Steel Blue 1 - case BitTorrent::TorrentState::PausedDownloading: - return QColor(250, 128, 114); // Salmon - case BitTorrent::TorrentState::PausedUploading: - if (!dark) - return QColor(0, 0, 139); // Dark Blue - else - return QColor(79, 148, 205); // Steel Blue 3 - case BitTorrent::TorrentState::Error: - case BitTorrent::TorrentState::MissingFiles: - return QColor(255, 0, 0); // red - case BitTorrent::TorrentState::QueuedDownloading: - case BitTorrent::TorrentState::QueuedUploading: - case BitTorrent::TorrentState::CheckingDownloading: - case BitTorrent::TorrentState::CheckingUploading: -#if LIBTORRENT_VERSION_NUM < 10100 - case BitTorrent::TorrentState::QueuedForChecking: -#endif - case BitTorrent::TorrentState::CheckingResumeData: - if (!dark) - return QColor(0, 128, 128); // Teal - else - return QColor(0, 205, 205); // Cyan 3 - case BitTorrent::TorrentState::Unknown: - return QColor(255, 0, 0); // red - default: - Q_ASSERT(false); - return QColor(255, 0, 0); // red - } -} - -QIcon getPausedIcon() -{ - static QIcon cached = QIcon(":/icons/skin/paused.png"); - return cached; -} - -QIcon getQueuedIcon() -{ - static QIcon cached = QIcon(":/icons/skin/queued.png"); - return cached; -} - -QIcon getDownloadingIcon() -{ - static QIcon cached = QIcon(":/icons/skin/downloading.png"); - return cached; -} - -QIcon getStalledDownloadingIcon() -{ - static QIcon cached = QIcon(":/icons/skin/stalledDL.png"); - return cached; -} - -QIcon getUploadingIcon() -{ - static QIcon cached = QIcon(":/icons/skin/uploading.png"); - return cached; -} - -QIcon getStalledUploadingIcon() -{ - static QIcon cached = QIcon(":/icons/skin/stalledUP.png"); - return cached; -} - -QIcon getCompletedIcon() -{ - static QIcon cached = QIcon(":/icons/skin/completed.png"); - return cached; -} - -QIcon getCheckingIcon() -{ - static QIcon cached = QIcon(":/icons/skin/checking.png"); - return cached; -} - -QIcon getErrorIcon() -{ - static QIcon cached = QIcon(":/icons/skin/error.png"); - return cached; -} - -bool isDarkTheme() +CachedSettingValue &TorrentModel::textIsColorizedSetting() { - QPalette pal = QApplication::palette(); - // QPalette::Base is used for the background of the Treeview - QColor color = pal.color(QPalette::Active, QPalette::Base); - return (color.lightness() < 127); + static CachedSettingValue setting("Appearance/TransferListIsColorized", true); + return setting; } diff --git a/src/gui/torrentmodel.h b/src/gui/torrentmodel.h index fd9161ec8c..f04f27d665 100644 --- a/src/gui/torrentmodel.h +++ b/src/gui/torrentmodel.h @@ -35,6 +35,8 @@ #include #include +template class CachedSettingValue; + namespace BitTorrent { class InfoHash; @@ -94,6 +96,9 @@ class TorrentModel : public QAbstractListModel BitTorrent::TorrentHandle *torrentHandle(const QModelIndex &index) const; + static bool textIsColorized(); + static void setTextIsColorized(bool v); + private slots: void addTorrent(BitTorrent::TorrentHandle *const torrent); void handleTorrentAboutToBeRemoved(BitTorrent::TorrentHandle *const torrent); @@ -101,6 +106,8 @@ private slots: void handleTorrentsUpdated(); private: + static CachedSettingValue &textIsColorizedSetting(); + QList m_torrents; }; diff --git a/src/gui/trackerlogin.cpp b/src/gui/trackerlogin.cpp index e2d4252218..2245c94398 100644 --- a/src/gui/trackerlogin.cpp +++ b/src/gui/trackerlogin.cpp @@ -33,6 +33,7 @@ #include #include #include "base/bittorrent/torrenthandle.h" +#include "guiiconprovider.h" trackerLogin::trackerLogin(QWidget *parent, BitTorrent::TorrentHandle *const torrent) : QDialog(parent) @@ -40,11 +41,8 @@ trackerLogin::trackerLogin(QWidget *parent, BitTorrent::TorrentHandle *const tor { setupUi(this); setAttribute(Qt::WA_DeleteOnClose); - buttonBox->button(QDialogButtonBox::Ok)->setText(tr("Log in")); - - login_logo->setPixmap(QPixmap(QString::fromUtf8(":/icons/qbt-theme/encrypted.png"))); - + login_logo->setPixmap(GuiIconProvider::instance()->getIcon(QLatin1String("encrypted")).pixmap(32, 32)); tracker_url->setText(torrent->currentTracker()); connect(buttonBox, &QDialogButtonBox::accepted, this, &trackerLogin::loginButtonClicked); diff --git a/src/gui/transferlistfilterswidget.cpp b/src/gui/transferlistfilterswidget.cpp index 6f750ad970..c07e874027 100644 --- a/src/gui/transferlistfilterswidget.cpp +++ b/src/gui/transferlistfilterswidget.cpp @@ -122,31 +122,27 @@ StatusFiltersWidget::StatusFiltersWidget(QWidget *parent, TransferListWidget *tr // Add status filters QListWidgetItem *all = new QListWidgetItem(this); all->setData(Qt::DisplayRole, QVariant(tr("All (0)", "this is for the status filter"))); - all->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterall.png")); QListWidgetItem *downloading = new QListWidgetItem(this); downloading->setData(Qt::DisplayRole, QVariant(tr("Downloading (0)"))); - downloading->setData(Qt::DecorationRole, QIcon(":/icons/skin/downloading.png")); QListWidgetItem *seeding = new QListWidgetItem(this); seeding->setData(Qt::DisplayRole, QVariant(tr("Seeding (0)"))); - seeding->setData(Qt::DecorationRole, QIcon(":/icons/skin/uploading.png")); QListWidgetItem *completed = new QListWidgetItem(this); completed->setData(Qt::DisplayRole, QVariant(tr("Completed (0)"))); - completed->setData(Qt::DecorationRole, QIcon(":/icons/skin/completed.png")); QListWidgetItem *resumed = new QListWidgetItem(this); resumed->setData(Qt::DisplayRole, QVariant(tr("Resumed (0)"))); - resumed->setData(Qt::DecorationRole, QIcon(":/icons/skin/resumed.png")); QListWidgetItem *paused = new QListWidgetItem(this); paused->setData(Qt::DisplayRole, QVariant(tr("Paused (0)"))); - paused->setData(Qt::DecorationRole, QIcon(":/icons/skin/paused.png")); QListWidgetItem *active = new QListWidgetItem(this); active->setData(Qt::DisplayRole, QVariant(tr("Active (0)"))); - active->setData(Qt::DecorationRole, QIcon(":/icons/skin/filteractive.png")); QListWidgetItem *inactive = new QListWidgetItem(this); inactive->setData(Qt::DisplayRole, QVariant(tr("Inactive (0)"))); - inactive->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterinactive.png")); QListWidgetItem *errored = new QListWidgetItem(this); errored->setData(Qt::DisplayRole, QVariant(tr("Errored (0)"))); - errored->setData(Qt::DecorationRole, QIcon(":/icons/skin/error.png")); + + updateStatusIcons(); + + connect(BitTorrent::Session::instance(), &BitTorrent::Session::torrentsUpdated, this, &StatusFiltersWidget::updateTorrentNumbers); + connect(GuiIconProvider::instance(), &GuiIconProvider::iconsChanged, this, &StatusFiltersWidget::updateStatusIcons); const Preferences* const pref = Preferences::instance(); setCurrentRow(pref->getTransSelFilter(), QItemSelectionModel::SelectCurrent); @@ -173,6 +169,23 @@ void StatusFiltersWidget::updateTorrentNumbers() item(TorrentFilter::Errored)->setData(Qt::DisplayRole, QVariant(tr("Errored (%1)").arg(report.nbErrored))); } +void StatusFiltersWidget::updateStatusIcons() +{ + const GuiIconProvider* icons = GuiIconProvider::instance(); // shortcut + using BitTorrent::TorrentState; + + item(TorrentFilter::All)->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterall.svg")); + item(TorrentFilter::Downloading)->setData(Qt::DecorationRole, icons->icon(TorrentState::Downloading)); + item(TorrentFilter::Seeding)->setData(Qt::DecorationRole, icons->icon(TorrentState::Uploading)); + item(TorrentFilter::Completed)->setData(Qt::DecorationRole, icons->icon(TorrentState::PausedUploading)); + item(TorrentFilter::Resumed)->setData(Qt::DecorationRole, QIcon(":/icons/skin/resumed.svg")); + item(TorrentFilter::Paused)->setData(Qt::DecorationRole, icons->icon(TorrentState::PausedDownloading)); + item(TorrentFilter::Active)->setData(Qt::DecorationRole, QIcon(":/icons/skin/filteractive.svg")); + item(TorrentFilter::Inactive)->setData(Qt::DecorationRole, QIcon(":/icons/skin/filterinactive.svg")); + item(TorrentFilter::Errored)->setData(Qt::DecorationRole, icons->icon(TorrentState::Error)); +} + + void StatusFiltersWidget::showMenu(QPoint) {} void StatusFiltersWidget::applyFilter(int row) diff --git a/src/gui/transferlistfilterswidget.h b/src/gui/transferlistfilterswidget.h index 98b1d6e9f6..62b7ca5ad4 100644 --- a/src/gui/transferlistfilterswidget.h +++ b/src/gui/transferlistfilterswidget.h @@ -80,6 +80,7 @@ class StatusFiltersWidget: public FiltersBase private slots: void updateTorrentNumbers(); + void updateStatusIcons(); private: // These 4 methods are virtual slots in the base class. diff --git a/src/gui/transferlistwidget.cpp b/src/gui/transferlistwidget.cpp index 34d9e75a03..ae9ef58b25 100644 --- a/src/gui/transferlistwidget.cpp +++ b/src/gui/transferlistwidget.cpp @@ -61,6 +61,8 @@ #include "transferlistdelegate.h" #include "transferlistsortmodel.h" #include "updownratiodlg.h" +#include "theme/fonttheme.h" +#include "theme/themeprovider.h" namespace { @@ -296,6 +298,10 @@ TransferListWidget::TransferListWidget(QWidget *parent, MainWindow *mainWindow) unused.setVerticalHeader(header()); header()->setParent(this); unused.setVerticalHeader(new QHeaderView(Qt::Horizontal)); + + applyFontTheme(); + connect(&Theme::ThemeProvider::instance(), &Theme::ThemeProvider::fontThemeChanged, + this, &TransferListWidget::applyFontTheme); } TransferListWidget::~TransferListWidget() @@ -844,7 +850,7 @@ void TransferListWidget::displayListMenu(const QPoint&) connect(&actionDelete, SIGNAL(triggered()), this, SLOT(softDeleteSelectedTorrents())); QAction actionPreview_file(GuiIconProvider::instance()->getIcon("view-preview"), tr("Preview file..."), 0); connect(&actionPreview_file, SIGNAL(triggered()), this, SLOT(previewSelectedTorrents())); - QAction actionSet_max_ratio(QIcon(QString::fromUtf8(":/icons/skin/ratio.png")), tr("Limit share ratio..."), 0); + QAction actionSet_max_ratio(QIcon(QString::fromUtf8(":/icons/skin/ratio.svg")), tr("Limit share ratio..."), 0); connect(&actionSet_max_ratio, SIGNAL(triggered()), this, SLOT(setMaxRatioSelectedTorrents())); QAction actionSet_upload_limit(GuiIconProvider::instance()->getIcon("kt-set-max-upload-speed"), tr("Limit upload rate..."), 0); connect(&actionSet_upload_limit, SIGNAL(triggered()), this, SLOT(setUpLimitSelectedTorrents())); @@ -1194,3 +1200,13 @@ void TransferListWidget::wheelEvent(QWheelEvent *event) QTreeView::wheelEvent(event); // event delegated to base class } + +void TransferListWidget::applyFontTheme() +{ + QFont font = Theme::FontTheme::current().font(Theme::FontThemeElement::TransferList); + setFont(font); + header()->setFont(font); + foreach (QWidget *widget, header()->findChildren()) { + widget->setFont(font); + } +} diff --git a/src/gui/transferlistwidget.h b/src/gui/transferlistwidget.h index 2ad24ab690..6d06598e99 100644 --- a/src/gui/transferlistwidget.h +++ b/src/gui/transferlistwidget.h @@ -120,6 +120,9 @@ protected slots: signals: void currentTorrentChanged(BitTorrent::TorrentHandle *const torrent); +private slots: + void applyFontTheme(); + private: void wheelEvent(QWheelEvent *event) override; void askAddTagsForSelection(); diff --git a/src/gui/utils/colorutils.cpp b/src/gui/utils/colorutils.cpp new file mode 100644 index 0000000000..9cf3740ed4 --- /dev/null +++ b/src/gui/utils/colorutils.cpp @@ -0,0 +1,328 @@ +/* This file is composed from stripped versions of + * kcolorutils.cpp, kguiaddons_colorhelpers_p.h, + * kcolorspaces_p.h, and kcolorspaces.cpp + * + * This file is part of the KDE project + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Thomas Zander + * Copyright (C) 2007 Zack Rusin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ +#include "colorutils.h" + +#include +#include + +#include // qIsNaN + +#include + +// BEGIN internal helper functions +namespace { + // normalize: like qBound(a, 0.0, 1.0) but without needing the args and with + // "safer" behavior on NaN (isnan(a) -> return 0.0) + inline qreal normalize(qreal a) + { + return (a < 1.0 ? (a > 0.0 ? a : 0.0) : 1.0); + } + + inline qreal mixQreal(qreal a, qreal b, qreal bias) + { + return a + (b - a) * bias; + } + + inline qreal wrap(qreal a, qreal d = 1.0) + { + qreal r = fmod(a, d); + return (r < 0.0 ? d + r : (r > 0.0 ? r : 0.0)); + } + + // END internal helper functions + + class HCY + { + public: + explicit HCY(const QColor &); + explicit HCY(qreal h_, qreal c_, qreal y_, qreal a_ = 1.0); + QColor qColor() const; + qreal h, c, y, a; + static qreal luma(const QColor &); + private: + static qreal gamma(qreal); + static qreal igamma(qreal); + static qreal lumag(qreal, qreal, qreal); + }; + + /////////////////////////////////////////////////////////////////////////////// + // HCY color space + +#define HCY_REC 709 // use 709 for now +#if HCY_REC == 601 + static const qreal yc[3] = { 0.299, 0.587, 0.114 }; +#elif HCY_REC == 709 + static const qreal yc[3] = {0.2126, 0.7152, 0.0722}; +#else // use Qt values + static const qreal yc[3] = { 0.34375, 0.5, 0.15625 }; +#endif + + qreal HCY::gamma(qreal n) + { + return pow(normalize(n), 2.2); + } + + qreal HCY::igamma(qreal n) + { + return pow(normalize(n), 1.0 / 2.2); + } + + qreal HCY::lumag(qreal r, qreal g, qreal b) + { + return r * yc[0] + g * yc[1] + b * yc[2]; + } + +#if 0 + HCY::HCY(qreal h_, qreal c_, qreal y_, qreal a_) + { + h = h_; + c = c_; + y = y_; + a = a_; + } +#endif + + HCY::HCY(const QColor &color) + { + qreal r = gamma(color.redF()); + qreal g = gamma(color.greenF()); + qreal b = gamma(color.blueF()); + a = color.alphaF(); + + // luma component + y = lumag(r, g, b); + + // hue component + qreal p = qMax(qMax(r, g), b); + qreal n = qMin(qMin(r, g), b); + qreal d = 6.0 * (p - n); + if (n == p) + h = 0.0; + else if (r == p) + h = ((g - b) / d); + else if (g == p) + h = ((b - r) / d) + (1.0 / 3.0); + else + h = ((r - g) / d) + (2.0 / 3.0); + + // chroma component + if (( r == g) && ( g == b) ) + c = 0.0; + else + c = qMax((y - n) / y, (p - y) / (1 - y)); + } + + QColor HCY::qColor() const + { + // start with sane component values + qreal _h = wrap(h); + qreal _c = normalize(c); + qreal _y = normalize(y); + + // calculate some needed variables + qreal _hs = _h * 6.0, th, tm; + if (_hs < 1.0) { + th = _hs; + tm = yc[0] + yc[1] * th; + } + else if (_hs < 2.0) { + th = 2.0 - _hs; + tm = yc[1] + yc[0] * th; + } + else if (_hs < 3.0) { + th = _hs - 2.0; + tm = yc[1] + yc[2] * th; + } + else if (_hs < 4.0) { + th = 4.0 - _hs; + tm = yc[2] + yc[1] * th; + } + else if (_hs < 5.0) { + th = _hs - 4.0; + tm = yc[2] + yc[0] * th; + } + else { + th = 6.0 - _hs; + tm = yc[0] + yc[2] * th; + } + + // calculate RGB channels in sorted order + qreal tn, to, tp; + if (tm >= _y) { + tp = _y + _y * _c * (1.0 - tm) / tm; + to = _y + _y * _c * (th - tm) / tm; + tn = _y - (_y * _c); + } + else { + tp = _y + (1.0 - _y) * _c; + to = _y + (1.0 - _y) * _c * (th - tm) / (1.0 - tm); + tn = _y - (1.0 - _y) * _c * tm / (1.0 - tm); + } + + // return RGB channels in appropriate order + if (_hs < 1.0) + return QColor::fromRgbF(igamma(tp), igamma(to), igamma(tn), a); + else if (_hs < 2.0) + return QColor::fromRgbF(igamma(to), igamma(tp), igamma(tn), a); + else if (_hs < 3.0) + return QColor::fromRgbF(igamma(tn), igamma(tp), igamma(to), a); + else if (_hs < 4.0) + return QColor::fromRgbF(igamma(tn), igamma(to), igamma(tp), a); + else if (_hs < 5.0) + return QColor::fromRgbF(igamma(to), igamma(tn), igamma(tp), a); + else + return QColor::fromRgbF(igamma(tp), igamma(tn), igamma(to), a); + } + + qreal HCY::luma(const QColor &color) + { + return lumag(gamma(color.redF()), + gamma(color.greenF()), + gamma(color.blueF())); + } + +} + +qreal Utils::Color::luma(const QColor &color) +{ + return HCY::luma(color); +} + +void Utils::Color::getHcy(const QColor &color, qreal *h, qreal *c, qreal *y, qreal *a) +{ + if (!c || !h || !y) + return; + HCY khcy(color); + *c = khcy.c; + *h = khcy.h; + *y = khcy.y; + if (a) + *a = khcy.a; +} + +static qreal contrastRatioForLuma(qreal y1, qreal y2) +{ + if (y1 > y2) + return (y1 + 0.05) / (y2 + 0.05); + else + return (y2 + 0.05) / (y1 + 0.05); +} + +qreal Utils::Color::contrastRatio(const QColor &c1, const QColor &c2) +{ + return contrastRatioForLuma(luma(c1), luma(c2)); +} + +QColor Utils::Color::lighten(const QColor &color, qreal ky, qreal kc) +{ + HCY c(color); + c.y = 1.0 - normalize((1.0 - c.y) * (1.0 - ky)); + c.c = 1.0 - normalize((1.0 - c.c) * kc); + return c.qColor(); +} + +QColor Utils::Color::darken(const QColor &color, qreal ky, qreal kc) +{ + HCY c(color); + c.y = normalize(c.y * (1.0 - ky)); + c.c = normalize(c.c * kc); + return c.qColor(); +} + +QColor Utils::Color::shade(const QColor &color, qreal ky, qreal kc) +{ + HCY c(color); + c.y = normalize(c.y + ky); + c.c = normalize(c.c + kc); + return c.qColor(); +} + +static QColor tintHelper(const QColor &base, qreal baseLuma, const QColor &color, qreal amount) +{ + HCY result(Utils::Color::mix(base, color, pow(amount, 0.3))); + result.y = mixQreal(baseLuma, result.y, amount); + + return result.qColor(); +} + +QColor Utils::Color::tint(const QColor &base, const QColor &color, qreal amount) +{ + if (amount <= 0.0) + return base; + if (amount >= 1.0) + return color; + if (qIsNaN(amount)) + return base; + + qreal baseLuma = luma(base); //cache value because luma call is expensive + double ri = contrastRatioForLuma(baseLuma, luma(color)); + double rg = 1.0 + ((ri + 1.0) * amount * amount * amount); + double u = 1.0, l = 0.0; + QColor result; + for (int i = 12; i; --i) { + double a = 0.5 * (l + u); + result = tintHelper(base, baseLuma, color, a); + double ra = contrastRatioForLuma(baseLuma, luma(result)); + if (ra > rg) + u = a; + else + l = a; + } + return result; +} + +QColor Utils::Color::mix(const QColor &c1, const QColor &c2, qreal bias) +{ + if (bias <= 0.0) + return c1; + if (bias >= 1.0) + return c2; + if (qIsNaN(bias)) + return c1; + + qreal r = mixQreal(c1.redF(), c2.redF(), bias); + qreal g = mixQreal(c1.greenF(), c2.greenF(), bias); + qreal b = mixQreal(c1.blueF(), c2.blueF(), bias); + qreal a = mixQreal(c1.alphaF(), c2.alphaF(), bias); + + return QColor::fromRgbF(r, g, b, a); +} + +QColor Utils::Color::overlayColors(const QColor &base, const QColor &paint, + QPainter::CompositionMode comp) +{ + // This isn't the fastest way, but should be "fast enough". + // It's also the only safe way to use QPainter::CompositionMode + QImage img(1, 1, QImage::Format_ARGB32_Premultiplied); + QPainter p(&img); + QColor start = base; + start.setAlpha(255); // opaque + p.fillRect(0, 0, 1, 1, start); + p.setCompositionMode(comp); + p.fillRect(0, 0, 1, 1, paint); + p.end(); + return img.pixel(0, 0); +} + diff --git a/src/gui/utils/colorutils.h b/src/gui/utils/colorutils.h new file mode 100644 index 0000000000..0f9223a732 --- /dev/null +++ b/src/gui/utils/colorutils.h @@ -0,0 +1,165 @@ +/* This is the stripped version of the file kcolorutils.h + * from KDE framework KGuiAddons. + * + * This file is part of the KDE project + * Copyright (C) 2007 Matthew Woehlke + * Copyright (C) 2007 Thomas Zander + * Copyright (C) 2007 Zack Rusin + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library 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 + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public License + * along with this library; see the file COPYING.LIB. If not, write to + * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, + * Boston, MA 02110-1301, USA. + */ + +#ifndef COLORUTILS_H +#define COLORUTILS_H + +#include + +class QColor; + +/** + * A set of methods used to work with colors. + */ +namespace Utils +{ + namespace Color + { + /** + * Calculate the luma of a color. Luma is weighted sum of gamma-adjusted + * R'G'B' components of a color. The result is similar to qGray. The range + * is from 0.0 (black) to 1.0 (white). + * + * Utils::Color::darken(), Utils::Color::lighten() and Utils::Color::shade() + * operate on the luma of a color. + * + * @see http://en.wikipedia.org/wiki/Luma_(video) + */ + qreal luma(const QColor &); + + /** + * Calculate hue, chroma and luma of a color in one call. + */ + void getHcy(const QColor &, qreal *hue, qreal *chroma, qreal *luma, qreal *alpha = 0); + + /** + * Calculate the contrast ratio between two colors, according to the + * W3C/WCAG2.0 algorithm, (Lmax + 0.05)/(Lmin + 0.05), where Lmax and Lmin + * are the luma values of the lighter color and the darker color, + * respectively. + * + * A contrast ration of 5:1 (result == 5.0) is the minimum for "normal" + * text to be considered readable (large text can go as low as 3:1). The + * ratio ranges from 1:1 (result == 1.0) to 21:1 (result == 21.0). + * + * @see Utils::Color::luma + */ + qreal contrastRatio(const QColor &, const QColor &); + + /** + * Adjust the luma of a color by changing its distance from white. + * + * @li amount == 1.0 gives white + * @li amount == 0.5 results in a color whose luma is halfway between 1.0 + * and that of the original color + * @li amount == 0.0 gives the original color + * @li amount == -1.0 gives a color that is 'twice as far from white' as + * the original color, that is luma(result) == 1.0 - 2*(1.0 - luma(color)) + * + * @param amount factor by which to adjust the luma component of the color + * @param chromaInverseGain (optional) factor by which to adjust the chroma + * component of the color; 1.0 means no change, 0.0 maximizes chroma + * @see Utils::Color::shade + */ + QColor lighten(const QColor &, qreal amount = 0.5, qreal chromaInverseGain = 1.0); + + /** + * Adjust the luma of a color by changing its distance from black. + * + * @li amount == 1.0 gives black + * @li amount == 0.5 results in a color whose luma is halfway between 0.0 + * and that of the original color + * @li amount == 0.0 gives the original color + * @li amount == -1.0 gives a color that is 'twice as far from black' as + * the original color, that is luma(result) == 2*luma(color) + * + * @param amount factor by which to adjust the luma component of the color + * @param chromaGain (optional) factor by which to adjust the chroma + * component of the color; 1.0 means no change, 0.0 minimizes chroma + * @see Utils::Color::shade + */ + QColor darken(const QColor &, qreal amount = 0.5, qreal chromaGain = 1.0); + + /** + * Adjust the luma and chroma components of a color. The amount is added + * to the corresponding component. + * + * @param lumaAmount amount by which to adjust the luma component of the + * color; 0.0 results in no change, -1.0 turns anything black, 1.0 turns + * anything white + * @param chromaAmount (optional) amount by which to adjust the chroma + * component of the color; 0.0 results in no change, -1.0 minimizes chroma, + * 1.0 maximizes chroma + * @see Utils::Color::luma + */ + QColor shade(const QColor &, qreal lumaAmount, qreal chromaAmount = 0.0); + + /** + * Create a new color by tinting one color with another. This function is + * meant for creating additional colors withings the same class (background, + * foreground) from colors in a different class. Therefore when @p amount + * is low, the luma of @p base is mostly preserved, while the hue and + * chroma of @p color is mostly inherited. + * + * @param base color to be tinted + * @param color color with which to tint + * @param amount how strongly to tint the base; 0.0 gives @p base, + * 1.0 gives @p color + */ + QColor tint(const QColor &base, const QColor &color, qreal amount = 0.3); + + /** + * Blend two colors into a new color by linear combination. + * @code + QColor lighter = Utils::Color::mix(myColor, Qt::white) + * @endcode + * @param c1 first color. + * @param c2 second color. + * @param bias weight to be used for the mix. @p bias <= 0 gives @p c1, + * @p bias >= 1 gives @p c2. @p bias == 0.5 gives a 50% blend of @p c1 + * and @p c2. + */ + QColor mix(const QColor &c1, const QColor &c2, + qreal bias = 0.5); + + /** + * Blend two colors into a new color by painting the second color over the + * first using the specified composition mode. + * @code + QColor white(Qt::white); + white.setAlphaF(0.5); + QColor lighter = Utils::Color::overlayColors(myColor, white); + @endcode + * @param base the base color (alpha channel is ignored). + * @param paint the color to be overlayed onto the base color. + * @param comp the CompositionMode used to do the blending. + */ + QColor overlayColors(const QColor &base, const QColor &paint, + QPainter::CompositionMode comp = QPainter::CompositionMode_SourceOver); + + } +} + +#endif // COLORUTILS_H + diff --git a/src/gui/utils/plasmacolorscheme.cpp b/src/gui/utils/plasmacolorscheme.cpp new file mode 100644 index 0000000000..a3c41d17d8 --- /dev/null +++ b/src/gui/utils/plasmacolorscheme.cpp @@ -0,0 +1,456 @@ +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2016 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#include "plasmacolorscheme.h" + +#include + +#include +#include +#include +#include +#include + +#include "colorutils.h" + +namespace +{ + class KDEColorThemeDecodingError: public std::runtime_error + { + public: + KDEColorThemeDecodingError(const QString& entryName); + const QString &entryName() const; + + private: + QString m_entryName; + }; + + KDEColorThemeDecodingError::KDEColorThemeDecodingError(const QString& entryName) + : std::runtime_error("Could not parse color '" + entryName.toStdString() + '\'') + , m_entryName {entryName} + { + } + + const QString &KDEColorThemeDecodingError::entryName() const + { + return m_entryName; + } + + static QString settingsGroupName(PlasmaColorScheme::ColorSet set) + { + switch (set) { + case PlasmaColorScheme::View: + return QLatin1String("Colors:View"); + case PlasmaColorScheme::Window: + return QLatin1String("Colors:Window"); + case PlasmaColorScheme::Button: + return QLatin1String("Colors:Button"); + case PlasmaColorScheme::Selection: + return QLatin1String("Colors:Selection"); + case PlasmaColorScheme::Tooltip: + return QLatin1String("Colors:Tooltip"); + default: + throw std::logic_error("Unexpected value of PlasmaColorScheme::ColorSet"); + } + } + + QRgb decodeKDEColor(QSettings &settings, const QString &colorName) + { + QVariantList components(settings.value(colorName).toList()); + if (components.size() == 3) { + bool rOk, gOk, bOk; + const int r = components[0].toString().toInt(&rOk); + const int g = components[1].toString().toInt(&gOk); + const int b = components[2].toString().toInt(&bOk); + if (rOk && gOk && bOk) { + return qRgb(r, g, b); + } + } + throw KDEColorThemeDecodingError(colorName); + } + + class SettingsGroupGuard + { + public: + SettingsGroupGuard(QSettings &settings, const QString &groupName); + ~SettingsGroupGuard(); + + private: + QSettings &m_settings; + }; + + SettingsGroupGuard::SettingsGroupGuard(QSettings& settings, const QString& groupName) + : m_settings(settings) + { + m_settings.beginGroup(groupName); + } + + SettingsGroupGuard::~SettingsGroupGuard() + { + m_settings.endGroup(); + } + + template + T readValue(QSettings &settings, const QString &key, const T &defaultValue = T()) + { + QVariant v = settings.value(key, defaultValue); + if (v.canConvert()) { + return v.value(); + } + throw KDEColorThemeDecodingError(key); + } + + PlasmaColorScheme::Effect readColorEffect(QSettings &settings, const QString &name) + { + using Effect = PlasmaColorScheme::Effect; + Effect res; + + SettingsGroupGuard group(settings, QLatin1String("ColorEffects:") + name); + + res.changeSelectionColor = settings.value(QLatin1String("ChangeSelectionColor"), false).toBool(); + res.color = decodeKDEColor(settings, QLatin1String("Color")); + res.colorAmount = readValue(settings, QLatin1String("ColorAmount")); + res.colorEffect = static_cast(readValue(settings, QLatin1String("ColorEffect"))); + res.contrastAmount = readValue(settings, QLatin1String("ContrastAmount")); + res.contrastEffect = static_cast(readValue(settings, QLatin1String("ContrastEffect"))); + res.enable = readValue(settings, QLatin1String("Enable"), false); + res.intensityAmount = readValue(settings, QLatin1String("IntensityAmount")); + res.intensityEffect = static_cast(readValue(settings, QLatin1String("IntensityEffect"))); + return res; + } + + QColor applyColorEffect(const QColor &color, const PlasmaColorScheme::Effect &effect) + { + using Effect = PlasmaColorScheme::Effect; + QColor res(color); + + switch (effect.intensityEffect) { + case Effect::IntensityEffectNone: + break; + case Effect::IntensityEffectShade: + res = Utils::Color::shade(res, effect.intensityAmount); + break; + case Effect::IntensityEffectDarken: + res = Utils::Color::darken(res, effect.intensityAmount); + break; + case Effect::IntensityEffectLighten: + res = Utils::Color::lighten(res, effect.intensityAmount); + break; + } + + switch (effect.colorEffect) { + case Effect::ColorEffectNone: + break; + case Effect::ColorEffectDesaturate: + res = Utils::Color::darken(res, 0., 1.0 - effect.colorAmount); + break; + case Effect::ColorEffectFade: + res = Utils::Color::mix(res, effect.color, effect.colorAmount); + break; + case Effect::ColorEffectTint: + res = Utils::Color::tint(res, effect.color, effect.colorAmount); + break; + } + +#if 0 + // shall we worry about contrast with background at all? + switch (effect.contrastEffect) { + case KDEColorEffect::ContrastEffectNone: + break; + case KDEColorEffect::ContrastEffectFade: + res = Utils::Color::mix(res, bgColor, effect.contrastAmount); + break; + case KDEColorEffect::ContrastEffectTint: + res = Utils::Color::tint(res, bgColor, effect.contrastAmount); + break; + } +#endif + return res; + } + + class ColorLoadingError: public std::runtime_error + { + public: + ColorLoadingError() : std::runtime_error(""){} + }; +} + +Q_GLOBAL_STATIC(PlasmaColorScheme, kdeColorScheme) + +const PlasmaColorScheme *PlasmaColorScheme::instance() +{ + return kdeColorScheme; +} + +PlasmaColorScheme::PlasmaColorScheme(const QProcessEnvironment& env) +{ + reload(env); +} + +void PlasmaColorScheme::reload(const QProcessEnvironment& env) +{ + try { + load(env); + m_loadedSuccesfully = true; + } + catch (ColorLoadingError&) { + qDebug() << "Could not load Plasma color theme. Falling back to QPalette"; + m_loadedSuccesfully = false; + setFallbackColors(); + } +} + +QColor PlasmaColorScheme::color(PlasmaColorScheme::ColorRole role, QPalette::ColorGroup group, PlasmaColorScheme::ColorSet set) const +{ + if (group > QPalette::Inactive) + return Qt::black; + + return m_colors[group][set][role]; +} + +QColor PlasmaColorScheme::inactiveColor(const QColor& color) const +{ + return applyColorEffect(color, m_inactiveEffect); +} + +QColor PlasmaColorScheme::disabledColor(const QColor& color) const +{ + return applyColorEffect(color, m_disabledEffect); +} + +bool PlasmaColorScheme::wasLoadedSuccesfully() const +{ + return m_loadedSuccesfully; +} + +void PlasmaColorScheme::load(const QProcessEnvironment& env) +{ + bool versionDecodedOk = false; + int kdeVersion = env.value(QLatin1String("KDE_SESSION_VERSION"), QLatin1String("4")) + .toInt(&versionDecodedOk); + if (!versionDecodedOk) + throw ColorLoadingError(); + QString kdeCfgFile; + switch (kdeVersion) { + case 4: + kdeCfgFile = QLatin1String(".kde4/share/config/kdeglobals"); + break; + case 5: + kdeCfgFile = QLatin1String(".config/kdeglobals"); + break; + default: + throw ColorLoadingError(); + } + + QString configPath = QDir(QDir::homePath()).absoluteFilePath(kdeCfgFile); + if (!QFileInfo(configPath).exists()) + throw ColorLoadingError(); + + QSettings kdeGlobals(configPath, QSettings::IniFormat); + ColorsSet activeColors; + try { + activeColors[View] = readSet(kdeGlobals, View); + activeColors[Window] = readSet(kdeGlobals, Window); + activeColors[Button] = readSet(kdeGlobals, Button); + activeColors[Selection] = readSet(kdeGlobals, Selection); + activeColors[Tooltip] = readSet(kdeGlobals, Tooltip); + } + catch (KDEColorThemeDecodingError& ex) { + qDebug("Could not parse KDE/Plasma color scheme: %s", ex.what()); + throw ColorLoadingError(); + } + + m_colors[QPalette::Active] = activeColors; + + try { + m_inactiveEffect = readColorEffect(kdeGlobals, QLatin1String("Inactive")); + m_disabledEffect = readColorEffect(kdeGlobals, QLatin1String("Disabled")); + } + catch (KDEColorThemeDecodingError&) { + // perhaps we have recent enough Plasma and effects were not copied to .kdeglobals, + // we have to load them from theme file + const QString themeName = kdeGlobals.value(QLatin1String("ColorScheme"), QString()).toString(); + if (themeName.isEmpty()) { + throw ColorLoadingError(); // we do not know theme name + } + const QString themeFileName = locateColorThemeFile(themeName); + if (themeFileName.isEmpty()) { + qDebug("Could not find file for Plasma color theme '%s'", qPrintable(themeName)); + throw ColorLoadingError(); + } + QSettings theme(themeFileName, QSettings::IniFormat); + try { + m_inactiveEffect = readColorEffect(theme, QLatin1String("Inactive")); + m_disabledEffect = readColorEffect(theme, QLatin1String("Disabled")); + } + catch (KDEColorThemeDecodingError& ex) { + // no luck, we give up + qDebug("Could not load effects from theme file '%s': %s", qPrintable(themeFileName), ex.what()); + throw ColorLoadingError(); + } + } + + m_colors[QPalette::Inactive] = applyEffect(activeColors, m_inactiveEffect); + m_colors[QPalette::Disabled] = applyEffect(activeColors, m_disabledEffect); +} + + +PlasmaColorScheme::ColorsMap PlasmaColorScheme::readSet(QSettings& settings, PlasmaColorScheme::ColorSet set) +{ + ColorsMap colors; + + try { + SettingsGroupGuard group(settings, settingsGroupName(set)); + colors[BackgroundAlternate] = decodeKDEColor(settings, QLatin1String("BackgroundAlternate")); + colors[BackgroundNormal] = decodeKDEColor(settings, QLatin1String("BackgroundNormal")); + colors[DecorationFocus] = decodeKDEColor(settings, QLatin1String("DecorationFocus")); + colors[ForegroundVisited] = decodeKDEColor(settings, QLatin1String("ForegroundVisited")); + colors[ForegroundNormal] = decodeKDEColor(settings, QLatin1String("ForegroundNormal")); + colors[DecorationHover] = decodeKDEColor(settings, QLatin1String("DecorationHover")); + colors[ForegroundActive] = decodeKDEColor(settings, QLatin1String("ForegroundActive")); + colors[ForegroundInactive] = decodeKDEColor(settings, QLatin1String("ForegroundInactive")); + colors[ForegroundLink] = decodeKDEColor(settings, QLatin1String("ForegroundLink")); + colors[ForegroundNegative] = decodeKDEColor(settings, QLatin1String("ForegroundNegative")); + colors[ForegroundNeutral] = decodeKDEColor(settings, QLatin1String("ForegroundNeutral")); + colors[ForegroundPositive] = decodeKDEColor(settings, QLatin1String("ForegroundPositive")); + + return colors; + } + catch (KDEColorThemeDecodingError &ex) { + throw KDEColorThemeDecodingError(settingsGroupName(set) + QLatin1Char('/') + ex.entryName()); + } +} + +void PlasmaColorScheme::setFallbackColors() +{ + m_colors[QPalette::Active] = fallbackColorSet(QPalette::Active); + m_colors[QPalette::Disabled] = fallbackColorSet(QPalette::Disabled); + m_colors[QPalette::Inactive] = fallbackColorSet(QPalette::Inactive); +} + +PlasmaColorScheme::ColorsSet PlasmaColorScheme::fallbackColorSet(QPalette::ColorGroup group) +{ + QPalette palette = QApplication::palette(); + ColorsMap view; + view[BackgroundAlternate] = palette.color(group, QPalette::AlternateBase); + view[BackgroundNormal] = palette.color(group, QPalette::Background); + view[DecorationFocus] = palette.color(group, QPalette::HighlightedText); + view[DecorationHover] = palette.color(group, QPalette::Highlight); + view[ForegroundActive] = palette.color(group, QPalette::Text); + view[ForegroundInactive] = palette.color(group, QPalette::Text); + view[ForegroundLink] = palette.color(group, QPalette::Link); + view[ForegroundNegative] = palette.color(group, QPalette::Text); + view[ForegroundNeutral] = palette.color(group, QPalette::Text); + view[ForegroundNormal] = palette.color(group, QPalette::Text); + view[ForegroundPositive] = palette.color(group, QPalette::Text); + view[ForegroundVisited] = palette.color(group, QPalette::LinkVisited); + + ColorsMap window = {view}; + window[BackgroundNormal] = window[BackgroundAlternate] = palette.color(group, QPalette::Window); + + ColorsMap button = {view}; + button[BackgroundNormal] = button[BackgroundAlternate] = palette.color(group, QPalette::Button); + button[ForegroundActive] = palette.color(group, QPalette::ButtonText); + button[ForegroundInactive] = palette.color(group, QPalette::ButtonText); + + ColorsMap tooltip = {view}; + tooltip[BackgroundNormal] = tooltip[BackgroundAlternate] = palette.color(group, QPalette::ToolTipBase); + tooltip[ForegroundActive] = tooltip[ForegroundInactive] = palette.color(group, QPalette::ToolTipText); + tooltip[ForegroundNegative] = tooltip[ForegroundNeutral] = palette.color(group, QPalette::ToolTipText); + tooltip[ForegroundNormal] = tooltip[ForegroundPositive] = palette.color(group, QPalette::ToolTipText); + + ColorsSet res; + res.insert(View, view); + res.insert(Window, window); + res.insert(Button, button); + res.insert(Tooltip, tooltip); + res.insert(Selection, view); + + return res; +} + +PlasmaColorScheme::ColorsSet PlasmaColorScheme::applyEffect(const PlasmaColorScheme::ColorsSet& set, const PlasmaColorScheme::Effect& effect) +{ + ColorsSet res; + for (auto i = set.begin(); i != set.end(); ++i) { + ColorsMap resMap; + const ColorsMap &srcMap = i.value(); + for (auto j = srcMap.begin(); j != srcMap.end(); ++j) + resMap[j.key()] = applyColorEffect(j.value(), effect); + res.insert(i.key(), resMap); + } + + return res; +} + +QString PlasmaColorScheme::locateColorThemeFile(const QString &themeName) +{ + // Plasma themes are located in share/color-schemes + // Usually file name is equal to ${themeName}.colors with + // first character uppercased. However, it is not guaranteed + // and we have to check Name key in the files + + QLatin1String colorShemesDirName("color-schemes"); + // let's go + // 1. uppercase first letter of the theme name and try to find such file + QString probableThemeFileName = themeName; + probableThemeFileName[0] = probableThemeFileName[0].toUpper(); + const QLatin1String themeFileExtension(".colors"); + + const QStringList probableThemes = QStandardPaths::locateAll( + QStandardPaths::GenericDataLocation, colorShemesDirName + QLatin1Char('/') + probableThemeFileName + themeFileExtension); + + // 2. this is our test function to check "Name=" key of the file + const auto checkThemeName = [&](const QString &file) + { + QSettings theme(file, QSettings::IniFormat); + return theme.value(QLatin1String("Name"), QString()).toString() == themeName; + }; + + for (const QString &f: probableThemes) { + if (checkThemeName(f)) { + return f; + } + } + + // 3. we have no other choice but to try all the files + const QStringList dirs = QStandardPaths::standardLocations(QStandardPaths::GenericDataLocation); + for (const QString &dir: dirs) { + QDir colorShemesDir(dir + QLatin1Char('/') + colorShemesDirName); + if (colorShemesDir.exists()) { + const QStringList files = colorShemesDir.entryList({QLatin1Char('*') + themeFileExtension}, QDir::Files); + for (const QString &f: files) { + if (checkThemeName(colorShemesDir.absoluteFilePath(f))) { + return colorShemesDir.absoluteFilePath(f); + } + } + } + } + // we failed to find theme file + return {}; +} diff --git a/src/gui/utils/plasmacolorscheme.h b/src/gui/utils/plasmacolorscheme.h new file mode 100644 index 0000000000..b212348cf0 --- /dev/null +++ b/src/gui/utils/plasmacolorscheme.h @@ -0,0 +1,173 @@ + +/* + * Bittorrent Client using Qt and libtorrent. + * Copyright (C) 2016 Eugene Shalygin + * + * 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 + * of the License, or (at your option) any later version. + * + * 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, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * In addition, as a special exception, the copyright holders give permission to + * link this program with the OpenSSL project's "OpenSSL" library (or with + * modified versions of it that use the same license as the "OpenSSL" library), + * and distribute the linked executables. You must obey the GNU General Public + * License in all respects for all of the code used other than "OpenSSL". If you + * modify file(s), you may extend this exception to your version of the file(s), + * but you are not obligated to do so. If you do not wish to do so, delete this + * exception statement from your version. + */ + +#ifndef QBT_GUI_UTILS_PLASMACOLORSCHEME_H +#define QBT_GUI_UTILS_PLASMACOLORSCHEME_H + +#include +#include +#include +#include + +class QSettings; + +class PlasmaColorScheme +{ + Q_GADGET + +public: + struct Effect + { + enum ColorEffect + { + ColorEffectNone = 0, + ColorEffectDesaturate, + ColorEffectFade, + ColorEffectTint + }; + + enum IntensityEffect + { + IntensityEffectNone = 0, + IntensityEffectShade, + IntensityEffectDarken, + IntensityEffectLighten + }; + + enum ContrastEffect + { + ContrastEffectNone = 0, + ContrastEffectFade, + ContrastEffectTint + }; + + bool changeSelectionColor; + QColor color; + qreal colorAmount; // [-1:1] + ColorEffect colorEffect; + qreal contrastAmount; // [-1:1] + ContrastEffect contrastEffect; + bool enable; + qreal intensityAmount; // [-1:1] + IntensityEffect intensityEffect; + }; + + enum ColorRole + { + BackgroundAlternate, + BackgroundNormal, + DecorationFocus, + DecorationHover, + ForegroundActive, + ForegroundInactive, + ForegroundLink, + ForegroundNegative, + ForegroundNeutral, + ForegroundNormal, + ForegroundPositive, + ForegroundVisited + }; + + Q_ENUM(ColorRole) + + enum ColorSet + { + /** + * Views; for example, frames, input fields, etc. + * + * If it contains things that can be selected, it is probably a View. + */ + View, + /** + * Non-editable window elements; for example, menus. + * + * If it isn't a Button, View, or Tooltip, it is probably a Window. + */ + Window, + /** + * Buttons and button-like controls. + * + * In addition to buttons, "button-like" controls such as non-editable + * dropdowns, scrollbar sliders, slider handles, etc. should also use + * this role. + */ + Button, + /** + * Selected items in views. + * + * Note that unfocused or disabled selections should use the Window + * role. This makes it more obvious to the user that the view + * containing the selection does not have input focus. + */ + Selection, + /** + * Tooltips. + * + * The tooltip set can often be substituted for the view + * set when editing is not possible, but the Window set is deemed + * inappropriate. "What's This" help is an excellent example, another + * might be pop-up notifications (depending on taste). + */ + Tooltip + }; + + Q_ENUM(ColorSet) + + PlasmaColorScheme(const QProcessEnvironment& env = QProcessEnvironment::systemEnvironment()); + void reload(const QProcessEnvironment& env = QProcessEnvironment::systemEnvironment()); + + static const PlasmaColorScheme* instance(); + + QColor color(ColorRole role, QPalette::ColorGroup group = QPalette::Active, ColorSet set = View) const; + QColor disabledColor(const QColor& color) const; + QColor inactiveColor(const QColor& color) const; + + bool wasLoadedSuccesfully() const; + +private: + using ColorsMap = QMap; + using ColorsSet = QMap; + using Colors = QMap; + + void load(const QProcessEnvironment& env = QProcessEnvironment::systemEnvironment()); + void setFallbackColors(); + static ColorsSet fallbackColorSet(QPalette::ColorGroup group); + + static ColorsMap readSet(QSettings& settings, ColorSet set); + static ColorsSet applyEffect(const ColorsSet &set, const Effect& effect); + + static QString locateColorThemeFile(const QString &themeName); + + Colors m_colors; + Effect m_disabledEffect; + Effect m_inactiveEffect; + bool m_loadedSuccesfully; +}; + +#endif // QBT_GUI_UTILS_PLASMACOLORSCHEME_H diff --git a/src/icons.qrc b/src/icons.qrc index 257b9813af..0696432143 100644 --- a/src/icons.qrc +++ b/src/icons.qrc @@ -242,108 +242,111 @@ icons/flags/za.png icons/flags/zm.png icons/flags/zw.png - icons/L.gif - icons/loading.png - icons/qbittorrent.png - icons/qbt-theme/application-exit.png - icons/qbt-theme/application-rss+xml.png - icons/qbt-theme/application-x-mswinurl.png - icons/qbt-theme/checked.png - icons/qbt-theme/configure.png - icons/qbt-theme/dialog-cancel.png - icons/qbt-theme/dialog-information.png - icons/qbt-theme/dialog-warning.png - icons/qbt-theme/document-edit-verify.png - icons/qbt-theme/document-edit.png - icons/qbt-theme/document-encrypt.png - icons/qbt-theme/document-import.png - icons/qbt-theme/document-new.png - icons/qbt-theme/document-properties.png - icons/qbt-theme/document-save.png - icons/qbt-theme/download.png - icons/qbt-theme/edit-clear-history.png - icons/qbt-theme/edit-clear.png - icons/qbt-theme/edit-copy.png - icons/qbt-theme/edit-cut.png - icons/qbt-theme/edit-delete.png - icons/qbt-theme/edit-find-user.png - icons/qbt-theme/edit-find.png - icons/qbt-theme/edit-paste.png - icons/qbt-theme/edit-rename.png - icons/qbt-theme/folder-documents.png - icons/qbt-theme/folder-download.png - icons/qbt-theme/folder-new.png - icons/qbt-theme/folder-remote.png - icons/qbt-theme/gear.png - icons/qbt-theme/gear32.png - icons/qbt-theme/go-bottom.png - icons/qbt-theme/go-down.png - icons/qbt-theme/go-top.png - icons/qbt-theme/go-up.png - icons/qbt-theme/help-about.png - icons/qbt-theme/help-contents.png - icons/qbt-theme/inode-directory.png - icons/qbt-theme/insert-link.png - icons/qbt-theme/kt-magnet.png - icons/qbt-theme/kt-set-max-download-speed.png - icons/qbt-theme/kt-set-max-upload-speed.png - icons/qbt-theme/list-add.png - icons/qbt-theme/list-remove.png - icons/qbt-theme/mail-folder-inbox.png - icons/qbt-theme/mail-mark-read.png - icons/qbt-theme/media-playback-pause.png - icons/qbt-theme/media-playback-start.png - icons/qbt-theme/media-seek-forward.png - icons/qbt-theme/network-server.png - icons/qbt-theme/network-wired.png - icons/qbt-theme/object-locked.png - icons/qbt-theme/office-chart-line.png - icons/qbt-theme/preferences-desktop.png - icons/qbt-theme/preferences-other.png - icons/qbt-theme/preferences-system-network.png - icons/qbt-theme/preferences-web-browser-cookies.png - icons/qbt-theme/rss-config.png - icons/qbt-theme/security-high.png - icons/qbt-theme/security-low.png - icons/qbt-theme/services.png - icons/qbt-theme/speedometer.png - icons/qbt-theme/system-log-out.png - icons/qbt-theme/tab-close.png - icons/qbt-theme/task-attention.png - icons/qbt-theme/task-complete.png - icons/qbt-theme/task-ongoing.png - icons/qbt-theme/task-reject.png - icons/qbt-theme/text-plain.png - icons/qbt-theme/tools-report-bug.png - icons/qbt-theme/unavailable.png - icons/qbt-theme/user-group-delete.png - icons/qbt-theme/user-group-new.png - icons/qbt-theme/view-calendar-journal.png - icons/qbt-theme/view-categories.png - icons/qbt-theme/view-filter.png - icons/qbt-theme/view-preview.png - icons/qbt-theme/view-refresh.png - icons/qbt-theme/view-statistics.png - icons/qbt-theme/wallet-open.png - icons/qbt-theme/webui.png + icons/qbt-theme/application-exit.svg + icons/qbt-theme/application-rss+xml.svg + icons/qbt-theme/application-x-mswinurl.svg + icons/qbt-theme/cloud-download.svg + icons/qbt-theme/cloud-upload.svg + icons/qbt-theme/configure.svg + icons/qbt-theme/dialog-cancel.svg + icons/qbt-theme/dialog-information.svg + icons/qbt-theme/dialog-warning.svg + icons/qbt-theme/document-edit-verify.svg + icons/qbt-theme/document-edit.svg + icons/qbt-theme/document-encrypt.svg + icons/qbt-theme/document-import.svg + icons/qbt-theme/document-new.svg + icons/qbt-theme/document-properties.svg + icons/qbt-theme/document-save.svg + icons/qbt-theme/download.svg + icons/qbt-theme/edit-clear-history.svg + icons/qbt-theme/edit-clear.svg + icons/qbt-theme/edit-copy.svg + icons/qbt-theme/edit-cut.svg + icons/qbt-theme/edit-delete.svg + icons/qbt-theme/edit-find-user.svg + icons/qbt-theme/edit-find.svg + icons/qbt-theme/edit-paste.svg + icons/qbt-theme/edit-rename.svg + icons/qbt-theme/folder-documents.svg + icons/qbt-theme/folder-download.svg + icons/qbt-theme/folder-new.svg + icons/qbt-theme/folder-remote.svg + icons/qbt-theme/gear.svg + icons/qbt-theme/gear32.svg + icons/qbt-theme/go-down.svg + icons/qbt-theme/go-up.svg + icons/qbt-theme/help-about.svg + icons/qbt-theme/help-contents.svg + icons/qbt-theme/inode-directory.svg + icons/qbt-theme/insert-link.svg + icons/qbt-theme/kt-magnet.svg + icons/qbt-theme/kt-set-max-download-speed.svg + icons/qbt-theme/kt-set-max-upload-speed.svg + icons/qbt-theme/list-add.svg + icons/qbt-theme/list-remove.svg + icons/qbt-theme/mail-folder-inbox.svg + icons/qbt-theme/mail-mark-read.svg + icons/qbt-theme/media-playback-pause.svg + icons/qbt-theme/media-playback-start.svg + icons/qbt-theme/media-seek-forward.svg + icons/qbt-theme/network-server.svg + icons/qbt-theme/network-wired.svg + icons/qbt-theme/object-locked.svg + icons/qbt-theme/preferences-desktop.svg + icons/qbt-theme/preferences-desktop-theme.svg + icons/qbt-theme/preferences-other.svg + icons/qbt-theme/preferences-system-network.svg + icons/qbt-theme/preferences-web-browser-cookies.svg + icons/qbt-theme/security-high.svg + icons/qbt-theme/security-low.svg + icons/qbt-theme/services.svg + icons/qbt-theme/speedometer.svg + icons/qbt-theme/system-log-out.svg + icons/qbt-theme/go-bottom.svg + icons/qbt-theme/go-top.svg + icons/qbt-theme/checked.svg + icons/qbt-theme/office-chart-line.svg + icons/qbt-theme/rss-config.svg + icons/qbt-theme/state-download.svg + icons/qbt-theme/state-error.svg + icons/qbt-theme/state-information.svg + icons/qbt-theme/state-offline.svg + icons/qbt-theme/state-ok.svg + icons/qbt-theme/state-pause.svg + icons/qbt-theme/state-sync.svg + icons/qbt-theme/state-warning.svg + icons/qbt-theme/tab-close.svg + icons/qbt-theme/task-attention.svg + icons/qbt-theme/text-plain.svg + icons/qbt-theme/tools-report-bug.svg + icons/qbt-theme/unavailable.svg + icons/qbt-theme/user-group-delete.svg + icons/qbt-theme/user-group-new.svg + icons/qbt-theme/view-calendar-journal.svg + icons/qbt-theme/view-categories.svg + icons/qbt-theme/view-filter.svg + icons/qbt-theme/view-preview.svg + icons/qbt-theme/view-refresh.svg + icons/qbt-theme/view-statistics.svg + icons/qbt-theme/wallet-open.svg + icons/qbt-theme/webui.svg icons/skin/arrow-right.gif icons/skin/bg-dropdown.gif icons/skin/bg-handle-horizontal.gif icons/skin/bg-header.gif icons/skin/bg-panel-header.gif - icons/skin/checking.png + icons/skin/checking.svg icons/skin/collapse-expand.gif - icons/skin/completed.png - icons/skin/connected.png - icons/skin/disconnected.png + icons/skin/connected.svg + icons/skin/disconnected.svg icons/skin/dock-tabs.gif - icons/skin/download.png - icons/skin/downloading.png - icons/skin/error.png - icons/skin/filteractive.png - icons/skin/filterall.png - icons/skin/filterinactive.png - icons/skin/firewalled.png + icons/skin/downloading.svg + icons/skin/error.svg + icons/skin/filteractive.svg + icons/skin/filterall.svg + icons/skin/filterinactive.svg + icons/skin/firewalled.svg icons/skin/handle-icon-horizontal.gif icons/skin/handle-icon.gif icons/skin/knob.gif @@ -351,31 +354,29 @@ icons/skin/logo.gif icons/skin/logo2.gif icons/skin/mascot.png - icons/skin/paused.png - icons/skin/qbittorrent-tray-dark.svg - icons/skin/qbittorrent-tray-light.svg + icons/skin/paused.svg icons/skin/qbittorrent16.png icons/skin/qbittorrent22.png icons/skin/qbittorrent32.png - icons/skin/queued.png - icons/skin/ratio.png - icons/skin/resumed.png - icons/skin/seeding.png + icons/skin/qbittorrent-tray-dark.svg + icons/skin/qbittorrent-tray-light.svg + icons/skin/queued.svg + icons/skin/ratio.svg icons/skin/slider-area.gif icons/skin/spacer.gif icons/skin/spinner-placeholder.gif icons/skin/spinner.gif icons/skin/splash.png - icons/skin/stalledDL.png - icons/skin/stalledUP.png + icons/skin/stalledDL.svg + icons/skin/stalledUP.svg icons/skin/tabs.gif icons/skin/toolbox-divider.gif icons/skin/toolbox-divider2.gif - icons/skin/uploading.png - icons/slow.png - icons/slow_off.png icons/sphere.png icons/sphere2.png icons/url.png + icons/skin/resumed.svg + icons/skin/uploading.svg + icons/skin/completed.svg diff --git a/src/icons/qbt-theme/application-exit.png b/src/icons/qbt-theme/application-exit.png deleted file mode 100644 index f88e000867..0000000000 Binary files a/src/icons/qbt-theme/application-exit.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/application-exit.svg b/src/icons/qbt-theme/application-exit.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/application-exit.svg rename to src/icons/qbt-theme/application-exit.svg diff --git a/src/icons/qbt-theme/application-rss+xml.png b/src/icons/qbt-theme/application-rss+xml.png deleted file mode 100644 index 7efeb3220d..0000000000 Binary files a/src/icons/qbt-theme/application-rss+xml.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/application-rss+xml.svg b/src/icons/qbt-theme/application-rss+xml.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/application-rss+xml.svg rename to src/icons/qbt-theme/application-rss+xml.svg diff --git a/src/icons/qbt-theme/application-x-mswinurl.png b/src/icons/qbt-theme/application-x-mswinurl.png deleted file mode 100644 index 95f49c94d5..0000000000 Binary files a/src/icons/qbt-theme/application-x-mswinurl.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/application-x-mswinurl.svg b/src/icons/qbt-theme/application-x-mswinurl.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/application-x-mswinurl.svg rename to src/icons/qbt-theme/application-x-mswinurl.svg diff --git a/src/icons/qbt-theme/build-icons/Gruntfile.js b/src/icons/qbt-theme/build-icons/Gruntfile.js deleted file mode 100644 index 2a88a46395..0000000000 --- a/src/icons/qbt-theme/build-icons/Gruntfile.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = function(grunt) { - - grunt.initConfig({ - svg2png: { - all: { - options:{ - size: 256 - }, - files: [ - { - src: ['icons/*.svg'] - } - ] - } - } - }); - - grunt.loadNpmTasks('grunt-svg2png'); - - grunt.registerTask('default', ['svg2png']); - -} \ No newline at end of file diff --git a/src/icons/qbt-theme/build-icons/icons/insert-link.svg b/src/icons/qbt-theme/build-icons/icons/insert-link.svg deleted file mode 100644 index a4be2d0088..0000000000 --- a/src/icons/qbt-theme/build-icons/icons/insert-link.svg +++ /dev/null @@ -1,5 +0,0 @@ - - - - - diff --git a/src/icons/qbt-theme/build-icons/package.json b/src/icons/qbt-theme/build-icons/package.json deleted file mode 100644 index c4e5b5ca34..0000000000 --- a/src/icons/qbt-theme/build-icons/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "build-icons", - "version": "1.0.0", - "description": "Convert svg icons to png", - "main": "index.js", - "dependencies": {}, - "devDependencies": { - "grunt": "^0.4.5", - "grunt-svg2png": "git+https://git@github.com/bertyhell/grunt-svg2png.git" - }, - "author": "Bert Verhelst", - "license": "ISC" -} diff --git a/src/icons/qbt-theme/build-icons/readme.md b/src/icons/qbt-theme/build-icons/readme.md deleted file mode 100644 index 97c3fd44f9..0000000000 --- a/src/icons/qbt-theme/build-icons/readme.md +++ /dev/null @@ -1,14 +0,0 @@ -Convert SVG icons to PNG ------------------------- - -install npm - -Execute: -``` -npm install -``` - -Convert icons by running: -``` -grunt -``` \ No newline at end of file diff --git a/src/icons/qbt-theme/checked.png b/src/icons/qbt-theme/checked.png deleted file mode 100644 index 79f7c0d00e..0000000000 Binary files a/src/icons/qbt-theme/checked.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/checked.svg b/src/icons/qbt-theme/checked.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/checked.svg rename to src/icons/qbt-theme/checked.svg diff --git a/src/icons/skin/build-icons/icons/download.svg b/src/icons/qbt-theme/cloud-download.svg similarity index 100% rename from src/icons/skin/build-icons/icons/download.svg rename to src/icons/qbt-theme/cloud-download.svg diff --git a/src/icons/skin/build-icons/icons/seeding.svg b/src/icons/qbt-theme/cloud-upload.svg similarity index 100% rename from src/icons/skin/build-icons/icons/seeding.svg rename to src/icons/qbt-theme/cloud-upload.svg diff --git a/src/icons/qbt-theme/configure.png b/src/icons/qbt-theme/configure.png deleted file mode 100644 index a4459360b8..0000000000 Binary files a/src/icons/qbt-theme/configure.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/configure.svg b/src/icons/qbt-theme/configure.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/configure.svg rename to src/icons/qbt-theme/configure.svg diff --git a/src/icons/qbt-theme/dialog-cancel.png b/src/icons/qbt-theme/dialog-cancel.png deleted file mode 100644 index 8127f72283..0000000000 Binary files a/src/icons/qbt-theme/dialog-cancel.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/dialog-cancel.svg b/src/icons/qbt-theme/dialog-cancel.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/dialog-cancel.svg rename to src/icons/qbt-theme/dialog-cancel.svg diff --git a/src/icons/qbt-theme/dialog-information.png b/src/icons/qbt-theme/dialog-information.png deleted file mode 100644 index ab4dfa923b..0000000000 Binary files a/src/icons/qbt-theme/dialog-information.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/dialog-information.svg b/src/icons/qbt-theme/dialog-information.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/dialog-information.svg rename to src/icons/qbt-theme/dialog-information.svg diff --git a/src/icons/qbt-theme/dialog-warning.png b/src/icons/qbt-theme/dialog-warning.png deleted file mode 100644 index b92e0cfd67..0000000000 Binary files a/src/icons/qbt-theme/dialog-warning.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/dialog-warning.svg b/src/icons/qbt-theme/dialog-warning.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/dialog-warning.svg rename to src/icons/qbt-theme/dialog-warning.svg diff --git a/src/icons/qbt-theme/document-edit-verify.png b/src/icons/qbt-theme/document-edit-verify.png deleted file mode 100644 index 515fe69552..0000000000 Binary files a/src/icons/qbt-theme/document-edit-verify.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/document-edit-verify.svg b/src/icons/qbt-theme/document-edit-verify.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/document-edit-verify.svg rename to src/icons/qbt-theme/document-edit-verify.svg diff --git a/src/icons/qbt-theme/document-edit.png b/src/icons/qbt-theme/document-edit.png deleted file mode 100644 index b4eb6fcac2..0000000000 Binary files a/src/icons/qbt-theme/document-edit.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/document-edit.svg b/src/icons/qbt-theme/document-edit.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/document-edit.svg rename to src/icons/qbt-theme/document-edit.svg diff --git a/src/icons/qbt-theme/document-encrypt.png b/src/icons/qbt-theme/document-encrypt.png deleted file mode 100644 index 39ac5c86fc..0000000000 Binary files a/src/icons/qbt-theme/document-encrypt.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/document-encrypt.svg b/src/icons/qbt-theme/document-encrypt.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/document-encrypt.svg rename to src/icons/qbt-theme/document-encrypt.svg diff --git a/src/icons/qbt-theme/document-import.png b/src/icons/qbt-theme/document-import.png deleted file mode 100644 index a136848322..0000000000 Binary files a/src/icons/qbt-theme/document-import.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/document-import.svg b/src/icons/qbt-theme/document-import.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/document-import.svg rename to src/icons/qbt-theme/document-import.svg diff --git a/src/icons/qbt-theme/document-new.png b/src/icons/qbt-theme/document-new.png deleted file mode 100644 index 40bded949c..0000000000 Binary files a/src/icons/qbt-theme/document-new.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/document-new.svg b/src/icons/qbt-theme/document-new.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/document-new.svg rename to src/icons/qbt-theme/document-new.svg diff --git a/src/icons/qbt-theme/document-properties.png b/src/icons/qbt-theme/document-properties.png deleted file mode 100644 index 38c00a3b1a..0000000000 Binary files a/src/icons/qbt-theme/document-properties.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/document-properties.svg b/src/icons/qbt-theme/document-properties.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/document-properties.svg rename to src/icons/qbt-theme/document-properties.svg diff --git a/src/icons/qbt-theme/document-save.png b/src/icons/qbt-theme/document-save.png deleted file mode 100644 index b88902a7ac..0000000000 Binary files a/src/icons/qbt-theme/document-save.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/document-save.svg b/src/icons/qbt-theme/document-save.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/document-save.svg rename to src/icons/qbt-theme/document-save.svg diff --git a/src/icons/qbt-theme/download.png b/src/icons/qbt-theme/download.png deleted file mode 100644 index 000e2d7304..0000000000 Binary files a/src/icons/qbt-theme/download.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/download.svg b/src/icons/qbt-theme/download.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/download.svg rename to src/icons/qbt-theme/download.svg diff --git a/src/icons/qbt-theme/edit-clear-history.png b/src/icons/qbt-theme/edit-clear-history.png deleted file mode 100644 index aca749b417..0000000000 Binary files a/src/icons/qbt-theme/edit-clear-history.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/edit-clear-history.svg b/src/icons/qbt-theme/edit-clear-history.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/edit-clear-history.svg rename to src/icons/qbt-theme/edit-clear-history.svg diff --git a/src/icons/qbt-theme/edit-clear.png b/src/icons/qbt-theme/edit-clear.png deleted file mode 100644 index a04529bcdd..0000000000 Binary files a/src/icons/qbt-theme/edit-clear.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/edit-clear.svg b/src/icons/qbt-theme/edit-clear.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/edit-clear.svg rename to src/icons/qbt-theme/edit-clear.svg diff --git a/src/icons/qbt-theme/edit-copy.png b/src/icons/qbt-theme/edit-copy.png deleted file mode 100644 index f78e267e35..0000000000 Binary files a/src/icons/qbt-theme/edit-copy.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/edit-copy.svg b/src/icons/qbt-theme/edit-copy.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/edit-copy.svg rename to src/icons/qbt-theme/edit-copy.svg diff --git a/src/icons/qbt-theme/edit-cut.png b/src/icons/qbt-theme/edit-cut.png deleted file mode 100644 index 61eaf4c15d..0000000000 Binary files a/src/icons/qbt-theme/edit-cut.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/edit-cut.svg b/src/icons/qbt-theme/edit-cut.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/edit-cut.svg rename to src/icons/qbt-theme/edit-cut.svg diff --git a/src/icons/qbt-theme/edit-delete.png b/src/icons/qbt-theme/edit-delete.png deleted file mode 100644 index f88e000867..0000000000 Binary files a/src/icons/qbt-theme/edit-delete.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/edit-delete.svg b/src/icons/qbt-theme/edit-delete.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/edit-delete.svg rename to src/icons/qbt-theme/edit-delete.svg diff --git a/src/icons/qbt-theme/edit-find-user.png b/src/icons/qbt-theme/edit-find-user.png deleted file mode 100644 index 378a98fcba..0000000000 Binary files a/src/icons/qbt-theme/edit-find-user.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/edit-find-user.svg b/src/icons/qbt-theme/edit-find-user.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/edit-find-user.svg rename to src/icons/qbt-theme/edit-find-user.svg diff --git a/src/icons/qbt-theme/edit-find.png b/src/icons/qbt-theme/edit-find.png deleted file mode 100644 index 522f30c9d7..0000000000 Binary files a/src/icons/qbt-theme/edit-find.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/edit-find.svg b/src/icons/qbt-theme/edit-find.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/edit-find.svg rename to src/icons/qbt-theme/edit-find.svg diff --git a/src/icons/qbt-theme/edit-paste.png b/src/icons/qbt-theme/edit-paste.png deleted file mode 100644 index eb73dbe8a4..0000000000 Binary files a/src/icons/qbt-theme/edit-paste.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/edit-paste.svg b/src/icons/qbt-theme/edit-paste.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/edit-paste.svg rename to src/icons/qbt-theme/edit-paste.svg diff --git a/src/icons/qbt-theme/edit-rename.png b/src/icons/qbt-theme/edit-rename.png deleted file mode 100644 index c8090e019a..0000000000 Binary files a/src/icons/qbt-theme/edit-rename.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/edit-rename.svg b/src/icons/qbt-theme/edit-rename.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/edit-rename.svg rename to src/icons/qbt-theme/edit-rename.svg diff --git a/src/icons/qbt-theme/folder-documents.png b/src/icons/qbt-theme/folder-documents.png deleted file mode 100644 index 74efc06456..0000000000 Binary files a/src/icons/qbt-theme/folder-documents.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/folder-documents.svg b/src/icons/qbt-theme/folder-documents.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/folder-documents.svg rename to src/icons/qbt-theme/folder-documents.svg diff --git a/src/icons/qbt-theme/folder-download.png b/src/icons/qbt-theme/folder-download.png deleted file mode 100644 index 000e2d7304..0000000000 Binary files a/src/icons/qbt-theme/folder-download.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/folder-download.svg b/src/icons/qbt-theme/folder-download.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/folder-download.svg rename to src/icons/qbt-theme/folder-download.svg diff --git a/src/icons/qbt-theme/folder-new.png b/src/icons/qbt-theme/folder-new.png deleted file mode 100644 index b187c4d113..0000000000 Binary files a/src/icons/qbt-theme/folder-new.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/folder-new.svg b/src/icons/qbt-theme/folder-new.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/folder-new.svg rename to src/icons/qbt-theme/folder-new.svg diff --git a/src/icons/qbt-theme/folder-remote.png b/src/icons/qbt-theme/folder-remote.png deleted file mode 100644 index 75a00291c8..0000000000 Binary files a/src/icons/qbt-theme/folder-remote.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/folder-remote.svg b/src/icons/qbt-theme/folder-remote.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/folder-remote.svg rename to src/icons/qbt-theme/folder-remote.svg diff --git a/src/icons/qbt-theme/gear.png b/src/icons/qbt-theme/gear.png deleted file mode 100644 index a4459360b8..0000000000 Binary files a/src/icons/qbt-theme/gear.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/gear.svg b/src/icons/qbt-theme/gear.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/gear.svg rename to src/icons/qbt-theme/gear.svg diff --git a/src/icons/qbt-theme/gear32.png b/src/icons/qbt-theme/gear32.png deleted file mode 100644 index a4459360b8..0000000000 Binary files a/src/icons/qbt-theme/gear32.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/gear32.svg b/src/icons/qbt-theme/gear32.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/gear32.svg rename to src/icons/qbt-theme/gear32.svg diff --git a/src/icons/qbt-theme/go-bottom.png b/src/icons/qbt-theme/go-bottom.png deleted file mode 100644 index bc345e4492..0000000000 Binary files a/src/icons/qbt-theme/go-bottom.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/go-bottom.svg b/src/icons/qbt-theme/go-bottom.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/go-bottom.svg rename to src/icons/qbt-theme/go-bottom.svg diff --git a/src/icons/qbt-theme/go-down.png b/src/icons/qbt-theme/go-down.png deleted file mode 100644 index cdce866876..0000000000 Binary files a/src/icons/qbt-theme/go-down.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/go-down.svg b/src/icons/qbt-theme/go-down.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/go-down.svg rename to src/icons/qbt-theme/go-down.svg diff --git a/src/icons/qbt-theme/go-top.png b/src/icons/qbt-theme/go-top.png deleted file mode 100644 index a6cbbbba03..0000000000 Binary files a/src/icons/qbt-theme/go-top.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/go-top.svg b/src/icons/qbt-theme/go-top.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/go-top.svg rename to src/icons/qbt-theme/go-top.svg diff --git a/src/icons/qbt-theme/go-up.png b/src/icons/qbt-theme/go-up.png deleted file mode 100644 index 37827553ee..0000000000 Binary files a/src/icons/qbt-theme/go-up.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/go-up.svg b/src/icons/qbt-theme/go-up.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/go-up.svg rename to src/icons/qbt-theme/go-up.svg diff --git a/src/icons/qbt-theme/help-about.png b/src/icons/qbt-theme/help-about.png deleted file mode 100644 index ab4dfa923b..0000000000 Binary files a/src/icons/qbt-theme/help-about.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/help-about.svg b/src/icons/qbt-theme/help-about.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/help-about.svg rename to src/icons/qbt-theme/help-about.svg diff --git a/src/icons/qbt-theme/help-contents.png b/src/icons/qbt-theme/help-contents.png deleted file mode 100644 index 5e4daff56f..0000000000 Binary files a/src/icons/qbt-theme/help-contents.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/help-contents.svg b/src/icons/qbt-theme/help-contents.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/help-contents.svg rename to src/icons/qbt-theme/help-contents.svg diff --git a/src/icons/qbt-theme/inode-directory.png b/src/icons/qbt-theme/inode-directory.png deleted file mode 100644 index 74efc06456..0000000000 Binary files a/src/icons/qbt-theme/inode-directory.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/inode-directory.svg b/src/icons/qbt-theme/inode-directory.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/inode-directory.svg rename to src/icons/qbt-theme/inode-directory.svg diff --git a/src/icons/qbt-theme/insert-link.png b/src/icons/qbt-theme/insert-link.png deleted file mode 100644 index 5edea8a44b..0000000000 Binary files a/src/icons/qbt-theme/insert-link.png and /dev/null differ diff --git a/src/icons/qbt-theme/insert-link.svg b/src/icons/qbt-theme/insert-link.svg new file mode 100644 index 0000000000..d4f4654b72 --- /dev/null +++ b/src/icons/qbt-theme/insert-link.svg @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + diff --git a/src/icons/qbt-theme/kt-magnet.png b/src/icons/qbt-theme/kt-magnet.png deleted file mode 100644 index 3bab5e4990..0000000000 Binary files a/src/icons/qbt-theme/kt-magnet.png and /dev/null differ diff --git a/src/icons/qbt-theme/kt-magnet.svg b/src/icons/qbt-theme/kt-magnet.svg new file mode 100644 index 0000000000..7927556b82 --- /dev/null +++ b/src/icons/qbt-theme/kt-magnet.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/icons/qbt-theme/kt-set-max-download-speed.png b/src/icons/qbt-theme/kt-set-max-download-speed.png deleted file mode 100644 index 2cd657da4d..0000000000 Binary files a/src/icons/qbt-theme/kt-set-max-download-speed.png and /dev/null differ diff --git a/src/icons/qbt-theme/kt-set-max-download-speed.svg b/src/icons/qbt-theme/kt-set-max-download-speed.svg new file mode 100644 index 0000000000..8529ca8150 --- /dev/null +++ b/src/icons/qbt-theme/kt-set-max-download-speed.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/icons/qbt-theme/kt-set-max-upload-speed.png b/src/icons/qbt-theme/kt-set-max-upload-speed.png deleted file mode 100644 index f5d14e556a..0000000000 Binary files a/src/icons/qbt-theme/kt-set-max-upload-speed.png and /dev/null differ diff --git a/src/icons/qbt-theme/kt-set-max-upload-speed.svg b/src/icons/qbt-theme/kt-set-max-upload-speed.svg new file mode 100644 index 0000000000..b12e9b8dc5 --- /dev/null +++ b/src/icons/qbt-theme/kt-set-max-upload-speed.svg @@ -0,0 +1,13 @@ + + + + + + diff --git a/src/icons/qbt-theme/list-add.png b/src/icons/qbt-theme/list-add.png deleted file mode 100644 index 40bded949c..0000000000 Binary files a/src/icons/qbt-theme/list-add.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/list-add.svg b/src/icons/qbt-theme/list-add.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/list-add.svg rename to src/icons/qbt-theme/list-add.svg diff --git a/src/icons/qbt-theme/list-remove.png b/src/icons/qbt-theme/list-remove.png deleted file mode 100644 index 2d2714e2e1..0000000000 Binary files a/src/icons/qbt-theme/list-remove.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/list-remove.svg b/src/icons/qbt-theme/list-remove.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/list-remove.svg rename to src/icons/qbt-theme/list-remove.svg diff --git a/src/icons/qbt-theme/mail-folder-inbox.png b/src/icons/qbt-theme/mail-folder-inbox.png deleted file mode 100644 index 7f3b6db51d..0000000000 Binary files a/src/icons/qbt-theme/mail-folder-inbox.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/mail-folder-inbox.svg b/src/icons/qbt-theme/mail-folder-inbox.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/mail-folder-inbox.svg rename to src/icons/qbt-theme/mail-folder-inbox.svg diff --git a/src/icons/qbt-theme/mail-mark-read.png b/src/icons/qbt-theme/mail-mark-read.png deleted file mode 100644 index e96d81f409..0000000000 Binary files a/src/icons/qbt-theme/mail-mark-read.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/mail-mark-read.svg b/src/icons/qbt-theme/mail-mark-read.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/mail-mark-read.svg rename to src/icons/qbt-theme/mail-mark-read.svg diff --git a/src/icons/qbt-theme/media-playback-pause.png b/src/icons/qbt-theme/media-playback-pause.png deleted file mode 100644 index 4f1e6e85b5..0000000000 Binary files a/src/icons/qbt-theme/media-playback-pause.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/media-playback-pause.svg b/src/icons/qbt-theme/media-playback-pause.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/media-playback-pause.svg rename to src/icons/qbt-theme/media-playback-pause.svg diff --git a/src/icons/qbt-theme/media-playback-start.png b/src/icons/qbt-theme/media-playback-start.png deleted file mode 100644 index bac2ea958c..0000000000 Binary files a/src/icons/qbt-theme/media-playback-start.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/media-playback-start.svg b/src/icons/qbt-theme/media-playback-start.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/media-playback-start.svg rename to src/icons/qbt-theme/media-playback-start.svg diff --git a/src/icons/qbt-theme/media-seek-forward.png b/src/icons/qbt-theme/media-seek-forward.png deleted file mode 100644 index 03b1f76f14..0000000000 Binary files a/src/icons/qbt-theme/media-seek-forward.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/media-seek-forward.svg b/src/icons/qbt-theme/media-seek-forward.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/media-seek-forward.svg rename to src/icons/qbt-theme/media-seek-forward.svg diff --git a/src/icons/qbt-theme/network-server.png b/src/icons/qbt-theme/network-server.png deleted file mode 100644 index a8f38a12ab..0000000000 Binary files a/src/icons/qbt-theme/network-server.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/network-server.svg b/src/icons/qbt-theme/network-server.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/network-server.svg rename to src/icons/qbt-theme/network-server.svg diff --git a/src/icons/qbt-theme/network-wired.png b/src/icons/qbt-theme/network-wired.png deleted file mode 100644 index 0fb3162034..0000000000 Binary files a/src/icons/qbt-theme/network-wired.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/network-wired.svg b/src/icons/qbt-theme/network-wired.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/network-wired.svg rename to src/icons/qbt-theme/network-wired.svg diff --git a/src/icons/qbt-theme/object-locked.png b/src/icons/qbt-theme/object-locked.png deleted file mode 100644 index 39ac5c86fc..0000000000 Binary files a/src/icons/qbt-theme/object-locked.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/object-locked.svg b/src/icons/qbt-theme/object-locked.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/object-locked.svg rename to src/icons/qbt-theme/object-locked.svg diff --git a/src/icons/qbt-theme/office-chart-line.png b/src/icons/qbt-theme/office-chart-line.png deleted file mode 100644 index afa1720be4..0000000000 Binary files a/src/icons/qbt-theme/office-chart-line.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/office-chart-line.svg b/src/icons/qbt-theme/office-chart-line.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/office-chart-line.svg rename to src/icons/qbt-theme/office-chart-line.svg diff --git a/src/icons/qbt-theme/preferences-desktop-theme.png b/src/icons/qbt-theme/preferences-desktop-theme.png new file mode 100644 index 0000000000..4c05fbb764 Binary files /dev/null and b/src/icons/qbt-theme/preferences-desktop-theme.png differ diff --git a/src/icons/qbt-theme/preferences-desktop-theme.svg b/src/icons/qbt-theme/preferences-desktop-theme.svg new file mode 100644 index 0000000000..64e1a906a5 --- /dev/null +++ b/src/icons/qbt-theme/preferences-desktop-theme.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/icons/qbt-theme/preferences-desktop.png b/src/icons/qbt-theme/preferences-desktop.png deleted file mode 100644 index 3991170dbb..0000000000 Binary files a/src/icons/qbt-theme/preferences-desktop.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/preferences-desktop.svg b/src/icons/qbt-theme/preferences-desktop.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/preferences-desktop.svg rename to src/icons/qbt-theme/preferences-desktop.svg diff --git a/src/icons/qbt-theme/preferences-other.png b/src/icons/qbt-theme/preferences-other.png deleted file mode 100644 index 70067b644b..0000000000 Binary files a/src/icons/qbt-theme/preferences-other.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/preferences-other.svg b/src/icons/qbt-theme/preferences-other.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/preferences-other.svg rename to src/icons/qbt-theme/preferences-other.svg diff --git a/src/icons/qbt-theme/preferences-system-network.png b/src/icons/qbt-theme/preferences-system-network.png deleted file mode 100644 index ad94a6b142..0000000000 Binary files a/src/icons/qbt-theme/preferences-system-network.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/preferences-system-network.svg b/src/icons/qbt-theme/preferences-system-network.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/preferences-system-network.svg rename to src/icons/qbt-theme/preferences-system-network.svg diff --git a/src/icons/qbt-theme/preferences-web-browser-cookies.png b/src/icons/qbt-theme/preferences-web-browser-cookies.png deleted file mode 100644 index 956587b97d..0000000000 Binary files a/src/icons/qbt-theme/preferences-web-browser-cookies.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/preferences-web-browser-cookies.svg b/src/icons/qbt-theme/preferences-web-browser-cookies.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/preferences-web-browser-cookies.svg rename to src/icons/qbt-theme/preferences-web-browser-cookies.svg diff --git a/src/icons/qbt-theme/rss-config.png b/src/icons/qbt-theme/rss-config.png deleted file mode 100644 index 20aba620b5..0000000000 Binary files a/src/icons/qbt-theme/rss-config.png and /dev/null differ diff --git a/src/icons/qbt-theme/rss-config.svg b/src/icons/qbt-theme/rss-config.svg new file mode 100644 index 0000000000..1388eed3b8 --- /dev/null +++ b/src/icons/qbt-theme/rss-config.svg @@ -0,0 +1,17 @@ + + + + + + + + diff --git a/src/icons/qbt-theme/security-high.png b/src/icons/qbt-theme/security-high.png deleted file mode 100644 index 74fc5c7066..0000000000 Binary files a/src/icons/qbt-theme/security-high.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/security-high.svg b/src/icons/qbt-theme/security-high.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/security-high.svg rename to src/icons/qbt-theme/security-high.svg diff --git a/src/icons/qbt-theme/security-low.png b/src/icons/qbt-theme/security-low.png deleted file mode 100644 index 01b3ca9e0b..0000000000 Binary files a/src/icons/qbt-theme/security-low.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/security-low.svg b/src/icons/qbt-theme/security-low.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/security-low.svg rename to src/icons/qbt-theme/security-low.svg diff --git a/src/icons/qbt-theme/services.png b/src/icons/qbt-theme/services.png deleted file mode 100644 index dc3fdae8f4..0000000000 Binary files a/src/icons/qbt-theme/services.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/services.svg b/src/icons/qbt-theme/services.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/services.svg rename to src/icons/qbt-theme/services.svg diff --git a/src/icons/qbt-theme/speedometer.png b/src/icons/qbt-theme/speedometer.png deleted file mode 100644 index 02a8f90861..0000000000 Binary files a/src/icons/qbt-theme/speedometer.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/speedometer.svg b/src/icons/qbt-theme/speedometer.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/speedometer.svg rename to src/icons/qbt-theme/speedometer.svg diff --git a/src/icons/qbt-theme/state-download.svg b/src/icons/qbt-theme/state-download.svg new file mode 100644 index 0000000000..7b9c8a20eb --- /dev/null +++ b/src/icons/qbt-theme/state-download.svg @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/src/icons/qbt-theme/state-error.svg b/src/icons/qbt-theme/state-error.svg new file mode 100644 index 0000000000..5291d9d01b --- /dev/null +++ b/src/icons/qbt-theme/state-error.svg @@ -0,0 +1,30 @@ + + + + + + + + + + diff --git a/src/icons/qbt-theme/state-information.svg b/src/icons/qbt-theme/state-information.svg new file mode 100644 index 0000000000..a16cfee931 --- /dev/null +++ b/src/icons/qbt-theme/state-information.svg @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/src/icons/qbt-theme/state-offline.svg b/src/icons/qbt-theme/state-offline.svg new file mode 100644 index 0000000000..185e69ffd4 --- /dev/null +++ b/src/icons/qbt-theme/state-offline.svg @@ -0,0 +1,23 @@ + + + + + + + + diff --git a/src/icons/qbt-theme/state-ok.svg b/src/icons/qbt-theme/state-ok.svg new file mode 100644 index 0000000000..0f49bbf869 --- /dev/null +++ b/src/icons/qbt-theme/state-ok.svg @@ -0,0 +1,27 @@ + + + + + + + + + + diff --git a/src/icons/qbt-theme/state-pause.svg b/src/icons/qbt-theme/state-pause.svg new file mode 100644 index 0000000000..b9b551815a --- /dev/null +++ b/src/icons/qbt-theme/state-pause.svg @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/src/icons/qbt-theme/state-sync.svg b/src/icons/qbt-theme/state-sync.svg new file mode 100644 index 0000000000..2f7ef018a8 --- /dev/null +++ b/src/icons/qbt-theme/state-sync.svg @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/src/icons/qbt-theme/state-warning.svg b/src/icons/qbt-theme/state-warning.svg new file mode 100644 index 0000000000..a951dad807 --- /dev/null +++ b/src/icons/qbt-theme/state-warning.svg @@ -0,0 +1,25 @@ + + + + + + + + diff --git a/src/icons/qbt-theme/system-log-out.png b/src/icons/qbt-theme/system-log-out.png deleted file mode 100644 index da48443091..0000000000 Binary files a/src/icons/qbt-theme/system-log-out.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/system-log-out.svg b/src/icons/qbt-theme/system-log-out.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/system-log-out.svg rename to src/icons/qbt-theme/system-log-out.svg diff --git a/src/icons/qbt-theme/tab-close.png b/src/icons/qbt-theme/tab-close.png deleted file mode 100644 index 627f4ac919..0000000000 Binary files a/src/icons/qbt-theme/tab-close.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/tab-close.svg b/src/icons/qbt-theme/tab-close.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/tab-close.svg rename to src/icons/qbt-theme/tab-close.svg diff --git a/src/icons/qbt-theme/task-attention.png b/src/icons/qbt-theme/task-attention.png deleted file mode 100644 index b92e0cfd67..0000000000 Binary files a/src/icons/qbt-theme/task-attention.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/task-attention.svg b/src/icons/qbt-theme/task-attention.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/task-attention.svg rename to src/icons/qbt-theme/task-attention.svg diff --git a/src/icons/qbt-theme/task-complete.png b/src/icons/qbt-theme/task-complete.png deleted file mode 100644 index b54b0aefa9..0000000000 Binary files a/src/icons/qbt-theme/task-complete.png and /dev/null differ diff --git a/src/icons/qbt-theme/task-ongoing.png b/src/icons/qbt-theme/task-ongoing.png deleted file mode 100644 index 7adb1afd26..0000000000 Binary files a/src/icons/qbt-theme/task-ongoing.png and /dev/null differ diff --git a/src/icons/qbt-theme/task-reject.png b/src/icons/qbt-theme/task-reject.png deleted file mode 100644 index f165fe0d87..0000000000 Binary files a/src/icons/qbt-theme/task-reject.png and /dev/null differ diff --git a/src/icons/qbt-theme/text-plain.png b/src/icons/qbt-theme/text-plain.png deleted file mode 100644 index 7aa62fd709..0000000000 Binary files a/src/icons/qbt-theme/text-plain.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/text-plain.svg b/src/icons/qbt-theme/text-plain.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/text-plain.svg rename to src/icons/qbt-theme/text-plain.svg diff --git a/src/icons/qbt-theme/tools-report-bug.png b/src/icons/qbt-theme/tools-report-bug.png deleted file mode 100644 index 9ddfb0b4da..0000000000 Binary files a/src/icons/qbt-theme/tools-report-bug.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/tools-report-bug.svg b/src/icons/qbt-theme/tools-report-bug.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/tools-report-bug.svg rename to src/icons/qbt-theme/tools-report-bug.svg diff --git a/src/icons/qbt-theme/unavailable.png b/src/icons/qbt-theme/unavailable.png deleted file mode 100644 index 0eeeef8ea3..0000000000 Binary files a/src/icons/qbt-theme/unavailable.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/unavailable.svg b/src/icons/qbt-theme/unavailable.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/unavailable.svg rename to src/icons/qbt-theme/unavailable.svg diff --git a/src/icons/qbt-theme/user-group-delete.png b/src/icons/qbt-theme/user-group-delete.png deleted file mode 100644 index c22fb6c62b..0000000000 Binary files a/src/icons/qbt-theme/user-group-delete.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/user-group-delete.svg b/src/icons/qbt-theme/user-group-delete.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/user-group-delete.svg rename to src/icons/qbt-theme/user-group-delete.svg diff --git a/src/icons/qbt-theme/user-group-new.png b/src/icons/qbt-theme/user-group-new.png deleted file mode 100644 index 42c6726aa2..0000000000 Binary files a/src/icons/qbt-theme/user-group-new.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/user-group-new.svg b/src/icons/qbt-theme/user-group-new.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/user-group-new.svg rename to src/icons/qbt-theme/user-group-new.svg diff --git a/src/icons/qbt-theme/view-calendar-journal.png b/src/icons/qbt-theme/view-calendar-journal.png deleted file mode 100644 index 33bdfb0b4c..0000000000 Binary files a/src/icons/qbt-theme/view-calendar-journal.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/view-calendar-journal.svg b/src/icons/qbt-theme/view-calendar-journal.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/view-calendar-journal.svg rename to src/icons/qbt-theme/view-calendar-journal.svg diff --git a/src/icons/qbt-theme/view-categories.png b/src/icons/qbt-theme/view-categories.png deleted file mode 100644 index f139b65fa4..0000000000 Binary files a/src/icons/qbt-theme/view-categories.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/view-categories.svg b/src/icons/qbt-theme/view-categories.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/view-categories.svg rename to src/icons/qbt-theme/view-categories.svg diff --git a/src/icons/qbt-theme/view-filter.png b/src/icons/qbt-theme/view-filter.png deleted file mode 100644 index d49ec895b0..0000000000 Binary files a/src/icons/qbt-theme/view-filter.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/view-filter.svg b/src/icons/qbt-theme/view-filter.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/view-filter.svg rename to src/icons/qbt-theme/view-filter.svg diff --git a/src/icons/qbt-theme/view-preview.png b/src/icons/qbt-theme/view-preview.png deleted file mode 100644 index 316b3cffcf..0000000000 Binary files a/src/icons/qbt-theme/view-preview.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/view-preview.svg b/src/icons/qbt-theme/view-preview.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/view-preview.svg rename to src/icons/qbt-theme/view-preview.svg diff --git a/src/icons/qbt-theme/view-refresh.png b/src/icons/qbt-theme/view-refresh.png deleted file mode 100644 index d0597294ff..0000000000 Binary files a/src/icons/qbt-theme/view-refresh.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/view-refresh.svg b/src/icons/qbt-theme/view-refresh.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/view-refresh.svg rename to src/icons/qbt-theme/view-refresh.svg diff --git a/src/icons/qbt-theme/view-statistics.png b/src/icons/qbt-theme/view-statistics.png deleted file mode 100644 index 07c6e10acc..0000000000 Binary files a/src/icons/qbt-theme/view-statistics.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/view-statistics.svg b/src/icons/qbt-theme/view-statistics.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/view-statistics.svg rename to src/icons/qbt-theme/view-statistics.svg diff --git a/src/icons/qbt-theme/wallet-open.png b/src/icons/qbt-theme/wallet-open.png deleted file mode 100644 index fd0b48f04e..0000000000 Binary files a/src/icons/qbt-theme/wallet-open.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/wallet-open.svg b/src/icons/qbt-theme/wallet-open.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/wallet-open.svg rename to src/icons/qbt-theme/wallet-open.svg diff --git a/src/icons/qbt-theme/webui.png b/src/icons/qbt-theme/webui.png deleted file mode 100644 index 03604e1ea2..0000000000 Binary files a/src/icons/qbt-theme/webui.png and /dev/null differ diff --git a/src/icons/qbt-theme/build-icons/icons/webui.svg b/src/icons/qbt-theme/webui.svg similarity index 100% rename from src/icons/qbt-theme/build-icons/icons/webui.svg rename to src/icons/qbt-theme/webui.svg diff --git a/src/icons/skin/build-icons/Gruntfile.js b/src/icons/skin/build-icons/Gruntfile.js deleted file mode 100644 index 2a88a46395..0000000000 --- a/src/icons/skin/build-icons/Gruntfile.js +++ /dev/null @@ -1,22 +0,0 @@ -module.exports = function(grunt) { - - grunt.initConfig({ - svg2png: { - all: { - options:{ - size: 256 - }, - files: [ - { - src: ['icons/*.svg'] - } - ] - } - } - }); - - grunt.loadNpmTasks('grunt-svg2png'); - - grunt.registerTask('default', ['svg2png']); - -} \ No newline at end of file diff --git a/src/icons/skin/build-icons/package.json b/src/icons/skin/build-icons/package.json deleted file mode 100644 index c4e5b5ca34..0000000000 --- a/src/icons/skin/build-icons/package.json +++ /dev/null @@ -1,13 +0,0 @@ -{ - "name": "build-icons", - "version": "1.0.0", - "description": "Convert svg icons to png", - "main": "index.js", - "dependencies": {}, - "devDependencies": { - "grunt": "^0.4.5", - "grunt-svg2png": "git+https://git@github.com/bertyhell/grunt-svg2png.git" - }, - "author": "Bert Verhelst", - "license": "ISC" -} diff --git a/src/icons/skin/build-icons/readme.md b/src/icons/skin/build-icons/readme.md deleted file mode 100644 index 97c3fd44f9..0000000000 --- a/src/icons/skin/build-icons/readme.md +++ /dev/null @@ -1,14 +0,0 @@ -Convert SVG icons to PNG ------------------------- - -install npm - -Execute: -``` -npm install -``` - -Convert icons by running: -``` -grunt -``` \ No newline at end of file diff --git a/src/icons/skin/checking.png b/src/icons/skin/checking.png deleted file mode 100644 index 58d3751aa1..0000000000 Binary files a/src/icons/skin/checking.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/checking.svg b/src/icons/skin/checking.svg similarity index 100% rename from src/icons/skin/build-icons/icons/checking.svg rename to src/icons/skin/checking.svg diff --git a/src/icons/skin/completed.png b/src/icons/skin/completed.png deleted file mode 100644 index 4e23903633..0000000000 Binary files a/src/icons/skin/completed.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/completed.svg b/src/icons/skin/completed.svg similarity index 100% rename from src/icons/skin/build-icons/icons/completed.svg rename to src/icons/skin/completed.svg diff --git a/src/icons/skin/connected.png b/src/icons/skin/connected.png deleted file mode 100644 index 043eb18423..0000000000 Binary files a/src/icons/skin/connected.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/connected.svg b/src/icons/skin/connected.svg similarity index 100% rename from src/icons/skin/build-icons/icons/connected.svg rename to src/icons/skin/connected.svg diff --git a/src/icons/skin/disconnected.png b/src/icons/skin/disconnected.png deleted file mode 100755 index 9432d6ba1e..0000000000 Binary files a/src/icons/skin/disconnected.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/disconnected.svg b/src/icons/skin/disconnected.svg similarity index 100% rename from src/icons/skin/build-icons/icons/disconnected.svg rename to src/icons/skin/disconnected.svg diff --git a/src/icons/skin/download.png b/src/icons/skin/download.png deleted file mode 100755 index b61efd5416..0000000000 Binary files a/src/icons/skin/download.png and /dev/null differ diff --git a/src/icons/skin/downloading.png b/src/icons/skin/downloading.png deleted file mode 100644 index 35d9f43f25..0000000000 Binary files a/src/icons/skin/downloading.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/downloading.svg b/src/icons/skin/downloading.svg similarity index 100% rename from src/icons/skin/build-icons/icons/downloading.svg rename to src/icons/skin/downloading.svg diff --git a/src/icons/skin/error.png b/src/icons/skin/error.png deleted file mode 100644 index ae2513198d..0000000000 Binary files a/src/icons/skin/error.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/error.svg b/src/icons/skin/error.svg similarity index 100% rename from src/icons/skin/build-icons/icons/error.svg rename to src/icons/skin/error.svg diff --git a/src/icons/skin/filteractive.png b/src/icons/skin/filteractive.png deleted file mode 100644 index 7bc43f4d3d..0000000000 Binary files a/src/icons/skin/filteractive.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/filteractive.svg b/src/icons/skin/filteractive.svg similarity index 100% rename from src/icons/skin/build-icons/icons/filteractive.svg rename to src/icons/skin/filteractive.svg diff --git a/src/icons/skin/filterall.png b/src/icons/skin/filterall.png deleted file mode 100644 index 7a185b2620..0000000000 Binary files a/src/icons/skin/filterall.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/filterall.svg b/src/icons/skin/filterall.svg similarity index 100% rename from src/icons/skin/build-icons/icons/filterall.svg rename to src/icons/skin/filterall.svg diff --git a/src/icons/skin/filterinactive.png b/src/icons/skin/filterinactive.png deleted file mode 100644 index 577a8d023b..0000000000 Binary files a/src/icons/skin/filterinactive.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/filterinactive.svg b/src/icons/skin/filterinactive.svg similarity index 100% rename from src/icons/skin/build-icons/icons/filterinactive.svg rename to src/icons/skin/filterinactive.svg diff --git a/src/icons/skin/firewalled.png b/src/icons/skin/firewalled.png deleted file mode 100644 index 9a1ef3425c..0000000000 Binary files a/src/icons/skin/firewalled.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/firewalled.svg b/src/icons/skin/firewalled.svg similarity index 100% rename from src/icons/skin/build-icons/icons/firewalled.svg rename to src/icons/skin/firewalled.svg diff --git a/src/icons/skin/paused.png b/src/icons/skin/paused.png deleted file mode 100644 index dd99800511..0000000000 Binary files a/src/icons/skin/paused.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/paused.svg b/src/icons/skin/paused.svg similarity index 100% rename from src/icons/skin/build-icons/icons/paused.svg rename to src/icons/skin/paused.svg diff --git a/src/icons/skin/queued.png b/src/icons/skin/queued.png deleted file mode 100644 index 873672939e..0000000000 Binary files a/src/icons/skin/queued.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/queued.svg b/src/icons/skin/queued.svg similarity index 100% rename from src/icons/skin/build-icons/icons/queued.svg rename to src/icons/skin/queued.svg diff --git a/src/icons/skin/ratio.png b/src/icons/skin/ratio.png deleted file mode 100644 index 9665d69de7..0000000000 Binary files a/src/icons/skin/ratio.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/ratio.svg b/src/icons/skin/ratio.svg similarity index 100% rename from src/icons/skin/build-icons/icons/ratio.svg rename to src/icons/skin/ratio.svg diff --git a/src/icons/skin/resumed.png b/src/icons/skin/resumed.png deleted file mode 100644 index 5aac2a07ef..0000000000 Binary files a/src/icons/skin/resumed.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/resumed.svg b/src/icons/skin/resumed.svg similarity index 100% rename from src/icons/skin/build-icons/icons/resumed.svg rename to src/icons/skin/resumed.svg diff --git a/src/icons/skin/seeding.png b/src/icons/skin/seeding.png deleted file mode 100644 index eb51988366..0000000000 Binary files a/src/icons/skin/seeding.png and /dev/null differ diff --git a/src/icons/skin/stalledDL.png b/src/icons/skin/stalledDL.png deleted file mode 100644 index eef7f68c04..0000000000 Binary files a/src/icons/skin/stalledDL.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/stalledDL.svg b/src/icons/skin/stalledDL.svg similarity index 100% rename from src/icons/skin/build-icons/icons/stalledDL.svg rename to src/icons/skin/stalledDL.svg diff --git a/src/icons/skin/stalledUP.png b/src/icons/skin/stalledUP.png deleted file mode 100644 index fb22c1a4b6..0000000000 Binary files a/src/icons/skin/stalledUP.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/stalledUP.svg b/src/icons/skin/stalledUP.svg similarity index 100% rename from src/icons/skin/build-icons/icons/stalledUP.svg rename to src/icons/skin/stalledUP.svg diff --git a/src/icons/skin/uploading.png b/src/icons/skin/uploading.png deleted file mode 100644 index 2a44949472..0000000000 Binary files a/src/icons/skin/uploading.png and /dev/null differ diff --git a/src/icons/skin/build-icons/icons/uploading.svg b/src/icons/skin/uploading.svg similarity index 100% rename from src/icons/skin/build-icons/icons/uploading.svg rename to src/icons/skin/uploading.svg diff --git a/src/icons/slow.png b/src/icons/slow.png deleted file mode 100644 index 0d978fc708..0000000000 Binary files a/src/icons/slow.png and /dev/null differ diff --git a/src/icons/slow_off.png b/src/icons/slow_off.png deleted file mode 100644 index 930663e789..0000000000 Binary files a/src/icons/slow_off.png and /dev/null differ diff --git a/src/update_qrc_files.py b/src/update_qrc_files.py index 8fda40e4e4..ebc2504159 100755 --- a/src/update_qrc_files.py +++ b/src/update_qrc_files.py @@ -75,7 +75,7 @@ if 'skin_unused' in dirs: dirs.remove('skin_unused') for file in files: - if splitext(file)[-1] in ('.png', '.jpg', '.gif'): + if splitext(file)[-1] in ('.svg', '.png', '.jpg', '.gif'): icons_list.append(join(root, file)) output = ''' diff --git a/unixconf.pri b/unixconf.pri index 2280f282aa..f71c723b19 100644 --- a/unixconf.pri +++ b/unixconf.pri @@ -125,6 +125,13 @@ nogui:systemd { INSTALLS += pixmap } +plasma_integration { + DEFINES += PLASMA_INTEGRATION + themes.files += $$DIST_PATH/qBittorrentPlasma.colortheme + themes.path = $$DATADIR/qBittorrent/theme/ + INSTALLS += themes +} + # INSTALL target.path = $$PREFIX/bin/ INSTALLS += target