diff --git a/CMakeLists.txt b/CMakeLists.txt index c8fa53991..668f3be9d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -48,8 +48,8 @@ message(STATUS "[${APP_LOW_NAME}] Revision number obtained: " ${APP_REVISION} ". # Configure internal C++ defines. configure_file ( - ${PROJECT_SOURCE_DIR}/src/defs.h.in - ${CMAKE_CURRENT_BINARY_DIR}/src/defs.h + ${PROJECT_SOURCE_DIR}/src/core/defs.h.in + ${CMAKE_CURRENT_BINARY_DIR}/src/core/defs.h ) # Configure executable "properties" for Windows. @@ -152,6 +152,11 @@ set(APP_SOURCES # GUI sources. src/gui/formmain.cpp + src/gui/systemtrayicon.cpp + + # CORE sources. + src/core/debugging.cpp + src/core/settings.cpp # Basic application sources. src/main.cpp @@ -168,6 +173,9 @@ set(APP_HEADERS # GUI headers. src/gui/formmain.h + src/gui/systemtrayicon.cpp + + # CORE headers. ) # Add form files. diff --git a/src/core/debugging.cpp b/src/core/debugging.cpp new file mode 100644 index 000000000..f18d05301 --- /dev/null +++ b/src/core/debugging.cpp @@ -0,0 +1,46 @@ +#include +#include + +#include "core/defs.h" +#include "core/debugging.h" + +#include + +#ifndef QT_NO_DEBUG_OUTPUT +#define DEBUG_OUTPUT_WORKER(type_string, file, line, message) \ + fprintf(stderr, "[%s] %s (%s:%d): %s\n", \ + APP_LOW_NAME, \ + type_string, \ + file, \ + line, \ + qPrintable(message)); +#endif + + +void Debugging::debugHandler(QtMsgType type, + const QMessageLogContext &placement, + const QString &message) { +#ifndef QT_NO_DEBUG_OUTPUT + const char *file = qPrintable(QString(placement.file).section(QDir::separator(), -1)); + switch (type) { + case QtDebugMsg: + DEBUG_OUTPUT_WORKER("INFO", file, placement.line, message); + break; + case QtWarningMsg: + DEBUG_OUTPUT_WORKER("WARNING", file, placement.line, message); + break; + case QtCriticalMsg: + DEBUG_OUTPUT_WORKER("CRITICAL", file, placement.line, message); + break; + case QtFatalMsg: + DEBUG_OUTPUT_WORKER("FATAL", file, placement.line, message); + qApp->exit(EXIT_FAILURE); + default: + break; + } +#else + Q_UNUSED(type); + Q_UNUSED(placement); + Q_UNUSED(message); +#endif +} diff --git a/src/core/debugging.h b/src/core/debugging.h new file mode 100644 index 000000000..e434bc2e8 --- /dev/null +++ b/src/core/debugging.h @@ -0,0 +1,17 @@ +#ifndef DEBUGGING_H +#define DEBUGGING_H + +#include + + +class Debugging { + public: + // Specifies format of output console messages. + // Macros: + // QT_NO_DEBUG_OUTPUT - disables debug outputs completely!!! + static void debugHandler(QtMsgType type, + const QMessageLogContext &placement, + const QString &message); +}; + +#endif // DEBUGGING_H diff --git a/src/defs.h.in b/src/core/defs.h.in similarity index 81% rename from src/defs.h.in rename to src/core/defs.h.in index 4f9e82040..3d7cfc39c 100644 --- a/src/defs.h.in +++ b/src/core/defs.h.in @@ -14,9 +14,8 @@ #define APP_CFG_PATH "data/config/config.ini" #define APP_CFG_GUI "gui" -#define APP_CFG_GEN "main" -#define APP_CFG_CALC "calculator" -#define APP_CFG_LANG "language" +#define APP_CFG_GEN "general" +#define APP_CFG_LANG "localization" #define APP_DB_PATH "data/storage/database.db" #define APP_PREFIX "@CMAKE_INSTALL_PREFIX@" @@ -29,15 +28,18 @@ #define APP_LANG_PATH APP_PREFIX + QString("/share/qonverter/l10n") #define APP_SKIN_PATH APP_PREFIX + QString("/share/qonverter/skins") #define APP_INFO_PATH APP_PREFIX + QString("/share/qonverter/information") +#define APP_THEME_PATH APP_PREFIX + QString("/share/qonverter/themes") #elif defined(Q_OS_MAC) #define APP_LANG_PATH QApplication::applicationDirPath() + "/../Resources/l10n" #define APP_SKIN_PATH QApplication::applicationDirPath() + "/../Resources/skins" #define APP_PLUGIN_PATH QApplication::applicationDirPath() + "/../Resources/plugins" #define APP_INFO_PATH QApplication::applicationDirPath() + "/../Resources/information" +#define APP_THEME_PATH QApplication::applicationDirPath() + "/../Resources/themes" #elif defined(Q_OS_WIN) || defined(Q_OS_OS2) #define APP_LANG_PATH QApplication::applicationDirPath() + QString("/l10n") #define APP_SKIN_PATH QApplication::applicationDirPath() + QString("/skins") #define APP_INFO_PATH QApplication::applicationDirPath() +#define APP_THEME_PATH QApplication::applicationDirPath() + QString("/themes") #endif #endif // DEFS_H diff --git a/src/core/settings.cpp b/src/core/settings.cpp new file mode 100644 index 000000000..fbfa63030 --- /dev/null +++ b/src/core/settings.cpp @@ -0,0 +1,84 @@ +/* + This file is part of Qonverter. + + Qonverter 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 3 of the License, or + (at your option) any later version. + + Qonverter 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 Qonverter. If not, see . + + Copyright 2012 - 2013 Martin Rotter +*/ + +#include +#include +#include +#include + +#include "core/settings.h" +#include "core/defs.h" + + +QPointer Settings::s_instance; + +QSettings::Status Settings::checkSettings() { + qDebug("Syncing settings."); + s_instance->sync(); + + return s_instance->status(); +} + +QVariant Settings::value(const QString §ion, + const QString &key, + const QVariant &default_value) { + if (s_instance.isNull()) { + setupSettings(); + } + return s_instance->value(QString("%1/%2").arg(section, key), default_value); +} + +void Settings::setValue(const QString §ion, + const QString &key, + const QVariant &value) { + if (s_instance.isNull()) { + setupSettings(); + } + s_instance->setValue(QString("%1/%2").arg(section, key), value); +} + +void Settings::deleteSettings() { + checkSettings(); + qDebug("Deleting global settings."); + delete s_instance.data(); +} + +QSettings::Status Settings::setupSettings() { + // If settings file exists in executable file working directory, + // then use it (portable settings). + // Otherwise use settings file stored in homePath(); + QString home_path = QDir::homePath() + QDir::separator() + + APP_LOW_H_NAME + QDir::separator() + + APP_CFG_PATH; + QString app_path = qApp->applicationDirPath() + QDir::separator() + + APP_CFG_PATH; + + if (QFile(app_path).exists()) { + s_instance = new QSettings(app_path, QSettings::IniFormat); + qDebug("Initializing settings in %s.", + qPrintable(QDir::toNativeSeparators(app_path))); + } + else { + s_instance = new QSettings(home_path, QSettings::IniFormat); + qDebug("Initializing settings in %s.", + qPrintable(QDir::toNativeSeparators(home_path))); + } + + return checkSettings(); +} diff --git a/src/core/settings.h b/src/core/settings.h new file mode 100644 index 000000000..b0b1234c4 --- /dev/null +++ b/src/core/settings.h @@ -0,0 +1,34 @@ +#ifndef SETTINGS_H +#define SETTINGS_H + +#include + + +class Settings { + private: + // We use QPointer instead of QScopedPointer + // because of late s_instance usage in QApplication::aboutToQuit() listeners. + static QPointer s_instance; + + public: + // Getter/setter for settings values. + static QVariant value(const QString §ion, + const QString &key, + const QVariant &default_value = QVariant()); + + static void setValue(const QString §ion, + const QString &key, + const QVariant &value); + + // It's better to cleanup settings manually via this function. + static void deleteSettings(); + + // Synchronises settings. + static QSettings::Status checkSettings(); + + protected: + // Creates settings file in correct location. + static QSettings::Status setupSettings(); +}; + +#endif // SETTINGS_H diff --git a/src/gui/formmain.cpp b/src/gui/formmain.cpp index df012b18e..f3d6e2d6e 100644 --- a/src/gui/formmain.cpp +++ b/src/gui/formmain.cpp @@ -1,8 +1,12 @@ -#include "formmain.h" +#include "gui/formmain.h" +#include "core/settings.h" +#include "qtsingleapplication/qtsingleapplication.h" FormMain::FormMain(QWidget *parent) : QMainWindow(parent), m_ui(new Ui::FormMain) { m_ui->setupUi(this); + + createConnections(); } FormMain::~FormMain() { @@ -11,4 +15,14 @@ FormMain::~FormMain() { void FormMain::processExecutionMessage(const QString &message) { // TODO: Implement proper reaction when application is launched more than once. + qDebug("Received '%s' execution message from another application instance.", + qPrintable(message)); +} + +void FormMain::cleanupResources() { + Settings::deleteSettings(); +} + +void FormMain::createConnections() { + connect(qApp, &QCoreApplication::aboutToQuit, this, &FormMain::cleanupResources); } diff --git a/src/gui/formmain.h b/src/gui/formmain.h index 97363d069..783c619c0 100644 --- a/src/gui/formmain.h +++ b/src/gui/formmain.h @@ -13,8 +13,14 @@ class FormMain : public QMainWindow { explicit FormMain(QWidget *parent = 0); ~FormMain(); + protected: + void createConnections(); + public slots: void processExecutionMessage(const QString &message); + + protected slots: + void cleanupResources(); private: Ui::FormMain *m_ui; diff --git a/src/gui/systemtrayicon.cpp b/src/gui/systemtrayicon.cpp new file mode 100644 index 000000000..3b7579a09 --- /dev/null +++ b/src/gui/systemtrayicon.cpp @@ -0,0 +1,5 @@ +#include "systemtrayicon.h" + + +SystemTrayIcon::SystemTrayIcon(QObject *parent) : QSystemTrayIcon(parent) { +} diff --git a/src/gui/systemtrayicon.h b/src/gui/systemtrayicon.h new file mode 100644 index 000000000..e6c96ab1e --- /dev/null +++ b/src/gui/systemtrayicon.h @@ -0,0 +1,18 @@ +#ifndef SYSTEMTRAYICON_H +#define SYSTEMTRAYICON_H + +#include + +class SystemTrayIcon : public QSystemTrayIcon +{ + Q_OBJECT + public: + explicit SystemTrayIcon(QObject *parent = 0); + + signals: + + public slots: + +}; + +#endif // SYSTEMTRAYICON_H diff --git a/src/main.cpp b/src/main.cpp index 0cd314758..04db49065 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,14 @@ -#include +// Needed for setting ini file format on Mac OS. +#ifdef Q_OS_MAC +#include +#endif +#include "core/defs.h" +#include "core/debugging.h" +#include "core/settings.h" #include "gui/formmain.h" #include "qtsingleapplication/qtsingleapplication.h" -#include "defs.h" - int main(int argc, char *argv[]) { //: Name of language, e.g. English. @@ -19,23 +23,57 @@ int main(int argc, char *argv[]) { //: Email of translator - optional. QObject::tr("LANG_EMAIL"); + // Ensure that ini format is used as application settings storage on Mac OS. +#ifdef Q_OS_MAC + QSettings::setDefaultFormat(QSettings::IniFormat); +#endif + + // Setup debug output system. + qInstallMessageHandler(Debugging::debugHandler); + // TODO: Finish implementation of QtSingleApplication into RSS Guard. // This primarily concerns slot in FormMain which reacts when application is launched // repeatedly. See 'trivial' example from QtSingleApplication source code for more // information. QtSingleApplication application(argc, argv); + qDebug("Instantiated QtSingleApplication class."); + // Check if another instance is running. if (application.sendMessage(APP_IS_RUNNING)) { + qDebug("Another instance of the application is already running. Notifying it."); return EXIT_SUCCESS; } + // Add 3rd party plugin directory to application PATH variable. + // This is useful for styles, encoders, ... + // This is probably not needed on Windows or Linux, not sure about Mac OS X. +#if defined(Q_OS_MAC) + QApplication::addLibraryPath(APP_PLUGIN_PATH); +#endif + + // These settings needs to be set before any QSettings object. + QtSingleApplication::setApplicationName(APP_NAME); + QtSingleApplication::setApplicationVersion(APP_VERSION); + QtSingleApplication::setOrganizationName(APP_AUTHORS); + QtSingleApplication::setOrganizationDomain(APP_URL); + QtSingleApplication::setWindowIcon(QIcon(":/graphics/qonverter.png")); + + // Instantiate main application window. FormMain window; window.show(); - application.setActivationWindow(&window, true); + if (Settings::value(APP_CFG_GEN, "first_start", true).toBool()) { + // TODO: Open initial "Welcome" dialog here. + Settings::setValue(APP_CFG_GEN, "first_start", false); + } + + + // Setup single-instance behavior. + application.setActivationWindow(&window, true); QObject::connect(&application, &QtSingleApplication::messageReceived, &window, &FormMain::processExecutionMessage); + // Enter global event loop. return QtSingleApplication::exec(); }