1
0
mirror of https://bitbucket.org/librepilot/librepilot.git synced 2024-12-01 09:24:10 +01:00

OP-69 allow to set language (translation) in use for first launch through

factory defaults
OP-723 do not hard code locale to en_US in order to allow locale specific
display and entry of numbers and dates
+ revisited the GCS startup sequence
This commit is contained in:
Philippe Renon 2013-04-11 19:47:05 +02:00
parent 50e66d6ee3
commit 42022df009

View File

@ -26,6 +26,32 @@
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
/*
The GCS locale is set to the system locale by default unless the "hidden" setting General/Locale has a value.
The user can not change General/Locale from the Options dialog.
The GCS language will default to the GCS locale unless the General/OverrideLanguage has a value.
The user can change General/OverrideLanguage to any available language from the Options dialog.
Both General/Locale and General/OverrideLanguage can be set from the command line or through the the factory defaults file.
The -reset switch will clear all the user settings and will trigger a reload of the factory defaults.
You can combine it with the -configfile=<file> command line argument to quickly switch between multiple settings files.
[code]
openpilotgcs -reset -configfile=./MyOpenPilotGCS.xml
[/code]
The specified file will be used to load the factory defaults from but only when the user settings are empty.
If the user settings are not empty the file will not be used.
This switch is useful on the 1st run when the user settings are empty or in combination with -reset.
*/
#include "qtsingleapplication.h"
#include "utils/xmlconfig.h"
#include "gcssplashscreen.h"
@ -35,6 +61,7 @@
#include <extensionsystem/iplugin.h>
#include <QtCore/QDir>
#include <QtCore/QElapsedTimer>
#include <QtCore/QTextStream>
#include <QtCore/QFileInfo>
#include <QtCore/QDebug>
@ -50,12 +77,28 @@
#include <QtGui/QSplashScreen>
#include <QtGui/QPainter>
#include <QElapsedTimer>
namespace {
enum { OptionIndent = 4, DescriptionIndent = 24 };
typedef QList<ExtensionSystem::PluginSpec *> PluginSpecSet;
typedef QMap<QString, bool> AppOptions;
typedef QMap<QString, QString> FoundAppOptions;
enum {
OptionIndent = 4, DescriptionIndent = 24
};
static const char *appNameC = "OpenPilot GCS";
static const char *corePluginNameC = "Core";
#ifdef Q_OS_MAC
static const char *SHARE_PATH = "/../Resources";
#else
static const char *SHARE_PATH = "/../share/openpilotgcs";
#endif
static const char *DEFAULT_CONFIG_FILENAME = "OpenPilotGCS.xml";
static const char *fixedOptionsC =
" [OPTION]... [FILE]...\n"
"Options:\n"
@ -65,25 +108,24 @@ static const char *fixedOptionsC =
" -clean-config Delete all existing configuration settings\n"
" -exit-after-config Exit GCS after manipulating configuration settings\n"
" -D key=value Override configuration settings e.g: -D General/OverrideLanguage=de\n"
" -configfile=value Default configuration file to load if settings file is empty\n";
static const char *HELP_OPTION1 = "-h";
static const char *HELP_OPTION2 = "-help";
static const char *HELP_OPTION3 = "/h";
static const char *HELP_OPTION4 = "--help";
static const char *VERSION_OPTION = "-version";
static const char *CLIENT_OPTION = "-client";
static const char *CONFIG_OPTION = "-D";
static const char *CLEAN_CONFIG_OPTION = "-clean-config";
static const char *EXIT_AFTER_CONFIG_OPTION = "-exit-after-config";
" -reset Reset user settings to factory defaults.\n";
typedef QList<ExtensionSystem::PluginSpec *> PluginSpecSet;
static const char *DEFAULT_CONFIG_FILENAME = "OpenPilotGCS.xml";
static QLatin1String HELP_OPTION1("-h");
static QLatin1String HELP_OPTION2("-help");
static QLatin1String HELP_OPTION3("/h");
static QLatin1String HELP_OPTION4("--help");
static QLatin1String VERSION_OPTION("-version");
static QLatin1String CLIENT_OPTION("-client");
static QLatin1String CONFIG_OPTION("-D");
static QLatin1String CLEAN_CONFIG_OPTION("-clean-config");
static QLatin1String EXIT_AFTER_CONFIG_OPTION("-exit-after-config");
static QLatin1String RESET("-reset");
static QLatin1String NO_SPLASH("-no-splash");
// Helpers for displaying messages. Note that there is no console on Windows.
#ifdef Q_OS_WIN
// Format as <pre> HTML
static inline void toHtml(QString &t)
inline void toHtml(QString &t)
{
t.replace(QLatin1Char('&'), QLatin1String("&amp;"));
t.replace(QLatin1Char('<'), QLatin1String("&lt;"));
@ -92,58 +134,57 @@ static inline void toHtml(QString &t)
t.append(QLatin1String("</pre></html>"));
}
static void displayHelpText(QString t) // No console on Windows.
void displayHelpText(QString t) // No console on Windows.
{
toHtml(t);
QMessageBox::information(0, QLatin1String(appNameC), t);
}
static void displayError(const QString &t) // No console on Windows.
void displayError(const QString &t) // No console on Windows.
{
QMessageBox::critical(0, QLatin1String(appNameC), t);
}
#else
static void displayHelpText(const QString &t)
void displayHelpText(const QString &t)
{
qWarning("%s", qPrintable(t));
}
static void displayError(const QString &t)
void displayError(const QString &t)
{
qCritical("%s", qPrintable(t));
}
#endif
static void printVersion(const ExtensionSystem::PluginSpec *coreplugin,
const ExtensionSystem::PluginManager &pm)
void printVersion(const ExtensionSystem::PluginSpec *coreplugin, const ExtensionSystem::PluginManager &pm)
{
QString version;
QTextStream str(&version);
str << '\n' << appNameC << ' ' << coreplugin->version()<< " based on Qt " << qVersion() << "\n\n";
str << '\n' << appNameC << ' ' << coreplugin->version() << " based on Qt " << qVersion() << "\n\n";
pm.formatPluginVersions(str);
str << '\n' << coreplugin->copyright() << '\n';
displayHelpText(version);
}
static void printHelp(const QString &a0, const ExtensionSystem::PluginManager &pm)
void printHelp(const QString &a0, const ExtensionSystem::PluginManager &pm)
{
QString help;
QTextStream str(&help);
str << "Usage: " << a0 << fixedOptionsC;
str << "Usage: " << a0 << fixedOptionsC;
ExtensionSystem::PluginManager::formatOptions(str, OptionIndent, DescriptionIndent);
pm.formatPluginOptions(str, OptionIndent, DescriptionIndent);
pm.formatPluginOptions(str, OptionIndent, DescriptionIndent);
displayHelpText(help);
}
static inline QString msgCoreLoadFailure(const QString &why)
inline QString msgCoreLoadFailure(const QString &why)
{
return QCoreApplication::translate("Application", "Failed to load core: %1").arg(why);
}
static inline QString msgSendArgumentFailed()
inline QString msgSendArgumentFailed()
{
return QCoreApplication::translate("Application", "Unable to send command line arguments to the already running instance. It appears to be not responding.");
}
@ -151,18 +192,20 @@ static inline QString msgSendArgumentFailed()
// Prepare a remote argument: If it is a relative file, add the current directory
// since the the central instance might be running in a different directory.
static inline QString prepareRemoteArgument(const QString &a)
inline QString prepareRemoteArgument(const QString &a)
{
QFileInfo fi(a);
if (!fi.exists())
if (!fi.exists()) {
return a;
if (fi.isRelative())
}
if (fi.isRelative()) {
return fi.absoluteFilePath();
}
return a;
}
// Send the arguments to an already running instance of OpenPilot GCS
static bool sendArguments(SharedTools::QtSingleApplication &app, const QStringList &arguments)
bool sendArguments(SharedTools::QtSingleApplication &app, const QStringList &arguments)
{
if (!arguments.empty()) {
// Send off arguments
@ -182,7 +225,21 @@ static bool sendArguments(SharedTools::QtSingleApplication &app, const QStringLi
return true;
}
static inline QStringList getPluginPaths()
void systemInit()
{
#ifdef Q_OS_MAC
// increase the number of file that can be opened in OpenPilot GCS
struct rlimit rl;
getrlimit(RLIMIT_NOFILE, &rl);
rl.rlim_cur = rl.rlim_max;
setrlimit(RLIMIT_NOFILE, &rl);
#endif
#ifdef Q_OS_LINUX
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
#endif
}
inline QStringList getPluginPaths()
{
QStringList rc;
// Figure out root: Up one from 'bin'
@ -206,22 +263,43 @@ static inline QStringList getPluginPaths()
return rc;
}
static void loadFactorySettings(QSettings &settings)
AppOptions options()
{
QDir directory(QCoreApplication::applicationDirPath());
#ifdef Q_OS_MAC
directory.cdUp();
directory.cd("Resources");
#else
directory.cdUp();
directory.cd("share");
directory.cd("openpilotgcs");
#endif
directory.cd("default_configurations");
AppOptions appOptions;
appOptions.insert(HELP_OPTION1, false);
appOptions.insert(HELP_OPTION2, false);
appOptions.insert(HELP_OPTION3, false);
appOptions.insert(HELP_OPTION4, false);
appOptions.insert(VERSION_OPTION, false);
appOptions.insert(CLIENT_OPTION, false);
appOptions.insert(CONFIG_OPTION, true);
appOptions.insert(CLEAN_CONFIG_OPTION, false);
appOptions.insert(EXIT_AFTER_CONFIG_OPTION, false);
appOptions.insert(RESET, false);
appOptions.insert(NO_SPLASH, false);
return appOptions;
}
FoundAppOptions parseCommandLine(SharedTools::QtSingleApplication &app, ExtensionSystem::PluginManager &pluginManager, QString &errorMessage)
{
FoundAppOptions foundAppOptions;
const QStringList arguments = app.arguments();
if (arguments.size() > 1) {
AppOptions appOptions = options();
if (!pluginManager.parseOptions(arguments, appOptions, &foundAppOptions, &errorMessage)) {
// displayError(errorMessage);
// printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager);
}
}
return foundAppOptions;
}
void loadFactoryDefaults(QSettings &settings)
{
QDir directory(QCoreApplication::applicationDirPath() + QString(SHARE_PATH) + QString("/default_configurations"));
qDebug() << "Looking for configuration files in:" << directory.absolutePath();
QString filename;
// check if command line contains a config file name
QString commandLine;
foreach(QString str, qApp->arguments()) {
@ -229,6 +307,7 @@ static void loadFactorySettings(QSettings &settings)
commandLine = str.split("=").at(1);
}
}
QString filename;
if (!commandLine.isEmpty() && QFile::exists(directory.absolutePath() + QDir::separator() + commandLine)) {
// use file name specified on command line
filename = directory.absolutePath() + QDir::separator() + commandLine;
@ -244,21 +323,18 @@ static void loadFactorySettings(QSettings &settings)
}
// create settings from file
QSettings *qs = new QSettings(filename, XmlConfig::XmlSettingsFormat);
QSettings qs(filename, XmlConfig::XmlSettingsFormat);
// transfer loaded settings to application settings
QStringList keys = qs->allKeys();
QStringList keys = qs.allKeys();
foreach(QString key, keys) {
settings.setValue(key, qs->value(key));
settings.setValue(key, qs.value(key));
}
// and delete loaded settings
delete qs;
qDebug() << "Configuration file" << filename << "was loaded.";
}
static void overrideSettings(QSettings &settings, int argc, char **argv)
void overrideSettings(QSettings &settings, int argc, char **argv)
{
// Options like -DMy/setting=test
QRegExp rx("([^=]+)=(.*)");
@ -277,73 +353,24 @@ static void overrideSettings(QSettings &settings, int argc, char **argv)
QList<QString> keys = settingOptions.keys();
foreach (QString key, keys) {
qDebug() << "Overriding user setting:" << key << "with value" << settingOptions.value(key);
settings.setValue(key, settingOptions.value(key));
}
settings.sync();
}
#ifdef Q_OS_MAC
# define SHARE_PATH "/../Resources"
#else
# define SHARE_PATH "/../share/openpilotgcs"
#endif
int main(int argc, char **argv)
void loadTranslators(QString language)
{
QElapsedTimer timer;
timer.start();
// TODO static!?!
static QTranslator translator;
static QTranslator qtTranslator;
#ifdef Q_OS_MAC
// increase the number of file that can be opened in OpenPilot GCS
struct rlimit rl;
getrlimit(RLIMIT_NOFILE, &rl);
rl.rlim_cur = rl.rlim_max;
setrlimit(RLIMIT_NOFILE, &rl);
#endif
#ifdef Q_OS_LINUX
QApplication::setAttribute(Qt::AA_X11InitThreads, true);
#endif
// Set the default locale to EN, if this is not set the system locale will be used
// and as of now we dont want that behaviour.
QLocale::setDefault(QLocale::English);
SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv);
// Open splash screen
GCSSplashScreen splash;
splash.show();
// Must be done before any QSettings class is created
QSettings::setPath(XmlConfig::XmlSettingsFormat, QSettings::SystemScope,
QCoreApplication::applicationDirPath() + QLatin1String(SHARE_PATH));
// keep this in sync with the MainWindow ctor in coreplugin/mainwindow.cpp
QSettings settings(XmlConfig::XmlSettingsFormat, QSettings::UserScope, QLatin1String("OpenPilot"),
QLatin1String("OpenPilotGCS_config"));
if (!settings.allKeys().count()) {
qDebug() << "No user setting, loading factory defaults.";
// no user settings, so try to load some default ones
loadFactorySettings(settings);
}
// override setting with command line provided settings
overrideSettings(settings, argc, argv);
QString locale = QLocale::system().name();
qDebug() << "main - system locale:" << locale;
QString language = QLocale::system().name();
language = settings.value("General/OverrideLanguage", language).toString();
qDebug() << "main - language:" << language;
QTranslator translator;
const QString &creatorTrPath = QCoreApplication::applicationDirPath() + QLatin1String(SHARE_PATH "/translations");
const QString &creatorTrPath = QCoreApplication::applicationDirPath() + QLatin1String(SHARE_PATH) + QLatin1String("/translations");
if (translator.load(QLatin1String("openpilotgcs_") + language, creatorTrPath)) {
const QString &qtTrPath = QLibraryInfo::location(QLibraryInfo::TranslationsPath);
const QString &qtTrFile = QLatin1String("qt_") + language;
// Binary installer puts Qt tr files into creatorTrPath
QTranslator qtTranslator;
if (qtTranslator.load(qtTrFile, qtTrPath) || qtTranslator.load(qtTrFile, creatorTrPath)) {
QCoreApplication::installTranslator(&translator);
QCoreApplication::installTranslator(&qtTranslator);
@ -352,38 +379,98 @@ int main(int argc, char **argv)
translator.load(QString());
}
}
app.setProperty("qtc_locale", locale); // Do we need this?
}
splash.showProgressMessage(QObject::tr("Application starting..."));
} // namespace anonymous
// Load
int main(int argc, char **argv)
{
QElapsedTimer timer;
timer.start();
// low level init
systemInit();
// create application
SharedTools::QtSingleApplication app((QLatin1String(appNameC)), argc, argv);
// initialize the plugin manager
ExtensionSystem::PluginManager pluginManager;
pluginManager.setFileExtension(QLatin1String("pluginspec"));
pluginManager.setPluginPaths(getPluginPaths());
const QStringList pluginPaths = getPluginPaths();
pluginManager.setPluginPaths(pluginPaths);
const QStringList arguments = app.arguments();
QMap<QString, QString> foundAppOptions;
if (arguments.size() > 1) {
QMap<QString, bool> appOptions;
appOptions.insert(QLatin1String(HELP_OPTION1), false);
appOptions.insert(QLatin1String(HELP_OPTION2), false);
appOptions.insert(QLatin1String(HELP_OPTION3), false);
appOptions.insert(QLatin1String(HELP_OPTION4), false);
appOptions.insert(QLatin1String(VERSION_OPTION), false);
appOptions.insert(QLatin1String(CLIENT_OPTION), false);
appOptions.insert(QLatin1String(CONFIG_OPTION), true);
appOptions.insert(QLatin1String(CLEAN_CONFIG_OPTION), false);
appOptions.insert(QLatin1String(EXIT_AFTER_CONFIG_OPTION), false);
QString errorMessage;
if (!pluginManager.parseOptions(arguments, appOptions, &foundAppOptions, &errorMessage)) {
displayError(errorMessage);
printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager);
return -1;
}
// parse command line
qDebug() << "Command line" << app.arguments();;
QString errorMessage;
FoundAppOptions foundAppOptions = parseCommandLine(app, pluginManager, errorMessage);
if (!errorMessage.isEmpty()) {
displayError(errorMessage);
printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager);
return -1;
}
// load user settings
// Must be done before any QSettings class is created
// keep this in sync with the MainWindow ctor in coreplugin/mainwindow.cpp
QString settingsPath = QCoreApplication::applicationDirPath() + QLatin1String(SHARE_PATH);
qDebug() << "Loading user settings from" << settingsPath;
QSettings::setPath(XmlConfig::XmlSettingsFormat, QSettings::SystemScope, settingsPath);
QSettings settings(XmlConfig::XmlSettingsFormat, QSettings::UserScope, QLatin1String("OpenPilot"),
QLatin1String("OpenPilotGCS_config"));
// need to reset all user settings?
if (foundAppOptions.contains(RESET)) {
qDebug() << "Resetting user settings!";
settings.clear();
}
// check if we have user settings
if (!settings.allKeys().count()) {
// no user settings, load the factory defaults
qDebug() << "No user settings found, loading factory defaults...";
loadFactoryDefaults(settings);
}
// override settings with command line provided values
// take notice that the overridden values will be saved in the user settings and will continue to be effective
// in subsequent GCS runs
overrideSettings(settings, argc, argv);
// initialize GCS locale
// use the value defined by the General/Locale setting or default to system locale.
// the General/Locale setting is not available in the Options dialog, it is a hidden setting but can still be changed:
// - through the command line
// - editing the factory defaults XML file before 1st launch
// - editing the user XML file
QString localeName = settings.value("General/Locale", QLocale::system().name()).toString();
QLocale::setDefault(localeName);
// some debuging
qDebug() << "main - system locale:" << QLocale::system().name();
qDebug() << "main - GCS locale:" << QLocale().name();
// load translation file
// the language used is defined by the General/OverrideLanguage setting (defaults to GCS locale)
// if the translation file for the given language is not found, GCS will default to built in English.
QString language = settings.value("General/OverrideLanguage", localeName).toString();
qDebug() << "main - translation language:" << language;
loadTranslators(language);
app.setProperty("qtc_locale", localeName); // Do we need this?
// open the splash screen
GCSSplashScreen *splash = 0;
if (!foundAppOptions.contains(NO_SPLASH)) {
splash = new GCSSplashScreen();
// show splash
splash->showProgressMessage(QObject::tr("Application starting..."));
splash->show();
// connect to track progress of plugin manager
QObject::connect(&pluginManager, SIGNAL(pluginAboutToBeLoaded(ExtensionSystem::PluginSpec*)),
splash, SLOT(showPluginLoadingProgress(ExtensionSystem::PluginSpec*)));
}
// find and load core plugin
const PluginSpecSet plugins = pluginManager.plugins();
ExtensionSystem::PluginSpec *coreplugin = 0;
foreach (ExtensionSystem::PluginSpec *spec, plugins) {
@ -393,7 +480,7 @@ int main(int argc, char **argv)
}
}
if (!coreplugin) {
QString nativePaths = QDir::toNativeSeparators(pluginPaths.join(QLatin1String(",")));
QString nativePaths = QDir::toNativeSeparators(getPluginPaths().join(QLatin1String(",")));
const QString reason = QCoreApplication::translate("Application", "Could not find 'Core.pluginspec' in %1").arg(
nativePaths);
displayError(msgCoreLoadFailure(reason));
@ -403,28 +490,27 @@ int main(int argc, char **argv)
displayError(msgCoreLoadFailure(coreplugin->errorString()));
return 1;
}
if (foundAppOptions.contains(QLatin1String(VERSION_OPTION))) {
if (foundAppOptions.contains(VERSION_OPTION)) {
printVersion(coreplugin, pluginManager);
return 0;
}
if (foundAppOptions.contains(QLatin1String(EXIT_AFTER_CONFIG_OPTION))) {
if (foundAppOptions.contains(EXIT_AFTER_CONFIG_OPTION)) {
return 0;
}
if (foundAppOptions.contains(QLatin1String(HELP_OPTION1))
|| foundAppOptions.contains(QLatin1String(HELP_OPTION2))
|| foundAppOptions.contains(QLatin1String(HELP_OPTION3))
|| foundAppOptions.contains(QLatin1String(HELP_OPTION4))) {
if (foundAppOptions.contains(HELP_OPTION1)
|| foundAppOptions.contains(HELP_OPTION2)
|| foundAppOptions.contains(HELP_OPTION3)
|| foundAppOptions.contains(HELP_OPTION4)) {
printHelp(QFileInfo(app.applicationFilePath()).baseName(), pluginManager);
return 0;
}
const bool isFirstInstance = !app.isRunning();
if (!isFirstInstance && foundAppOptions.contains(QLatin1String(CLIENT_OPTION))) {
if (!isFirstInstance && foundAppOptions.contains(CLIENT_OPTION)) {
return sendArguments(app, pluginManager.arguments()) ? 0 : -1;
}
QObject::connect(&pluginManager, SIGNAL(pluginAboutToBeLoaded(ExtensionSystem::PluginSpec*)),
&splash, SLOT(showPluginLoadingProgress(ExtensionSystem::PluginSpec*)));
pluginManager.loadPlugins();
if (coreplugin->hasError()) {
@ -434,19 +520,21 @@ int main(int argc, char **argv)
{
QStringList errors;
foreach (ExtensionSystem::PluginSpec *p, pluginManager.plugins())
if (p->hasError())
foreach (ExtensionSystem::PluginSpec *p, pluginManager.plugins()) {
if (p->hasError()) {
errors.append(p->errorString());
if (!errors.isEmpty())
}
}
if (!errors.isEmpty()) {
QMessageBox::warning(0,
QCoreApplication::translate("Application", "OpenPilot GCS - Plugin loader messages"),
errors.join(QString::fromLatin1("\n\n")));
QCoreApplication::translate("Application", "OpenPilot GCS - Plugin loader messages"),
errors.join(QString::fromLatin1("\n\n")));
}
}
if (isFirstInstance) {
// Set up lock and remote arguments for the first instance only.
// Silently fallback to unconnected instances for any subsequent
// instances.
// Silently fallback to unconnected instances for any subsequent instances.
app.initialize();
QObject::connect(&app, SIGNAL(messageReceived(QString)), coreplugin->plugin(), SLOT(remoteArgument(QString)));
}
@ -455,9 +543,12 @@ int main(int argc, char **argv)
// Do this after the event loop has started
QTimer::singleShot(100, &pluginManager, SLOT(startTests()));
//Update message and postpone closing of splashscreen 3 seconds
splash.showProgressMessage(QObject::tr("Application started."));
QTimer::singleShot(1500, &splash, SLOT(close()));
if (splash) {
// Update message and postpone closing of splashscreen 3 seconds
splash->showProgressMessage(QObject::tr("Application started."));
QTimer::singleShot(1500, splash, SLOT(close()));
// TODO delete splash
}
qDebug() << "main - main took" << timer.elapsed() << "ms";