+/**
+ * @file main/GUI/main.cpp
+ * @brief The main eVaf GUI application class
+ * @author Enar Vaikene
+ *
+ * Copyright (c) 2011 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 "main.h"
+#include "exithandler.h"
+#include "fatalerr.h"
+//#include "version_p.h"
+#include "version.h"
+
+#ifdef Q_OS_WIN32
+#include "winconsole.h"
+#endif
+
+#include <Common/Globals>
+#include <Common/iLogger>
+#include <Common/iEnv>
+#include <Common/iApp>
+
+#include <QtGui>
+
+#ifdef Q_OS_LINUX
+# include <sys/types.h>
+# include <unistd.h>
+#endif
+
+
+//-------------------------------------------------------------------
+
+namespace eVaf {
+namespace GUI {
+namespace Internal {
+
+/**
+ * Flag indicating that the application should be more verbose when dealing with fatal errors.
+ *
+ * If this flag is set, then shows fatal errors on the screen as dialog boxes and
+ * the user has to close them before terminating the application.
+ *
+ * If this flag is not set, then no messages are shown and the application terminates
+ * silently. Error messages are written only into the log file.
+ */
+static bool BeVerbose = true;
+
+#ifdef Q_OS_WIN32
+/**
+ * Flag indicating that the application needs a console window.
+ *
+ * If this flag is set, opens an extra console window for message output.
+ */
+static bool NeedsConsole = false;
+#endif
+
+/**
+ * Console severity level.
+ *
+ * This variable is used to set the console severity level. The severity level is changed
+ * with command-line arguments.
+ */
+static eVaf::Common::iLogger::Severity ConsoleSeverityLevel = eVaf::Common::iLogger::Fatal;
+
+/**
+ * Qt message handler replacement.
+ * @param type Type of the message
+ * @param msg The message
+ *
+ * This function outputs messages to the console and to the log file.
+ */
+static void messageOutput(QtMsgType type, char const * const msg)
+{
+ static bool inHandler = false;
+
+ // Avoid recursions in case outputting a message causes another message to be output
+ if (inHandler)
+ return;
+ inHandler = true;
+
+ // Qt message type conversion to eVaf logger severity levels
+ eVaf::Common::iLogger::Severity v;
+ switch (type) {
+ case QtWarningMsg:
+ v = eVaf::Common::iLogger::Warning;
+ break;
+ case QtCriticalMsg:
+ v = eVaf::Common::iLogger::Error;
+ break;
+ case QtFatalMsg:
+ v = eVaf::Common::iLogger::Fatal;
+ break;
+ default:
+ v = eVaf::Common::iLogger::Debug;
+ }
+
+ // Output to the log file and console
+ eVaf::Common::iLogger::instance()->write(v, msg);
+
+ inHandler = false;
+}
+
+/**
+ * Fatal error message handler
+ * @param msg The error message
+ * @param source Source of the message
+ * @param where Where the error occurred
+ *
+ * This function shows a critical error message box on the screen if needed and then terminates
+ * the application.
+ *
+ * If the critical error message is shown, then the user has an option to ignore the error. In this
+ * case the application is not terminated.
+ */
+static void fatalMsgHandler(QString const & msg, QString const & source, QString const & where)
+{
+ // Show the message on the screen
+ if (BeVerbose) {
+ if (FatalErr::message(QObject::tr("Fatal Error"),
+ QObject::tr("%1\n\nOccurred in '%2'")
+ .arg(msg)
+ .arg(where),
+ 0) == FatalErr::Ignore)
+ return;
+ }
+#ifdef Q_OS_LINUX
+ abort();
+#else
+ exit(1);
+#endif
+}
+
+} // namespace eVaf::GUI::Internal
+} // namespace eVaf::GUI
+} // namespace eVaf
+
+
+//-------------------------------------------------------------------
+
+using namespace eVaf;
+using namespace eVaf::GUI;
+
+Application::Application(int & argc, char ** argv)
+ : QApplication(argc, argv)
+{
+ setObjectName(QString("%1-%2").arg(VER_MODULE_NAME_STR).arg(__FUNCTION__));
+
+ EVAF_INFO("%s version %s created", qPrintable(objectName()), VER_FILE_VERSION_STR);
+}
+
+Application::~Application()
+{
+ EVAF_INFO("%s destroyed", qPrintable(objectName()));
+}
+
+bool Application::processCommandLine(int argc, char ** argv)
+{
+ QStringList args;
+ for (int i = 1; i < argc; ++i)
+ args += argv[i];
+
+ for (int i = 0; i < args.size(); ++i) {
+ // Get the argument and optional value
+ QStringList arg = args.at(i).simplified().split(QChar('='));
+
+ if (QRegExp("(-[-]?version)|([-//]V)").exactMatch(arg.at(0))) {
+ printVersion();
+ return false;
+ }
+ else if (QRegExp("(-[-]?help)|([-//][h/?])").exactMatch(arg.at(0))) {
+ printHelp();
+ return false;
+ }
+ else if (QRegExp("-[-]?help-qt").exactMatch(arg.at(0))) {
+ printQtHelp();
+ return false;
+ }
+ else if (QRegExp("-[-]?verbose").exactMatch(arg.at(0)) && arg.size() > 1) {
+#ifdef Q_OS_WIN32
+ Internal::NeedsConsole = true;
+#endif
+ QString v = arg.at(1).toLower();
+ if (v == "debug")
+ Internal::ConsoleSeverityLevel = Common::iLogger::Debug;
+ else if (v == "info")
+ Internal::ConsoleSeverityLevel = Common::iLogger::Info;
+ else if (v == "warning")
+ Internal::ConsoleSeverityLevel = Common::iLogger::Warning;
+ else if (v == "error")
+ Internal::ConsoleSeverityLevel = Common::iLogger::Error;
+ else if (v == "fatal")
+ Internal::ConsoleSeverityLevel = Common::iLogger::Fatal;
+ else if (v == "none") {
+ Internal::ConsoleSeverityLevel = Common::iLogger::None;
+ Internal::BeVerbose = false;
+#ifdef Q_OS_WIN32
+ Internal::NeedsConsole = false;
+#endif
+ }
+ else {
+ printHelp();
+ return false;
+ }
+ }
+ else if (QRegExp("-[v]+").exactMatch(arg.at(0)) && arg.size() == 1) {
+ // The number of 'v's increases the verbosity
+ for (int j = 1; j < arg.at(0).size(); ++j) {
+ switch (Internal::ConsoleSeverityLevel) {
+ case Common::iLogger::None:
+ Internal::ConsoleSeverityLevel = Common::iLogger::Fatal;
+ break;
+ case Common::iLogger::Fatal:
+ Internal::ConsoleSeverityLevel = Common::iLogger::Error;
+ break;
+ case Common::iLogger::Error:
+ Internal::ConsoleSeverityLevel = Common::iLogger::Warning;
+ break;
+ case Common::iLogger::Warning:
+ Internal::ConsoleSeverityLevel = Common::iLogger::Info;
+ break;
+ case Common::iLogger::Info:
+ Internal::ConsoleSeverityLevel = Common::iLogger::Debug;
+ break;
+ case Common::iLogger::Debug:
+ break;
+ }
+ }
+ }
+ }
+
+ return true;
+}
+
+void Application::printHelp()
+{
+ char const * const txt = QT_TR_NOOP(
+ "Usage: eVafGUI [options]\n"
+ "\n"
+ // General options
+ " -help Shows this help and quits.\n"
+ " -help-qt Shows Qt command line options and quits.\n"
+ " -version Shows version information and quits.\n"
+ " -verbose=LEVEL Specifies the verbose level. LEVEL can be one of the\n"
+ " following: NONE, FATAL, ERROR, WARNING, INFO, DEBUG.\n"
+ " -v Makes the application more verbose. Can be repeated for\n"
+ " more verbosity.\n"
+ // Handled by the iApp interface implementation
+ " -appl[ication]=NAME Specifies the name of the application.\n"
+ " -lang[uage]=xx[_CC] Specifies the language, where xx is the ISO 639\n"
+ " language code followed by an optional ISO 3166 country\n"
+ " code.\n"
+ // Handled by the iEnv interface implementation
+ " -root[dir]=DIR Specifies the application's root directory.\n"
+ " -dataroot[dir]=DIR Specifies the data root directory.\n"
+ " -etc[dir]=DIR Specifies the configuration files directory.\n"
+ " -log[dir]=DIR Specifies the log files directory.\n"
+ " -doc[dir]=DIR Specifies the documentation directory.\n"
+ " -qtplugins[dir]=DIR Specifies the Qt plugins directory.\n"
+ );
+ ::fputs(tr(txt).toLocal8Bit().constData(), stdout);
+}
+
+void Application::printQtHelp()
+{
+ // Cannot translate this text as QT_TR_NOOP() is not able to process #ifdef parts.
+ char const * const txt =
+#ifdef QT_DEBUG
+ "Qt debugging options:\n"
+ " -nograb tells Qt that it must never grab the mouse or the keyboard.\n"
+#ifdef Q_OS_UNIX
+ " -dograb running under a debugger can cause an implicit -nograb,\n"
+ " use -dograb to override.\n"
+ " -sync switches to synchronous mode for debugging.\n\n"
+#endif
+#endif
+ "Qt common options:\n"
+ " -style=STYLE sets the application GUI style. Possible values are motif,\n"
+ " windows, and platinum.\n"
+ " -style STYLE is the same as listed above.\n"
+ " -stylesheet=STYLESHEET sets the application style sheet.\n"
+ " -stylesheet STYLESHEET is the same as listed above.\n"
+ " -session=SESSION restores the application from an earlier session.\n"
+ " -session SESSION is the same as listed above.\n"
+ " -widgetcount prints debug message at the end about number of widgets\n"
+ " left undestroyed and maximum number of widgets existed at\n"
+ " the same time.\n"
+ " -reverse sets the application's layout direction to Qt::RightToLeft\n\n"
+#ifdef Q_OS_WIN32
+ "Qt options on Windows:\n"
+ " -direct3d will make the Direct3D paint engine the default widget\n"
+ " paint engine in Qt.\n\n"
+#endif
+#ifdef Q_WS_X11
+ "Qt options on X11:\n"
+ " -display DISPLAY sets the X display.\n"
+ " -geometry GEOMETRY sets the client geometry of the first window that is\n"
+ " shown.\n"
+ " -fn or -font FONT defines the application font.\n"
+ " -bg or -background COLOR sets the default background color and an\n"
+ " application palette.\n"
+ " -fg or -foreground COLOR sets the default foreground color.\n"
+ " -btn or -button COLOR sets the default button color.\n"
+ " -name NAME sets the application name.\n"
+ " -title TITLE sets the application title.\n"
+ " -visual TrueColor forces the application to use a TrueColor visual on an\n"
+ " 8-bit display.\n"
+ " -ncols COUNT limits the number of colors allocated in the color cube on\n"
+ " an 8-bit display, if the application is using the\n"
+ " QApplication::ManyColor color specification. If COUNT is\n"
+ " 216 then a 6x6x6 color cube is used (i.e. 6 levels of red,\n"
+ " 6 of green, and 6 of blue); for other values, a cube\n"
+ " approximately proportional to a 2x3x1 cube is used.\n"
+ " -cmap causes the application to install a private color map on an\n"
+ " 8-bit display.\n"
+ " -im sets the input method server (equivalent to setting the\n"
+ " XMODIFIERS environment variable).\n"
+ " -noxim disables the input method framework (\"no X input method\").\n"
+ " -inputstyle defines how the input is inserted into the given widget.\n"
+ " E.g., onTheSpot makes the input appear directly in the\n"
+ " widget, while overTheSpot makes the input appear in a box\n"
+ " floating over the widget and is not inserted until the\n"
+ " editing is done.\n"
+#endif
+ ;
+ ::fputs(txt, stdout);
+}
+
+void Application::printVersion()
+{
+ ::printf("%s version %s release date %s, %s version %s\n",
+ VER_PRODUCT_NAME_STR,
+ VER_PRODUCT_VERSION_STR,
+ VER_PRODUCT_DATE_STR,
+ VER_MODULE_NAME_STR,
+ VER_FILE_VERSION_STR
+ );
+}
+
+
+//-------------------------------------------------------------------
+
+int main(int argc, char ** argv)
+{
+ Common::iLogger::instance()->setSeverity(Common::iLogger::Warning);
+
+ // Install our own message handlers
+ Common::iLogger::instance()->installFatalMsgHandler(Internal::fatalMsgHandler);
+ qInstallMsgHandler(Internal::messageOutput);
+
+ // Process command-line arguments
+ if (!Application::processCommandLine(argc, argv))
+ return 1;
+
+ // Set the console severity
+ Common::iLogger::instance()->setConsoleSeverity(Internal::ConsoleSeverityLevel);
+
+#ifdef Q_OS_WIN32
+ // Enable the extra message console on Windows
+ if (Internal::NeedsConsole)
+ Internal::enableWinConsole();
+#endif
+
+ EVAF_INFO("%s version %s release date %s, %s version %s",
+ VER_PRODUCT_NAME_STR,
+ VER_PRODUCT_VERSION_STR,
+ VER_PRODUCT_DATE_STR,
+ VER_MODULE_NAME_STR,
+ VER_FILE_VERSION_STR);
+
+#ifdef Q_OS_LINUX
+ EVAF_INFO("%s application pid = %d", VER_MODULE_NAME_STR, getpid());
+#endif
+
+ Application app(argc, argv);
+
+ // Install the exit handler
+ if (!Internal::installExitHandler())
+ return 1;
+
+ // Plugin manager
+ // Plugins::PluginManager pluginManager;
+
+ // The main run loop
+ bool quit = false;
+ int rval;
+ while (!quit) {
+
+ EVAF_INFO("%s is starting up", VER_MODULE_NAME_STR);
+
+ // Initialize the common library
+ if (!Common::init())
+ return 1;
+
+ // Initialize the plugin manager and load plugins
+ //if (!pluginManager.init())
+ // return 1;
+
+ // Run the application
+ rval = app.exec();
+
+ quit = rval != Common::iApp::RC_Restart;
+
+ EVAF_INFO("%s is %s", VER_MODULE_NAME_STR, quit ? "exiting" : "restarting");
+
+ // Unload plugins and finalize the plugin manager
+ // pluginManager.done();
+ }
+
+ EVAF_INFO("%s exit with code %d", VER_MODULE_NAME_STR, rval);
+
+ return rval;
+}