/** * @file Common/app.cpp * @brief Application interface implementation * @author Enar Vaikene * * Copyright (c) 2011-2019 Enar Vaikene * * This file is part of the eVaf C++ cross-platform application development framework. * * This file can be used under the terms of the GNU General Public License * version 3.0 as published by the Free Software Foundation and appearing in * the file LICENSE included in the packaging of this file. Please review the * the following information to ensure the GNU General Public License version * 3.0 requirements will be met: http://www.gnu.org/copyleft/gpl.html. * * Alternatively, this file may be used in accordance with the Commercial License * Agreement provided with the Software. */ #include "app.h" #include "globals.h" #include "iregistry.h" #include "ieventqueue.h" #include "event.h" #include "version.h" #include //------------------------------------------------------------------- using namespace eVaf::Common; namespace { static Internal::App * singleton = nullptr; } iApp * iApp::instance() { if (nullptr == singleton) { singleton = new Internal::App; } return singleton; } char const * const iApp::EV_QUIT = "iApp::quit"; char const * const iApp::EV_RESTART = "iApp::restart"; char const * const iApp::EV_READY = "iApp::ready"; char const * const iApp::EV_TERMINATING = "iApp::terminating"; //------------------------------------------------------------------- using namespace eVaf::Common::Internal; void App::destroyInstance() { if (nullptr != singleton) { delete singleton; singleton = nullptr; } } App::App() : iApp() , mReady(false) , mName(VER_PRODUCT_NAME_STR) , mEvQuit(0) , mEvRestart(0) , mEvReady(0) , mEvTerminating(0) { setObjectName(QString("%1.iApp").arg(VER_MODULE_NAME_STR)); EVAF_INFO("%s-App created", VER_MODULE_NAME_STR); } App::~App() { EVAF_INFO("%s-App destroyed", VER_MODULE_NAME_STR); } bool App::init() { // Register our interface iRegistry::instance()->registerInterface("iApp", this); // Register events mEvQuit = iEventQueue::instance()->subscribeEvent(iEventQueue::instance()->registerEvent(EV_QUIT), this); mEvRestart = iEventQueue::instance()->subscribeEvent(iEventQueue::instance()->registerEvent(EV_RESTART), this); mEvReady = iEventQueue::instance()->registerEvent(EV_READY); mEvTerminating = iEventQueue::instance()->registerEvent(EV_TERMINATING); // Set the default application name and language mName = VER_PRODUCT_NAME_STR; mLanguage = QLocale::system().name(); // Clear the XML file name mXmlFile.clear(); // Set initial bin and root directories mRootDir = mBinDir = qApp->applicationDirPath(); int t = mBinDir.lastIndexOf(QChar('/'), -1); if (t >= 0) mRootDir = mBinDir.left(t); if (!mBinDir.endsWith('/')) mBinDir.append('/'); if (!mRootDir.endsWith('/')) mRootDir.append('/'); // Clear other directories mDataRootDir.clear(); mQtPluginsDir.clear(); mEtcDir.clear(); mLogDir.clear(); mDocDir.clear(); // Process environment variables QStringList env = QProcess::systemEnvironment(); for (int i = 0; i < env.size(); ++i) { // Get the name/value pair QString name = env.at(i).section('=', 0, 0).trimmed(); QString value = env.at(i).section('=', 1).trimmed(); if (name == "EVAF_APP_NAME") mName = value; else if (name == "EVAF_LANGUAGE") mLanguage = value; else if (name == "EVAF_ROOT_DIR") { mRootDir = value; if (!mRootDir.endsWith('/')) mRootDir.append('/'); } else if (name == "EVAF_DATA_ROOT_DIR") { mDataRootDir = value; if (!mDataRootDir.endsWith('/')) mDataRootDir.append('/'); } else if (name == "EVAF_ETC_DIR") { mEtcDir = value; if (!mEtcDir.endsWith('/')) mEtcDir.append('/'); } else if (name == "EVAF_LOG_DIR") { mLogDir = value; if (!mLogDir.endsWith('/')) mLogDir.append('/'); } else if (name == "EVAF_DOC_DIR") { mDocDir = value; if (!mDocDir.endsWith('/')) mDocDir.append('/'); } else if (name == "EVAF_QT_PLUGINS_DIR") { mQtPluginsDir = value; if (!mQtPluginsDir.endsWith('/')) mQtPluginsDir.append('/'); } } // Then process command-line arguments env = QCoreApplication::arguments(); for (int i = 0; i < env.size(); ++i) { // Get the name and optional value QStringList arg = env.at(i).simplified().split('='); if (QRegExp("-[-]?app(lication)?").exactMatch(arg.at(0)) && arg.size() > 1) mName = arg.at(1); else if (QRegExp("-[-]?lang(uage)?").exactMatch(arg.at(0)) && arg.size() > 1) mLanguage = arg.at(1); else if (QRegExp("-[-]?root(dir)?").exactMatch(arg.at(0)) && arg.size() > 1) { mRootDir = arg.at(1); if (!mRootDir.endsWith('/')) mRootDir.append('/'); } else if (QRegExp("-[-]?dataroot(dir)?").exactMatch(arg.at(0)) && arg.size() > 1) { mDataRootDir = arg.at(1); if (!mDataRootDir.endsWith('/')) mDataRootDir.append('/'); } else if (QRegExp("-[-]?etc(dir)?").exactMatch(arg.at(0)) && arg.size() > 1) { mEtcDir = arg.at(1); if (!mEtcDir.endsWith('/')) mEtcDir.append('/'); } else if (QRegExp("-[-]?log(dir)?").exactMatch(arg.at(0)) && arg.size() > 1) { mLogDir = arg.at(1); if (!mLogDir.endsWith('/')) mLogDir.append('/'); } else if (QRegExp("-[-]?doc(dir)?").exactMatch(arg.at(0)) && arg.size() > 1) { mDocDir = arg.at(1); if (!mDocDir.endsWith('/')) mDocDir.append('/'); } else if (QRegExp("-[-]?qtplugins(dir)?").exactMatch(arg.at(0)) && arg.size() > 1) { mQtPluginsDir = arg.at(1); if (!mQtPluginsDir.endsWith('/')) mQtPluginsDir.append('/'); } } return true; } bool App::event(QEvent * e) { if (e->type() == Event::eVafEvent) { Event * event = static_cast(e); if (event->id() == mEvQuit) quit(); else if (event->id() == mEvRestart) restart(); return false; } else return iApp::event(e); } QString const App::dataRootDir() const { if (mDataRootDir.isEmpty()) { #if defined(Q_OS_LINUX) || defined(Q_OS_MACOS) QString dataLoc = QDir::homePath(); if (!dataLoc.endsWith('/')) dataLoc.append('/'); dataLoc.append(".local/share/data/"); mDataRootDir = dataLoc + name(); if (!mDataRootDir.endsWith('/')) mDataRootDir.append('/'); #endif /// @TODO: Needs local data directory on Windows mDataRootDir = rootDir(); } return mDataRootDir; } QString const App::etcDir() const { if (mEtcDir.isEmpty()) mEtcDir = dataRootDir() + "etc/"; return mEtcDir; } QString const App::logDir() const { if (mLogDir.isEmpty()) mLogDir = dataRootDir() + "log/"; return mLogDir; } QString const App::docDir() const { if (mDocDir.isEmpty()) mDocDir = rootDir() + "doc/"; return mDocDir; } QString const App::qtPluginsDir() const { if (mQtPluginsDir.isEmpty()) mQtPluginsDir = binDir(); return mQtPluginsDir; } QString const App::xmlFileName() const { if (mXmlFile.isEmpty()) { QFileInfo fi; // Try the full application name + country + language combination QString name = mName + "_" + mLanguage + ".xml"; fi.setFile(etcDir() + name); if (fi.isReadable()) mXmlFile = name; else { // Try application name + country name = mName + "_" + mLanguage.left(2) + ".xml"; fi.setFile(etcDir() + name); if (fi.isReadable()) mXmlFile = name; else // Fall-back to the generic name mXmlFile = mName + ".xml"; } } return mXmlFile; } int App::exec() { setReady(true); int rval = QCoreApplication::exec(); setReady(false); return rval; } void App::restart() { QCoreApplication::exit(RC_Restart); } void App::quit(bool err) { QCoreApplication::exit(err ? RC_Error : RC_Quit); } void App::setReady(bool value) { if (mReady != value) { mReady = value; iEventQueue::instance()->broadcastEvent(new Event(mReady ? mEvReady : mEvTerminating)); if (mReady) emit ready(); else emit terminating(); } }