/**************************************************************** * This file is distributed under the following license: * * Copyright (c) 2006-2007, crypton * Copyright (c) 2006, Matt Edman, Justin Hipple * * 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. ****************************************************************/ #include #include #include #include #include #include #include #include #include #include #include "gui/settings/rsharesettings.h" #include "rshare.h" /* Available command-line arguments. */ #define ARG_LANGUAGE "lang" /**< Argument specifying language. */ #define ARG_GUISTYLE "style" /**< Argument specfying GUI style. */ #define ARG_GUISTYLESHEET "stylesheet" /**< Argument specfying GUI style. */ #define ARG_RESET "reset" /**< Reset Rshare's saved settings. */ #define ARG_DATADIR "datadir" /**< Directory to use for data files. */ #define ARG_LOGFILE "logfile" /**< Location of our logfile. */ #define ARG_LOGLEVEL "loglevel" /**< Log verbosity. */ /* Static member variables */ QMap Rshare::_args; /**< List of command-line arguments. */ QString Rshare::_style; /**< The current GUI style. */ QString Rshare::_language; /**< The current language. */ QString Rshare::_stylesheet; /**< The current GUI stylesheet. */ Log Rshare::_log; bool Rshare::useConfigDir; QString Rshare::configDir; /** Catches debugging messages from Qt and sends them to RetroShare's logs. If Qt * emits a QtFatalMsg, we will write the message to the log and then abort(). */ void Rshare::qt_msg_handler(QtMsgType type, const char *s) { QString msg(s); switch (type) { case QtDebugMsg: rDebug("QtDebugMsg: %1").arg(msg); break; case QtWarningMsg: rNotice("QtWarningMsg: %1").arg(msg); break; case QtCriticalMsg: rWarn("QtCriticalMsg: %1").arg(msg); break; case QtFatalMsg: rError("QtFatalMsg: %1").arg(msg); break; } if (type == QtFatalMsg) { rError("Fatal Qt error. Aborting."); abort(); } } /** Constructor. Parses the command-line arguments, resets Rshare's * configuration (if requested), and sets up the GUI style and language * translation. */ Rshare::Rshare(QStringList args, int &argc, char **argv, QString dir) : QApplication(argc, argv) { qInstallMsgHandler(qt_msg_handler); /* Read in all our command-line arguments. */ parseArguments(args); /* Check if we're supposed to reset our config before proceeding. */ if (_args.contains(ARG_RESET)) { Settings->reset(); } /* Handle the -loglevel and -logfile options. */ if (_args.contains(ARG_LOGFILE)) _log.open(_args.value(ARG_LOGFILE)); if (_args.contains(ARG_LOGLEVEL)) { _log.setLogLevel(Log::stringToLogLevel( _args.value(ARG_LOGLEVEL))); if (!_args.contains(ARG_LOGFILE)) _log.open(stdout); } if (!_args.contains(ARG_LOGLEVEL) && !_args.contains(ARG_LOGFILE)) _log.setLogLevel(Log::Off); /* config directory */ useConfigDir = false; if (dir != "") { setConfigDirectory(dir); } /** Initialize support for language translations. */ //LanguageSupport::initialize(); /** Translate the GUI to the appropriate language. */ setLanguage(_args.value(ARG_LANGUAGE)); /** Set the GUI style appropriately. */ setStyle(_args.value(ARG_GUISTYLE)); /** Set the GUI stylesheet appropriately. */ setSheet(_args.value(ARG_GUISTYLESHEET)); /* Switch off auto shutdown */ setQuitOnLastWindowClosed ( false ); } /** Destructor */ Rshare::~Rshare() { } /** Enters the main event loop and waits until exit() is called. The signal * running() will be emitted when the event loop has started. */ int Rshare::run() { QTimer::singleShot(0, rApp, SLOT(onEventLoopStarted())); return rApp->exec(); } /** Called when the application's main event loop has started. This method * will emit the running() signal to indicate that the application's event * loop is running. */ void Rshare::onEventLoopStarted() { emit running(); } #if defined(Q_OS_WIN) /** On Windows, we need to catch the WM_QUERYENDSESSION message * so we know that it is time to shutdown. */ bool Rshare::winEventFilter(MSG *msg, long *result) { if (msg->message == WM_QUERYENDSESSION) { emit shutdown(); } return QApplication::winEventFilter(msg, result); } #endif /** Display usage information regarding command-line arguments. */ /*void Rshare::printUsage(QString errmsg) { QTextStream out(stdout);*/ /* If there was an error message, print it out. */ /*if (!errmsg.isEmpty()) { out << "** " << errmsg << " **" << endl << endl; }*/ /* Now print the application usage */ //out << "Usage: " << endl; //out << "\t" << qApp->arguments().at(0) << " [options]" << endl; /* And available options */ //out << endl << "Available Options:" << endl; //out << "\t-"ARG_RESET"\t\tResets ALL stored Rshare settings." << endl; //out << "\t-"ARG_DATADIR"\tSets the directory Rshare uses for data files"<< endl; //out << "\t-"ARG_GUISTYLE"\t\tSets Rshare's interface style." << endl; //out << "\t-"ARG_GUISTYLESHEET"\t\tSets Rshare's stylesheet." << endl; //out << "\t\t\t[" << QStyleFactory::keys().join("|") << "]" << endl; //out << "\t-"ARG_LANGUAGE"\t\tSets Rshare's language." << endl; //out << "\t\t\t[" << LanguageSupport::languageCodes().join("|") << "]" << endl; //} /** Displays usage information for command-line args. */ void Rshare::showUsageMessageBox() { QString usage; QTextStream out(&usage); out << "Available Options:" << endl; out << ""; //out << trow(tcol("-"ARG_HELP) + // tcol(tr("Displays this usage message and exits."))); out << trow(tcol("-"ARG_RESET) + tcol(tr("Resets ALL stored RetroShare settings."))); out << trow(tcol("-"ARG_DATADIR" <dir>") + tcol(tr("Sets the directory RetroShare uses for data files."))); out << trow(tcol("-"ARG_LOGFILE" <file>") + tcol(tr("Sets the name and location of RetroShare's logfile."))); out << trow(tcol("-"ARG_LOGLEVEL" <level>") + tcol(tr("Sets the verbosity of Vidalia's logging.") + "
[" + Log::logLevels().join("|") +"]")); out << trow(tcol("-"ARG_GUISTYLE" <style>") + tcol(tr("Sets RetroShare's interface style.") + "
[" + QStyleFactory::keys().join("|") + "]")); out << trow(tcol("-"ARG_GUISTYLESHEET" <stylesheet>") + tcol(tr("Sets RetroShare's interface stylesheets."))); out << trow(tcol("-"ARG_LANGUAGE" <language>") + tcol(tr("Sets RetroShare's language.") + "
[" + LanguageSupport::languageCodes().join("|") + "]")); out << "
"; VMessageBox::information(0, tr("RetroShare Usage Information"), usage, VMessageBox::Ok); } /** Returns true if the specified argument expects a value. */ bool Rshare::argNeedsValue(QString argName) { return (argName == ARG_GUISTYLE || argName == ARG_GUISTYLESHEET || argName == ARG_LANGUAGE || argName == ARG_DATADIR || argName == ARG_LOGFILE || argName == ARG_LOGLEVEL); } /** Parses the list of command-line arguments for their argument names and * values. */ void Rshare::parseArguments(QStringList args) { QString arg, value; /* Loop through all command-line args/values and put them in a map */ for (int i = 0; i < args.size(); i++) { /* Get the argument name and set a blank value */ arg = args.at(i).toLower(); value = ""; /* Check if it starts with a - or -- */ if (arg.startsWith("-")) { arg = arg.mid((arg.startsWith("--") ? 2 : 1)); } /* Check if it takes a value and there is one on the command-line */ if (i < args.size()-1 && argNeedsValue(arg)) { value = args.at(++i); } /* Place this arg/value in the map */ _args.insert(arg, value); } } /** Verifies that all specified arguments were valid. */ bool Rshare::validateArguments(QString &errmsg) { /* Check for a language that Retroshare recognizes. */ if (_args.contains(ARG_LANGUAGE) && !LanguageSupport::isValidLanguageCode(_args.value(ARG_LANGUAGE))) { errmsg = tr("Invalid language code specified: ") + _args.value(ARG_LANGUAGE); return false; } /* Check for a valid GUI style */ if (_args.contains(ARG_GUISTYLE) && !QStyleFactory::keys().contains(_args.value(ARG_GUISTYLE), Qt::CaseInsensitive)) { errmsg = tr("Invalid GUI style specified: ") + _args.value(ARG_GUISTYLE); return false; } /* Check for a valid log level */ if (_args.contains(ARG_LOGLEVEL) && !Log::logLevels().contains(_args.value(ARG_LOGLEVEL))) { errmsg = tr("Invalid log level specified: ") + _args.value(ARG_LOGLEVEL); return false; } /* Check for a writable log file */ if (_args.contains(ARG_LOGFILE) && !_log.isOpen()) { errmsg = tr("Unable to open log file '%1': %2") .arg(_args.value(ARG_LOGFILE)) .arg(_log.errorString()); return false; } return true; } /** Sets the translation RetroShare will use. If one was specified on the * command-line, we will use that. Otherwise, we'll check to see if one was * saved previously. If not, we'll default to one appropriate for the system * locale. */ bool Rshare::setLanguage(QString languageCode) { /* If the language code is empty, use the previously-saved setting */ if (languageCode.isEmpty()) { languageCode = Settings->getLanguageCode(); } /* Translate into the desired langauge */ if (LanguageSupport::translate(languageCode)) { _language = languageCode; return true; } return false; } /** Sets the GUI style RetroShare will use. If one was specified on the * command-line, we will use that. Otherwise, we'll check to see if one was * saved previously. If not, we'll default to one appropriate for the * operating system. */ bool Rshare::setStyle(QString styleKey) { /* If no style was specified, use the previously-saved setting */ if (styleKey.isEmpty()) { styleKey = Settings->getInterfaceStyle(); } /* Apply the specified GUI style */ if (QApplication::setStyle(styleKey)) { _style = styleKey; return true; } return false; } bool Rshare::setSheet(QString sheet) { /* If no stylesheet was specified, use the previously-saved setting */ if (sheet.isEmpty()) { sheet = Settings->getSheetName(); } /* Apply the specified GUI stylesheet */ _stylesheet = sheet; return true; } /** Returns the directory RetroShare uses for its data files. */ QString Rshare::dataDirectory() { if (useConfigDir) { return configDir; } else if (_args.contains(ARG_DATADIR)) { return _args.value(ARG_DATADIR); } return defaultDataDirectory(); } /** Returns the default location of RetroShare's data directory. */ QString Rshare::defaultDataDirectory() { #if defined(Q_OS_WIN32) return (win32_app_data_folder() + "\\RetroShare"); #else return (QDir::homePath() + "/.RetroShare"); #endif } /** Creates Rshare's data directory, if it doesn't already exist. */ bool Rshare::createDataDirectory(QString *errmsg) { QDir datadir(dataDirectory()); if (!datadir.exists()) { QString path = datadir.absolutePath(); if (!datadir.mkpath(path)) { return err(errmsg, QString("Could not create data directory: %1").arg(path)); } } return true; } /** Set Rshare's data directory - externally */ bool Rshare::setConfigDirectory(QString dir) { useConfigDir = true; configDir = dir; return true; } /** Writes msg with severity level to Vidalia's log. */ Log::LogMessage Rshare::log(Log::LogLevel level, QString msg) { return _log.log(level, msg); } /** Creates and binds a shortcut such that when key is pressed in * sender's context, receiver's slot will be called. */ void Rshare::createShortcut(const QKeySequence &key, QWidget *sender, QWidget *receiver, const char *slot) { QShortcut *s = new QShortcut(key, sender); connect(s, SIGNAL(activated()), receiver, slot); }