diff --git a/CMakeLists.txt b/CMakeLists.txt index 3f05cfdc8..e62895c03 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,10 +137,11 @@ if(${USE_QT_5}) find_package(Qt5WebKitWidgets) find_package(Qt5Widgets) find_package(Qt5Xml) + find_package(Qt5XmlPatterns) find_package(Qt5Network) find_package(Qt5LinguistTools) else(${USE_QT_5}) - find_package(Qt4 REQUIRED QtCore QtGui QtSql QtNetwork QtWebkit QtXml) + find_package(Qt4 REQUIRED QtCore QtGui QtSql QtNetwork QtWebkit QtXml QtXmlPatterns) include(${QT_USE_FILE}) endif(${USE_QT_5}) @@ -157,9 +158,9 @@ if(MINGW AND WIN32) ) set(APP_SOURCES ${APP_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/resources/executable_properties/rssguard_win.o) -# MSVC takes care of this automatically no need to use windres.exe +# MSVC takes care of this automatically - no need to use windres.exe # for MSVC compilers. -elseif(WIN32) +elseif(MINGW AND WIN32) set(APP_SOURCES ${APP_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/resources/executable_properties/rssguard_win.rc) endif(MINGW AND WIN32) @@ -175,6 +176,7 @@ set(APP_SOURCES src/gui/formmain.cpp src/gui/systemtrayicon.cpp src/gui/iconthemefactory.cpp + src/gui/skinfactory.cpp src/gui/formsettings.cpp src/gui/formwelcome.cpp src/gui/formabout.cpp @@ -218,6 +220,7 @@ set(APP_HEADERS src/gui/formmain.h src/gui/systemtrayicon.h src/gui/iconthemefactory.h + src/gui/skinfactory.h src/gui/formsettings.h src/gui/formwelcome.h src/gui/formabout.h @@ -334,6 +337,7 @@ if(${USE_QT_5}) Sql Network Xml + XmlPatterns WebKit WebKitWidgets ) @@ -354,6 +358,7 @@ else(${USE_QT_5}) ${QT_QTNETWORK_LIBRARY} ${QT_QTSQL_LIBRARY} ${QT_QTXML_LIBRARY} + ${QT_QTXMLPATTERNS_LIBRARY} ${QT_QTMAIN_LIBRARY} ${QT_QTWEBKIT_LIBRARY} ) diff --git a/src/gui/formmain.cpp b/src/gui/formmain.cpp index aff17139b..dc45da8a9 100644 --- a/src/gui/formmain.cpp +++ b/src/gui/formmain.cpp @@ -122,7 +122,7 @@ void FormMain::cleanupResources() { } bool FormMain::event(QEvent *event) { - if (event->type() == ThemeFactoryEvent::type()) { + if (event->type() == IconThemeFactoryEvent::type()) { // Handle the change of icon theme. setupIcons(); event->accept(); diff --git a/src/gui/iconthemefactory.cpp b/src/gui/iconthemefactory.cpp index ea6f7b758..10b144cd3 100644 --- a/src/gui/iconthemefactory.cpp +++ b/src/gui/iconthemefactory.cpp @@ -11,20 +11,20 @@ QPointer IconThemeFactory::s_instance; -QEvent::Type ThemeFactoryEvent::m_typeOfEvent = QEvent::None; +QEvent::Type IconThemeFactoryEvent::m_typeOfEvent = QEvent::None; // // ThemeFactoryEvent class // -ThemeFactoryEvent::ThemeFactoryEvent() : QEvent(ThemeFactoryEvent::type()) { +IconThemeFactoryEvent::IconThemeFactoryEvent() : QEvent(IconThemeFactoryEvent::type()) { } -ThemeFactoryEvent::~ThemeFactoryEvent() { +IconThemeFactoryEvent::~IconThemeFactoryEvent() { qDebug("Destroying IconThemeFactoryEvent."); } -QEvent::Type ThemeFactoryEvent::type() { +QEvent::Type IconThemeFactoryEvent::type() { if (m_typeOfEvent == QEvent::None) { m_typeOfEvent = static_cast(QEvent::registerEventType(2000)); } @@ -109,7 +109,7 @@ void IconThemeFactory::loadCurrentIconTheme(bool notify_widgets) { if (notify_widgets) { foreach (QWidget *widget, QtSingleApplication::allWidgets()) { QtSingleApplication::postEvent((QObject*) widget, - new ThemeFactoryEvent()); + new IconThemeFactoryEvent()); } } } diff --git a/src/gui/iconthemefactory.h b/src/gui/iconthemefactory.h index bbde9aa99..64070940e 100644 --- a/src/gui/iconthemefactory.h +++ b/src/gui/iconthemefactory.h @@ -54,15 +54,15 @@ class IconThemeFactory : public QObject { static QPointer s_instance; }; -class ThemeFactoryEvent : public QEvent { +class IconThemeFactoryEvent : public QEvent { public: enum Type { IconThemeChange = 2000 }; // Constructors. - explicit ThemeFactoryEvent(); - virtual ~ThemeFactoryEvent(); + explicit IconThemeFactoryEvent(); + virtual ~IconThemeFactoryEvent(); static QEvent::Type type(); diff --git a/src/gui/skinfactory.cpp b/src/gui/skinfactory.cpp new file mode 100644 index 000000000..e05e7f151 --- /dev/null +++ b/src/gui/skinfactory.cpp @@ -0,0 +1,149 @@ +#include +#include +#include +#include +#include + +#include "core/defs.h" +#include "core/settings.h" +#include "gui/skinfactory.h" + + +QPointer SkinFactory::s_instance; + +SkinFactory::SkinFactory(QObject *parent) + : QObject(parent), m_currentSkin(APP_THEME_SYSTEM) { +} + +SkinFactory::~SkinFactory() { + qDebug("Destroying SkinFactory instance."); +} + +SkinFactory *SkinFactory::getInstance() { + if (s_instance.isNull()) { + s_instance = new SkinFactory(qApp); + } + + return s_instance; +} + +void SkinFactory::loadCurrentSkin() { + QString skin_name_from_settings = Settings::getInstance()->value(APP_CFG_GUI, + "skin", + "base/plain.xml").toString(); + bool loaded = false; + Skin skin_data = getSkinInfo(skin_name_from_settings, &loaded); + + if (loaded) { + loadSkinFromData(skin_data.m_rawData, skin_name_from_settings); + + foreach (QString style, skin_data.m_stylesName.split("\n")) { + if (qApp->setStyle(style) != 0) { + qDebug("Style '%s' loaded.", qPrintable(style)); + break; + } + } + + m_currentSkin = skin_name_from_settings; + + qDebug("Skin '%s' loaded.", qPrintable(skin_name_from_settings)); + } + else { + qDebug("Skin '%s' not loaded because style name is not specified or skin raw data is missing.", + qPrintable(skin_name_from_settings)); + } +} + +bool SkinFactory::loadSkinFromData(QString skin_data, const QString &skin_path) { + QStringList skin_parts = skin_path.split('/', QString::SkipEmptyParts); + + // Skin does not contain leading folder name or the actual skin file name. + if (skin_parts.size() != 2) { + qDebug("Loading of sking %s failed because skin name does not contain " + "base folder name or the actual skin name.", + qPrintable(skin_path)); + return false; + } + else { + qDebug("Loading skin '%s'.", qPrintable(skin_path)); + } + + // Create needed variables and create QFile object representing skin contents. + QString skin_folder = skin_parts.at(0); + + // Here we use "/" instead of QDir::separator() because CSS2.1 url field + // accepts '/' as path elements separator. + // + // "##" is placeholder for the actual path to skin file. This is needed for using + // images within the QSS file. + QString parsed_data = skin_data.replace("##", + APP_SKIN_PATH + "/" + skin_folder + "/images"); + qApp->setStyleSheet(parsed_data); + return true; +} + +void SkinFactory::setCurrentSkinName(const QString &skin_name) { + Settings::getInstance()->setValue(APP_CFG_GUI, "skin", skin_name); + loadCurrentSkin(); +} + +QString SkinFactory::getCurrentSkinName() { + return m_currentSkin; +} + +Skin SkinFactory::getSkinInfo(const QString &skin_name, bool *ok) { + Skin skin; + QXmlQuery query; + QFile skin_file(APP_SKIN_PATH + QDir::separator() + skin_name); + + if (!skin_file.open(QIODevice::ReadOnly) ||!query.setFocus(&skin_file)) { + if (ok) { + *ok = false; + } + return skin; + } + + // Obtain skin raw data. + query.setQuery("string(skin/data)"); + query.evaluateTo(&skin.m_rawData); + + // Obtain style name. + query.setQuery("string(/skin/style)"); + query.evaluateTo(&skin.m_stylesName); + skin.m_stylesName = skin.m_stylesName.remove("\n"); + + // Obtain author. + query.setQuery("string(/skin/author/name)"); + query.evaluateTo(&skin.m_author); + skin.m_author = skin.m_author.remove("\n"); + + // Obtain email. + query.setQuery("string(/skin/author/email)"); + query.evaluateTo(&skin.m_email); + skin.m_email = skin.m_email.remove("\n"); + + // Obtain version. + query.setQuery("string(/skin/@version)"); + query.evaluateTo(&skin.m_version); + skin.m_version = skin.m_version.remove("\n"); + + // Obtain other information. + skin.m_baseName = skin_name; + + // Free resources. + skin_file.close(); + skin_file.deleteLater(); + + if (ok) { + *ok = !skin.m_author.isEmpty() && !skin.m_version.isEmpty() && + !skin.m_baseName.isEmpty() && !skin.m_email.isEmpty() && + !skin.m_rawData.isEmpty() && !skin.m_stylesName.isEmpty(); + } + + return skin; +} + +QList SkinFactory::getInstalledSkins() { + QList skins; + return skins; +} diff --git a/src/gui/skinfactory.h b/src/gui/skinfactory.h new file mode 100644 index 000000000..5c8c84c6e --- /dev/null +++ b/src/gui/skinfactory.h @@ -0,0 +1,54 @@ +#ifndef SKINFACTORY_H +#define SKINFACTORY_H + +#include +#include + + +struct Skin { + QString m_baseName; + QString m_stylesName; + QString m_author; + QString m_email; + QString m_version; + QString m_rawData; +}; + +class SkinFactory : public QObject { + Q_OBJECT + + private: + explicit SkinFactory(QObject *parent = 0); + + bool loadSkinFromData(QString skin_data, const QString &skin_path); + + public: + // Singleton getter. + static SkinFactory *getInstance(); + + // Destructor. + virtual ~SkinFactory(); + + // Loads skin name from settings and sets it as active. + void loadCurrentSkin(); + + // Return the name of the currently activated skin. + // NOTE: Skin name is formatted as "/.xml". + QString getCurrentSkinName(); + + Skin getSkinInfo(const QString &skin_name, bool *ok = NULL); + + QList getInstalledSkins(); + + // Sets the desired skin as the active one if it exists. + void setCurrentSkinName(const QString &skin_name); + + private: + // Holds name of the current skin. + QString m_currentSkin; + + // Singleton. + static QPointer s_instance; +}; + +#endif // SKINFACTORY_H diff --git a/src/main.cpp b/src/main.cpp index 6f1728dcf..484b7cb1d 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -11,6 +11,7 @@ #include "core/settings.h" #include "core/dynamicshortcuts.h" #include "gui/iconthemefactory.h" +#include "gui/skinfactory.h" #include "gui/formmain.h" #include "gui/formwelcome.h" #include "gui/systemtrayicon.h" @@ -66,9 +67,11 @@ int main(int argc, char *argv[]) { QtSingleApplication::addLibraryPath(APP_PLUGIN_PATH); #endif - // Add an extra path for non-system icon themes and set current icon theme. + // Add an extra path for non-system icon themes and set current icon theme + // and skin. IconThemeFactory::getInstance()->setupSearchPaths(); IconThemeFactory::getInstance()->loadCurrentIconTheme(false); + SkinFactory::getInstance()->loadCurrentSkin(); // Load localization and setup locale before any widget is constructed. LoadLocalization();