2015-03-07 23:52:03 +00:00
|
|
|
// QtLauncher.cxx - GUI launcher dialog using Qt5
|
|
|
|
//
|
|
|
|
// Written by James Turner, started December 2014.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2014 James Turner <zakalawe@mac.com>
|
|
|
|
//
|
|
|
|
// 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.
|
|
|
|
|
2018-08-28 22:34:44 +00:00
|
|
|
#include "config.h"
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
#include "QtLauncher.hxx"
|
|
|
|
|
2016-02-02 20:44:46 +00:00
|
|
|
#include <locale.h>
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
// Qt
|
2017-05-16 11:51:46 +00:00
|
|
|
#include <QtGlobal>
|
|
|
|
#include <QString>
|
2014-12-26 12:20:51 +00:00
|
|
|
#include <QProgressDialog>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFileInfo>
|
|
|
|
#include <QTimer>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QSettings>
|
|
|
|
#include <QUrl>
|
2015-03-06 18:52:06 +00:00
|
|
|
#include <QApplication>
|
2016-08-08 22:13:57 +00:00
|
|
|
#include <QThread>
|
2017-02-20 00:19:13 +00:00
|
|
|
#include <QProcess>
|
2018-08-27 12:20:23 +00:00
|
|
|
#include <QTranslator>
|
2020-05-03 17:02:20 +00:00
|
|
|
#include <QMessageBox>
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
// Simgear
|
|
|
|
#include <simgear/timing/timestamp.hxx>
|
|
|
|
#include <simgear/props/props_io.hxx>
|
|
|
|
#include <simgear/structure/exception.hxx>
|
2015-03-10 08:30:55 +00:00
|
|
|
#include <simgear/structure/subsystem_mgr.hxx>
|
2014-12-26 12:20:51 +00:00
|
|
|
#include <simgear/misc/sg_path.hxx>
|
2020-03-17 11:46:26 +00:00
|
|
|
#include <simgear/package/Root.hxx>
|
2015-08-03 20:53:56 +00:00
|
|
|
#include <simgear/package/Catalog.hxx>
|
|
|
|
#include <simgear/package/Package.hxx>
|
|
|
|
#include <simgear/package/Install.hxx>
|
2017-01-29 15:16:28 +00:00
|
|
|
#include <simgear/debug/logstream.hxx>
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
#include <Main/globals.hxx>
|
2016-07-08 08:36:42 +00:00
|
|
|
#include <Main/fg_props.hxx>
|
2014-12-26 12:20:51 +00:00
|
|
|
#include <Navaids/NavDataCache.hxx>
|
2015-10-16 01:05:17 +00:00
|
|
|
#include <Navaids/navrecord.hxx>
|
2015-11-26 16:48:23 +00:00
|
|
|
#include <Navaids/SHPParser.hxx>
|
2017-01-25 20:41:40 +00:00
|
|
|
#include <Airports/airport.hxx>
|
2019-02-02 20:46:09 +00:00
|
|
|
#include <Add-ons/AddonManager.hxx>
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2015-03-10 08:30:55 +00:00
|
|
|
#include <Main/fg_init.hxx>
|
2020-06-17 15:15:01 +00:00
|
|
|
#include <Main/locale.hxx>
|
|
|
|
#include <Main/options.hxx>
|
2015-03-10 08:30:55 +00:00
|
|
|
#include <Network/HTTPClient.hxx>
|
2020-06-17 15:15:01 +00:00
|
|
|
#include <Viewer/WindowBuilder.hxx>
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
#include "LauncherMainWindow.hxx"
|
2017-07-19 18:24:33 +00:00
|
|
|
#include "LaunchConfig.hxx"
|
2018-07-17 10:55:25 +00:00
|
|
|
#include "UnitsModel.hxx"
|
2020-02-24 11:32:43 +00:00
|
|
|
#include "PathListModel.hxx"
|
2017-01-30 07:00:37 +00:00
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
using namespace flightgear;
|
2015-08-03 20:53:56 +00:00
|
|
|
using namespace simgear::pkg;
|
Split flightgear::initApp() to make it usable even without FGGlobals initialized
- Add an optional argument to flightgear::initApp(): doInitQSettings.
This argument defaults to true, preserving initApp()'s behavior in
this respect. If this argument is set to false, FGGlobals doesn't have
to be initialized.
- New function flightgear::initQSettings(), called by
flightgear::initApp() when its 'doInitQSettings' argument is true.
This allows initializing the QSettings exactly when it is needed.
- New function flightgear::checkKeyboardModifiersForSettingFGRoot().
The code it contains used to be run from initApp(), which is
undesirable because:
1) initApp() is not only called at FG initialization (fgMainInit()),
but also from QtMessageBox(), from QtFileDialog::exec() and twice
from Options::setupRoot(). However, checking the Alt and Shift
modifiers to set 'fg-root' in QSettings to the special value
"!ask" only makes sense in fgMainInit(), not in these other
places.
2) This code relies on the QSettings to be set up, and therefore on
FGGlobals. Thus, freeing initApp() of its dependency on FGGlobals
requires splitting this keyboard modifiers checking code out of
initApp().
2016-12-26 20:45:44 +00:00
|
|
|
using std::string;
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
namespace { // anonymous namespace
|
|
|
|
|
2020-04-29 10:16:52 +00:00
|
|
|
struct ProgressLabel {
|
|
|
|
NavDataCache::RebuildPhase phase;
|
|
|
|
const char* label;
|
|
|
|
};
|
|
|
|
|
|
|
|
static std::initializer_list<ProgressLabel> progressStrings = {
|
|
|
|
{NavDataCache::REBUILD_READING_APT_DAT_FILES, QT_TRANSLATE_NOOP("initNavCache","Reading airport data")},
|
|
|
|
{NavDataCache::REBUILD_LOADING_AIRPORTS, QT_TRANSLATE_NOOP("initNavCache","Loading airports")},
|
|
|
|
{NavDataCache::REBUILD_FIXES, QT_TRANSLATE_NOOP("initNavCache","Loading waypoint data")},
|
|
|
|
{NavDataCache::REBUILD_NAVAIDS, QT_TRANSLATE_NOOP("initNavCache","Loading navigation data")},
|
|
|
|
{NavDataCache::REBUILD_POIS, QT_TRANSLATE_NOOP("initNavCache","Loading point-of-interest data")}
|
|
|
|
};
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
void initNavCache()
|
|
|
|
{
|
2020-04-29 10:16:52 +00:00
|
|
|
const char* baseLabelKey = QT_TRANSLATE_NOOP("initNavCache", "Initialising navigation data, this may take several minutes");
|
|
|
|
QString baseLabel= qApp->translate("initNavCache", baseLabelKey);
|
|
|
|
|
2015-03-08 00:40:22 +00:00
|
|
|
NavDataCache* cache = NavDataCache::createInstance();
|
2014-12-26 12:20:51 +00:00
|
|
|
if (cache->isRebuildRequired()) {
|
2015-03-19 17:01:38 +00:00
|
|
|
QProgressDialog rebuildProgress(baseLabel,
|
2014-12-26 12:20:51 +00:00
|
|
|
QString() /* cancel text */,
|
2016-04-13 14:15:27 +00:00
|
|
|
0, 100, Q_NULLPTR,
|
|
|
|
Qt::Dialog
|
|
|
|
| Qt::CustomizeWindowHint
|
|
|
|
| Qt::WindowTitleHint
|
|
|
|
| Qt::WindowSystemMenuHint
|
|
|
|
| Qt::MSWindowsFixedSizeDialogHint);
|
2014-12-26 12:20:51 +00:00
|
|
|
rebuildProgress.setWindowModality(Qt::WindowModal);
|
2020-05-12 21:00:43 +00:00
|
|
|
rebuildProgress.setMinimumWidth(600);
|
2020-05-18 10:47:11 +00:00
|
|
|
rebuildProgress.setAutoReset(false);
|
2020-05-18 10:46:17 +00:00
|
|
|
rebuildProgress.setAutoClose(false);
|
2014-12-26 12:20:51 +00:00
|
|
|
rebuildProgress.show();
|
2020-05-12 21:00:43 +00:00
|
|
|
|
|
|
|
QTimer updateTimer;
|
2020-05-13 14:51:42 +00:00
|
|
|
updateTimer.setInterval(100);
|
2020-05-12 21:00:43 +00:00
|
|
|
QObject::connect(&updateTimer, &QTimer::timeout, [&cache, &rebuildProgress, &baseLabel]() {
|
|
|
|
auto phase = cache->rebuild();
|
|
|
|
if (phase == NavDataCache::REBUILD_DONE) {
|
|
|
|
rebuildProgress.done(0);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-04-29 10:16:52 +00:00
|
|
|
auto it = std::find_if(progressStrings.begin(), progressStrings.end(), [phase]
|
|
|
|
(const ProgressLabel& l) { return l.phase == phase; });
|
|
|
|
if (it == progressStrings.end()) {
|
2015-03-19 17:01:38 +00:00
|
|
|
rebuildProgress.setLabelText(baseLabel);
|
2020-04-29 10:16:52 +00:00
|
|
|
} else {
|
|
|
|
QString trans = qApp->translate("initNavCache", it->label);
|
|
|
|
rebuildProgress.setLabelText(trans);
|
2015-03-19 17:01:38 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (phase == NavDataCache::REBUILD_UNKNOWN) {
|
|
|
|
rebuildProgress.setValue(0);
|
|
|
|
rebuildProgress.setMaximum(0);
|
|
|
|
} else {
|
2020-04-29 10:16:52 +00:00
|
|
|
rebuildProgress.setValue(static_cast<int>(cache->rebuildPhaseCompletionPercentage()));
|
2015-03-19 17:01:38 +00:00
|
|
|
rebuildProgress.setMaximum(100);
|
|
|
|
}
|
2020-05-12 21:00:43 +00:00
|
|
|
});
|
|
|
|
|
|
|
|
updateTimer.start(); // timer won't actually run until we process events
|
|
|
|
rebuildProgress.exec();
|
|
|
|
updateTimer.stop();
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-08 22:13:57 +00:00
|
|
|
class NaturalEarthDataLoaderThread : public QThread
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
|
2016-08-09 08:38:54 +00:00
|
|
|
NaturalEarthDataLoaderThread() :
|
|
|
|
m_lineInsertCount(0)
|
2016-08-08 22:13:57 +00:00
|
|
|
{
|
|
|
|
connect(this, &QThread::finished, this, &NaturalEarthDataLoaderThread::onFinished);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
2018-09-01 14:20:05 +00:00
|
|
|
void run() override
|
2016-08-08 22:13:57 +00:00
|
|
|
{
|
|
|
|
loadNaturalEarthFile("ne_10m_coastline.shp", flightgear::PolyLine::COASTLINE, false);
|
|
|
|
loadNaturalEarthFile("ne_10m_rivers_lake_centerlines.shp", flightgear::PolyLine::RIVER, false);
|
|
|
|
loadNaturalEarthFile("ne_10m_lakes.shp", flightgear::PolyLine::LAKE, true);
|
|
|
|
loadNaturalEarthFile("ne_10m_urban_areas.shp", flightgear::PolyLine::URBAN, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2016-08-15 22:10:06 +00:00
|
|
|
Q_SLOT void onFinished()
|
2016-08-08 22:13:57 +00:00
|
|
|
{
|
2016-08-09 08:38:54 +00:00
|
|
|
flightgear::PolyLineList::const_iterator begin = m_parsedLines.begin() + m_lineInsertCount;
|
2018-09-01 14:20:05 +00:00
|
|
|
unsigned int numToAdd = std::min<unsigned int>(1000U, static_cast<unsigned int>(m_parsedLines.size()) - m_lineInsertCount);
|
2016-08-09 08:38:54 +00:00
|
|
|
flightgear::PolyLineList::const_iterator end = begin + numToAdd;
|
|
|
|
flightgear::PolyLine::bulkAddToSpatialIndex(begin, end);
|
|
|
|
|
|
|
|
if (end == m_parsedLines.end()) {
|
|
|
|
deleteLater(); // commit suicide
|
|
|
|
} else {
|
|
|
|
m_lineInsertCount += 1000;
|
2016-08-11 21:32:29 +00:00
|
|
|
QTimer::singleShot(50, this, SLOT(onFinished()));
|
2016-08-09 08:38:54 +00:00
|
|
|
}
|
2016-08-08 22:13:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void loadNaturalEarthFile(const std::string& aFileName,
|
|
|
|
flightgear::PolyLine::Type aType,
|
|
|
|
bool areClosed)
|
|
|
|
{
|
|
|
|
SGPath path(globals->get_fg_root());
|
|
|
|
path.append( "Geodata" );
|
|
|
|
path.append(aFileName);
|
|
|
|
if (!path.exists())
|
|
|
|
return; // silently fail for now
|
|
|
|
|
|
|
|
flightgear::PolyLineList lines;
|
|
|
|
flightgear::SHPParser::parsePolyLines(path, aType, m_parsedLines, areClosed);
|
|
|
|
}
|
2017-02-19 22:59:39 +00:00
|
|
|
|
2016-08-08 22:13:57 +00:00
|
|
|
flightgear::PolyLineList m_parsedLines;
|
2016-08-09 08:38:54 +00:00
|
|
|
unsigned int m_lineInsertCount;
|
2016-08-08 22:13:57 +00:00
|
|
|
};
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
} // of anonymous namespace
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
static void initQtResources()
|
|
|
|
{
|
|
|
|
Q_INIT_RESOURCE(resources);
|
2018-08-28 16:12:31 +00:00
|
|
|
#if defined(HAVE_QRC_TRANSLATIONS)
|
2018-08-27 12:20:23 +00:00
|
|
|
Q_INIT_RESOURCE(translations);
|
2018-08-28 16:12:31 +00:00
|
|
|
#endif
|
2015-10-16 01:05:17 +00:00
|
|
|
}
|
|
|
|
|
2017-01-29 15:16:28 +00:00
|
|
|
static void simgearMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
|
|
|
|
{
|
|
|
|
sgDebugPriority mappedPriority = SG_WARN;
|
|
|
|
switch (type) {
|
|
|
|
case QtDebugMsg: mappedPriority = SG_DEBUG; break;
|
2017-02-01 16:36:48 +00:00
|
|
|
#if QT_VERSION >= 0x050500
|
2017-01-29 15:16:28 +00:00
|
|
|
case QtInfoMsg: mappedPriority = SG_INFO; break;
|
2017-02-01 16:36:48 +00:00
|
|
|
#endif
|
2017-01-29 15:16:28 +00:00
|
|
|
case QtWarningMsg: mappedPriority = SG_WARN; break;
|
|
|
|
case QtCriticalMsg: mappedPriority = SG_ALERT; break;
|
|
|
|
case QtFatalMsg: mappedPriority = SG_POPUP; break;
|
|
|
|
}
|
|
|
|
|
|
|
|
static const char* nullFile = "";
|
|
|
|
const char* file = context.file ? context.file : nullFile;
|
|
|
|
sglog().log(SG_GUI, mappedPriority, file, context.line, msg.toStdString());
|
|
|
|
if (type == QtFatalMsg) {
|
|
|
|
abort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
namespace flightgear
|
|
|
|
{
|
|
|
|
|
2017-06-07 15:25:19 +00:00
|
|
|
// making this a unique ptr ensures the QApplication will be deleted
|
|
|
|
// event if we forget to call shutdownQtApp. Cleanly destroying this is
|
|
|
|
// important so QPA resources, in particular the XCB thread, are exited
|
|
|
|
// cleanly on quit. However, at present, the official policy is that static
|
|
|
|
// destruction is too late to call this, hence why we have shutdownQtApp()
|
|
|
|
|
2018-08-27 12:20:23 +00:00
|
|
|
static std::unique_ptr<QApplication> static_qApp;
|
2017-06-07 15:25:19 +00:00
|
|
|
|
Split flightgear::initApp() to make it usable even without FGGlobals initialized
- Add an optional argument to flightgear::initApp(): doInitQSettings.
This argument defaults to true, preserving initApp()'s behavior in
this respect. If this argument is set to false, FGGlobals doesn't have
to be initialized.
- New function flightgear::initQSettings(), called by
flightgear::initApp() when its 'doInitQSettings' argument is true.
This allows initializing the QSettings exactly when it is needed.
- New function flightgear::checkKeyboardModifiersForSettingFGRoot().
The code it contains used to be run from initApp(), which is
undesirable because:
1) initApp() is not only called at FG initialization (fgMainInit()),
but also from QtMessageBox(), from QtFileDialog::exec() and twice
from Options::setupRoot(). However, checking the Alt and Shift
modifiers to set 'fg-root' in QSettings to the special value
"!ask" only makes sense in fgMainInit(), not in these other
places.
2) This code relies on the QSettings to be set up, and therefore on
FGGlobals. Thus, freeing initApp() of its dependency on FGGlobals
requires splitting this keyboard modifiers checking code out of
initApp().
2016-12-26 20:45:44 +00:00
|
|
|
// Only requires FGGlobals to be initialized if 'doInitQSettings' is true.
|
|
|
|
// Safe to call several times.
|
|
|
|
void initApp(int& argc, char** argv, bool doInitQSettings)
|
2015-10-16 01:05:17 +00:00
|
|
|
{
|
|
|
|
static bool qtInitDone = false;
|
2015-11-26 16:49:55 +00:00
|
|
|
static int s_argc;
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
if (!qtInitDone) {
|
|
|
|
qtInitDone = true;
|
2016-02-02 20:44:46 +00:00
|
|
|
|
|
|
|
sglog().setLogLevels( SG_ALL, SG_INFO );
|
|
|
|
initQtResources(); // can't be called from a namespace
|
|
|
|
|
2015-11-26 16:49:55 +00:00
|
|
|
s_argc = argc; // QApplication only stores a reference to argc,
|
|
|
|
// and may crash if it is freed
|
|
|
|
// http://doc.qt.io/qt-5/qguiapplication.html#QGuiApplication
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2017-01-29 15:16:28 +00:00
|
|
|
// log to simgear instead of the console from Qt, so we go to
|
|
|
|
// whichever log locations SimGear has configured
|
|
|
|
qInstallMessageHandler(simgearMessageOutput);
|
|
|
|
|
2018-04-27 08:53:49 +00:00
|
|
|
// ensure we use desktop OpenGL, don't even fall back to ANGLE, since
|
|
|
|
// this gets into a knot on Optimus setups (since we export the magic
|
|
|
|
// Optimus / AMD symbols in main.cxx).
|
|
|
|
QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
|
2018-07-03 08:48:49 +00:00
|
|
|
|
|
|
|
// becuase on Windows, Qt only supports integer scaling factors,
|
|
|
|
// forceibly enabling HighDpiScaling is controversial.
|
|
|
|
// leave things unset here, so users can use env var
|
|
|
|
// QT_AUTO_SCREEN_SCALE_FACTOR=1 to enable it at runtime
|
|
|
|
|
|
|
|
//#if QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)
|
|
|
|
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
|
|
|
|
//#endif
|
2017-06-07 15:25:19 +00:00
|
|
|
static_qApp.reset(new QApplication(s_argc, argv));
|
|
|
|
static_qApp->setOrganizationName("FlightGear");
|
|
|
|
static_qApp->setApplicationName("FlightGear");
|
|
|
|
static_qApp->setOrganizationDomain("flightgear.org");
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2017-05-16 11:51:46 +00:00
|
|
|
#if QT_VERSION >= QT_VERSION_CHECK(5, 7, 0)
|
2017-06-07 15:25:19 +00:00
|
|
|
static_qApp->setDesktopFileName(
|
2017-05-16 11:51:46 +00:00
|
|
|
QStringLiteral("org.flightgear.FlightGear.desktop"));
|
|
|
|
#endif
|
2018-08-27 12:20:23 +00:00
|
|
|
QTranslator* fallbackTranslator = new QTranslator(static_qApp.get());
|
2020-05-08 13:54:53 +00:00
|
|
|
if (!fallbackTranslator->load(QLatin1String(":/FlightGear_en_US.qm"))) {
|
2018-08-27 12:20:23 +00:00
|
|
|
qWarning() << "Failed to load default (en) translations";
|
|
|
|
delete fallbackTranslator;
|
|
|
|
} else {
|
|
|
|
static_qApp->installTranslator(fallbackTranslator);
|
|
|
|
}
|
|
|
|
|
2019-04-17 14:11:15 +00:00
|
|
|
qInfo() << "UI languages:" << QLocale().uiLanguages();
|
|
|
|
|
2018-08-27 12:20:23 +00:00
|
|
|
QTranslator* translator = new QTranslator(static_qApp.get());
|
2018-10-12 10:37:39 +00:00
|
|
|
// check for --langauge=xx option and prefer that over QLocale
|
|
|
|
// detection of the locale if it exists
|
|
|
|
auto lang = Options::getArgValue(argc, argv, "--language");
|
|
|
|
if (!lang.empty()) {
|
|
|
|
QString localeFile = "FlightGear_" + QString::fromStdString(lang);
|
|
|
|
if (translator->load(localeFile, QLatin1String(":/"))) {
|
|
|
|
qInfo() << "Loaded translations based on --language from:" << localeFile;
|
|
|
|
static_qApp->installTranslator(translator);
|
|
|
|
} else {
|
|
|
|
qInfo() << "--langauge was set, but no translations found at:" << localeFile;
|
|
|
|
delete translator;
|
|
|
|
}
|
|
|
|
} else if (translator->load(QLocale(), QLatin1String("FlightGear"), QLatin1String("_"), QLatin1String(":/"))) {
|
2018-10-11 20:37:33 +00:00
|
|
|
// QLocale().name() looks like ' "it_IT" ' (without the outer
|
|
|
|
// quotes) when running FG on Linux with LANG=it_IT.UTF-8.
|
|
|
|
qInfo() << "Loaded translations for locale" << QLocale().name();
|
2018-08-27 12:20:23 +00:00
|
|
|
static_qApp->installTranslator(translator);
|
|
|
|
} else {
|
|
|
|
delete translator;
|
|
|
|
}
|
2017-05-16 11:51:46 +00:00
|
|
|
|
2016-02-02 20:44:46 +00:00
|
|
|
// reset numeric / collation locales as described at:
|
|
|
|
// http://doc.qt.io/qt-5/qcoreapplication.html#details
|
|
|
|
::setlocale(LC_NUMERIC, "C");
|
|
|
|
::setlocale(LC_COLLATE, "C");
|
Split flightgear::initApp() to make it usable even without FGGlobals initialized
- Add an optional argument to flightgear::initApp(): doInitQSettings.
This argument defaults to true, preserving initApp()'s behavior in
this respect. If this argument is set to false, FGGlobals doesn't have
to be initialized.
- New function flightgear::initQSettings(), called by
flightgear::initApp() when its 'doInitQSettings' argument is true.
This allows initializing the QSettings exactly when it is needed.
- New function flightgear::checkKeyboardModifiersForSettingFGRoot().
The code it contains used to be run from initApp(), which is
undesirable because:
1) initApp() is not only called at FG initialization (fgMainInit()),
but also from QtMessageBox(), from QtFileDialog::exec() and twice
from Options::setupRoot(). However, checking the Alt and Shift
modifiers to set 'fg-root' in QSettings to the special value
"!ask" only makes sense in fgMainInit(), not in these other
places.
2) This code relies on the QSettings to be set up, and therefore on
FGGlobals. Thus, freeing initApp() of its dependency on FGGlobals
requires splitting this keyboard modifiers checking code out of
initApp().
2016-12-26 20:45:44 +00:00
|
|
|
}
|
2016-02-02 20:44:46 +00:00
|
|
|
|
Split flightgear::initApp() to make it usable even without FGGlobals initialized
- Add an optional argument to flightgear::initApp(): doInitQSettings.
This argument defaults to true, preserving initApp()'s behavior in
this respect. If this argument is set to false, FGGlobals doesn't have
to be initialized.
- New function flightgear::initQSettings(), called by
flightgear::initApp() when its 'doInitQSettings' argument is true.
This allows initializing the QSettings exactly when it is needed.
- New function flightgear::checkKeyboardModifiersForSettingFGRoot().
The code it contains used to be run from initApp(), which is
undesirable because:
1) initApp() is not only called at FG initialization (fgMainInit()),
but also from QtMessageBox(), from QtFileDialog::exec() and twice
from Options::setupRoot(). However, checking the Alt and Shift
modifiers to set 'fg-root' in QSettings to the special value
"!ask" only makes sense in fgMainInit(), not in these other
places.
2) This code relies on the QSettings to be set up, and therefore on
FGGlobals. Thus, freeing initApp() of its dependency on FGGlobals
requires splitting this keyboard modifiers checking code out of
initApp().
2016-12-26 20:45:44 +00:00
|
|
|
if (doInitQSettings) {
|
|
|
|
initQSettings();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-06-07 15:25:19 +00:00
|
|
|
void shutdownQtApp()
|
|
|
|
{
|
2018-07-02 22:13:36 +00:00
|
|
|
// restore default message handler, otherwise Qt logging on
|
|
|
|
// shutdown crashes once sglog is killed
|
|
|
|
qInstallMessageHandler(nullptr);
|
2017-06-07 15:25:19 +00:00
|
|
|
static_qApp.reset();
|
|
|
|
}
|
|
|
|
|
Split flightgear::initApp() to make it usable even without FGGlobals initialized
- Add an optional argument to flightgear::initApp(): doInitQSettings.
This argument defaults to true, preserving initApp()'s behavior in
this respect. If this argument is set to false, FGGlobals doesn't have
to be initialized.
- New function flightgear::initQSettings(), called by
flightgear::initApp() when its 'doInitQSettings' argument is true.
This allows initializing the QSettings exactly when it is needed.
- New function flightgear::checkKeyboardModifiersForSettingFGRoot().
The code it contains used to be run from initApp(), which is
undesirable because:
1) initApp() is not only called at FG initialization (fgMainInit()),
but also from QtMessageBox(), from QtFileDialog::exec() and twice
from Options::setupRoot(). However, checking the Alt and Shift
modifiers to set 'fg-root' in QSettings to the special value
"!ask" only makes sense in fgMainInit(), not in these other
places.
2) This code relies on the QSettings to be set up, and therefore on
FGGlobals. Thus, freeing initApp() of its dependency on FGGlobals
requires splitting this keyboard modifiers checking code out of
initApp().
2016-12-26 20:45:44 +00:00
|
|
|
// Requires FGGlobals to be initialized. Safe to call several times.
|
|
|
|
void initQSettings()
|
|
|
|
{
|
|
|
|
static bool qSettingsInitDone = false;
|
|
|
|
|
|
|
|
if (!qSettingsInitDone) {
|
2018-07-17 10:55:25 +00:00
|
|
|
qRegisterMetaType<QuantityValue>();
|
|
|
|
qRegisterMetaTypeStreamOperators<QuantityValue>("QuantityValue");
|
|
|
|
|
Split flightgear::initApp() to make it usable even without FGGlobals initialized
- Add an optional argument to flightgear::initApp(): doInitQSettings.
This argument defaults to true, preserving initApp()'s behavior in
this respect. If this argument is set to false, FGGlobals doesn't have
to be initialized.
- New function flightgear::initQSettings(), called by
flightgear::initApp() when its 'doInitQSettings' argument is true.
This allows initializing the QSettings exactly when it is needed.
- New function flightgear::checkKeyboardModifiersForSettingFGRoot().
The code it contains used to be run from initApp(), which is
undesirable because:
1) initApp() is not only called at FG initialization (fgMainInit()),
but also from QtMessageBox(), from QtFileDialog::exec() and twice
from Options::setupRoot(). However, checking the Alt and Shift
modifiers to set 'fg-root' in QSettings to the special value
"!ask" only makes sense in fgMainInit(), not in these other
places.
2) This code relies on the QSettings to be set up, and therefore on
FGGlobals. Thus, freeing initApp() of its dependency on FGGlobals
requires splitting this keyboard modifiers checking code out of
initApp().
2016-12-26 20:45:44 +00:00
|
|
|
qSettingsInitDone = true;
|
|
|
|
string fgHome = globals->get_fg_home().utf8Str();
|
|
|
|
|
|
|
|
QSettings::setDefaultFormat(QSettings::IniFormat);
|
|
|
|
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope,
|
|
|
|
QString::fromStdString(fgHome));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
bool checkKeyboardModifiersForSettingFGRoot()
|
|
|
|
{
|
|
|
|
initQSettings();
|
2020-03-26 17:10:14 +00:00
|
|
|
#if defined(Q_OS_WIN)
|
|
|
|
const auto altState = GetAsyncKeyState(VK_MENU);
|
|
|
|
const auto shiftState = GetAsyncKeyState(VK_SHIFT);
|
|
|
|
if ((altState < 0) || (shiftState < 0))
|
|
|
|
#else
|
Split flightgear::initApp() to make it usable even without FGGlobals initialized
- Add an optional argument to flightgear::initApp(): doInitQSettings.
This argument defaults to true, preserving initApp()'s behavior in
this respect. If this argument is set to false, FGGlobals doesn't have
to be initialized.
- New function flightgear::initQSettings(), called by
flightgear::initApp() when its 'doInitQSettings' argument is true.
This allows initializing the QSettings exactly when it is needed.
- New function flightgear::checkKeyboardModifiersForSettingFGRoot().
The code it contains used to be run from initApp(), which is
undesirable because:
1) initApp() is not only called at FG initialization (fgMainInit()),
but also from QtMessageBox(), from QtFileDialog::exec() and twice
from Options::setupRoot(). However, checking the Alt and Shift
modifiers to set 'fg-root' in QSettings to the special value
"!ask" only makes sense in fgMainInit(), not in these other
places.
2) This code relies on the QSettings to be set up, and therefore on
FGGlobals. Thus, freeing initApp() of its dependency on FGGlobals
requires splitting this keyboard modifiers checking code out of
initApp().
2016-12-26 20:45:44 +00:00
|
|
|
Qt::KeyboardModifiers mods = qApp->queryKeyboardModifiers();
|
2020-03-26 17:10:14 +00:00
|
|
|
if (mods & (Qt::AltModifier | Qt::ShiftModifier))
|
|
|
|
#endif
|
|
|
|
{
|
Split flightgear::initApp() to make it usable even without FGGlobals initialized
- Add an optional argument to flightgear::initApp(): doInitQSettings.
This argument defaults to true, preserving initApp()'s behavior in
this respect. If this argument is set to false, FGGlobals doesn't have
to be initialized.
- New function flightgear::initQSettings(), called by
flightgear::initApp() when its 'doInitQSettings' argument is true.
This allows initializing the QSettings exactly when it is needed.
- New function flightgear::checkKeyboardModifiersForSettingFGRoot().
The code it contains used to be run from initApp(), which is
undesirable because:
1) initApp() is not only called at FG initialization (fgMainInit()),
but also from QtMessageBox(), from QtFileDialog::exec() and twice
from Options::setupRoot(). However, checking the Alt and Shift
modifiers to set 'fg-root' in QSettings to the special value
"!ask" only makes sense in fgMainInit(), not in these other
places.
2) This code relies on the QSettings to be set up, and therefore on
FGGlobals. Thus, freeing initApp() of its dependency on FGGlobals
requires splitting this keyboard modifiers checking code out of
initApp().
2016-12-26 20:45:44 +00:00
|
|
|
qWarning() << "Alt/shift pressed during launch";
|
|
|
|
return true;
|
2015-10-16 01:05:17 +00:00
|
|
|
}
|
Split flightgear::initApp() to make it usable even without FGGlobals initialized
- Add an optional argument to flightgear::initApp(): doInitQSettings.
This argument defaults to true, preserving initApp()'s behavior in
this respect. If this argument is set to false, FGGlobals doesn't have
to be initialized.
- New function flightgear::initQSettings(), called by
flightgear::initApp() when its 'doInitQSettings' argument is true.
This allows initializing the QSettings exactly when it is needed.
- New function flightgear::checkKeyboardModifiersForSettingFGRoot().
The code it contains used to be run from initApp(), which is
undesirable because:
1) initApp() is not only called at FG initialization (fgMainInit()),
but also from QtMessageBox(), from QtFileDialog::exec() and twice
from Options::setupRoot(). However, checking the Alt and Shift
modifiers to set 'fg-root' in QSettings to the special value
"!ask" only makes sense in fgMainInit(), not in these other
places.
2) This code relies on the QSettings to be set up, and therefore on
FGGlobals. Thus, freeing initApp() of its dependency on FGGlobals
requires splitting this keyboard modifiers checking code out of
initApp().
2016-12-26 20:45:44 +00:00
|
|
|
|
|
|
|
return false;
|
2015-10-16 01:05:17 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
void restartTheApp()
|
2014-12-26 12:20:51 +00:00
|
|
|
{
|
2017-02-20 00:19:13 +00:00
|
|
|
QStringList fgArgs;
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
// Spawn a new instance of myApplication:
|
|
|
|
QProcess proc;
|
|
|
|
QStringList args;
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2018-07-02 22:13:36 +00:00
|
|
|
// ensure we release whatever mutex/lock file we have in home,
|
|
|
|
// so the new instance runs in writeable mode
|
|
|
|
fgShutdownHome();
|
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
#if defined(Q_OS_MAC)
|
|
|
|
QDir dir(qApp->applicationDirPath()); // returns the 'MacOS' dir
|
|
|
|
dir.cdUp(); // up to 'contents' dir
|
|
|
|
dir.cdUp(); // up to .app dir
|
|
|
|
// see 'man open' for details, but '-n' ensures we launch a new instance,
|
|
|
|
// and we want to pass remaining arguments to us, not open.
|
|
|
|
args << "-n" << dir.absolutePath() << "--args" << "--launcher" << fgArgs;
|
|
|
|
qDebug() << "args" << args;
|
|
|
|
proc.startDetached("open", args);
|
|
|
|
#else
|
|
|
|
args << "--launcher" << fgArgs;
|
|
|
|
proc.startDetached(qApp->applicationFilePath(), args);
|
2017-01-30 07:00:37 +00:00
|
|
|
#endif
|
2017-02-20 00:19:13 +00:00
|
|
|
qApp->exit(-1);
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
void launcherSetSceneryPaths()
|
2016-01-10 22:38:01 +00:00
|
|
|
{
|
|
|
|
globals->clear_fg_scenery();
|
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
// mimic what options.cxx does, so we can find airport data for parking
|
2016-01-10 22:38:01 +00:00
|
|
|
// positions
|
|
|
|
QSettings settings;
|
|
|
|
// append explicit scenery paths
|
2020-03-10 16:10:55 +00:00
|
|
|
Q_FOREACH(QString path, PathListModel::readEnabledPaths("scenery-paths-v2")) {
|
2016-01-10 22:38:01 +00:00
|
|
|
globals->append_fg_scenery(path.toStdString());
|
|
|
|
}
|
|
|
|
|
|
|
|
// append the TerraSync path
|
|
|
|
QString downloadDir = settings.value("download-dir").toString();
|
|
|
|
if (downloadDir.isEmpty()) {
|
2016-06-23 13:26:34 +00:00
|
|
|
downloadDir = QString::fromStdString(flightgear::defaultDownloadDir().utf8Str());
|
2016-01-10 22:38:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SGPath terraSyncDir(downloadDir.toStdString());
|
|
|
|
terraSyncDir.append("TerraSync");
|
|
|
|
if (terraSyncDir.exists()) {
|
2016-06-21 11:29:04 +00:00
|
|
|
globals->append_fg_scenery(terraSyncDir);
|
2016-01-10 22:38:01 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 11:12:02 +00:00
|
|
|
// add the installation path since it contains default airport data,
|
|
|
|
// if terrasync is disabled or on first-launch
|
2018-11-06 17:06:57 +00:00
|
|
|
const SGPath rootScenery = globals->get_fg_root() / "Scenery";
|
|
|
|
if (rootScenery.exists()) {
|
|
|
|
globals->append_fg_scenery(rootScenery);
|
|
|
|
}
|
2016-01-10 22:38:01 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
bool runLauncherDialog()
|
2015-10-16 01:05:17 +00:00
|
|
|
{
|
2017-02-20 00:19:13 +00:00
|
|
|
// Used for NavDataCache initialization: needed to find the apt.dat files
|
|
|
|
launcherSetSceneryPaths();
|
|
|
|
// startup the nav-cache now. This pre-empts normal startup of
|
|
|
|
// the cache, but no harm done. (Providing scenery paths are consistent)
|
2017-01-26 00:31:41 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
initNavCache();
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2017-07-19 18:24:33 +00:00
|
|
|
auto options = flightgear::Options::sharedInstance();
|
|
|
|
if (options->isOptionSet("download-dir")) {
|
|
|
|
// user set download-dir on command line, don't mess with it in the
|
|
|
|
// launcher GUI. We'll disable the UI.
|
|
|
|
LaunchConfig::setEnableDownloadDirUI(false);
|
|
|
|
} else {
|
|
|
|
QSettings settings;
|
2017-12-15 15:42:36 +00:00
|
|
|
QString downloadDir = settings.value("download-dir").toString();
|
2017-07-19 18:24:33 +00:00
|
|
|
if (!downloadDir.isEmpty()) {
|
|
|
|
options->setOption("download-dir", downloadDir.toStdString());
|
|
|
|
}
|
2016-11-07 11:54:38 +00:00
|
|
|
}
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
fgInitPackageRoot();
|
2016-01-10 18:56:46 +00:00
|
|
|
|
2020-03-17 11:46:26 +00:00
|
|
|
// setup package language
|
|
|
|
auto lang = options->valueForOption("language");
|
2020-06-17 15:15:01 +00:00
|
|
|
if (lang.empty()) {
|
2020-03-17 11:46:26 +00:00
|
|
|
const auto langName = QLocale::languageToString(QLocale{}.language());
|
2020-06-17 15:15:01 +00:00
|
|
|
lang = langName.toStdString();
|
2020-03-17 11:46:26 +00:00
|
|
|
}
|
|
|
|
|
2020-06-17 15:15:01 +00:00
|
|
|
// we will re-do this later, but we want to access translated strings
|
|
|
|
// from within the launcher
|
|
|
|
globals->get_locale()->selectLanguage(lang);
|
|
|
|
globals->packageRoot()->setLocale(lang);
|
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
// startup the HTTP system now since packages needs it
|
|
|
|
FGHTTPClient* http = globals->add_new_subsystem<FGHTTPClient>();
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
// we guard against re-init in the global phase; bind and postinit
|
|
|
|
// will happen as normal
|
|
|
|
http->init();
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
NaturalEarthDataLoaderThread* naturalEarthLoader = new NaturalEarthDataLoaderThread;
|
|
|
|
naturalEarthLoader->start();
|
2016-07-08 08:36:42 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
// avoid double Apple menu and other weirdness if both Qt and OSG
|
|
|
|
// try to initialise various Cocoa structures.
|
|
|
|
flightgear::WindowBuilder::setPoseAsStandaloneApp(false);
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
LauncherMainWindow dlg;
|
2019-12-17 22:40:25 +00:00
|
|
|
if (options->isOptionSet("enable-fullscreen")) {
|
|
|
|
dlg.showFullScreen();
|
|
|
|
} else {
|
|
|
|
dlg.show();
|
|
|
|
}
|
2016-01-10 19:47:57 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
int appResult = qApp->exec();
|
|
|
|
if (appResult <= 0) {
|
|
|
|
return false; // quit
|
2016-01-10 19:47:57 +00:00
|
|
|
}
|
2015-10-25 00:40:41 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
// don't set scenery paths twice
|
|
|
|
globals->clear_fg_scenery();
|
2020-06-17 15:15:01 +00:00
|
|
|
globals->get_locale()->clear();
|
2015-11-03 21:28:36 +00:00
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
return true;
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
bool runInAppLauncherDialog()
|
2014-12-26 12:20:51 +00:00
|
|
|
{
|
2017-02-20 00:19:13 +00:00
|
|
|
LauncherMainWindow dlg;
|
|
|
|
bool accepted = dlg.execInApp();
|
|
|
|
if (!accepted) {
|
|
|
|
return false;
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
return true;
|
2016-02-24 19:50:37 +00:00
|
|
|
}
|
|
|
|
|
2020-05-07 15:49:50 +00:00
|
|
|
static const char* static_lockFileDialog_Title =
|
|
|
|
QT_TRANSLATE_NOOP("LockFileDialog", "Multiple copies of FlightGear running");
|
|
|
|
static const char* static_lockFileDialog_Text =
|
|
|
|
QT_TRANSLATE_NOOP("LockFileDialog",
|
|
|
|
"FlightGear has detected another copy is already running. "
|
|
|
|
"This copy will run in read-only mode, so downloads will not be possible, "
|
|
|
|
"and settings will not be saved.");
|
|
|
|
static const char* static_lockFileDialog_Info =
|
|
|
|
QT_TRANSLATE_NOOP("LockFileDialog",
|
|
|
|
"If you are sure another copy is not running on this computer, "
|
|
|
|
"you can choose to reset the lock file, and run this copy as normal. "
|
|
|
|
"Alternatively, you can close this copy of the software.");
|
|
|
|
|
2020-05-03 17:02:20 +00:00
|
|
|
LockFileDialogResult showLockFileDialog()
|
|
|
|
{
|
2020-05-07 15:49:50 +00:00
|
|
|
QString title = qApp->translate("LockFileDialog", static_lockFileDialog_Title);
|
|
|
|
QString text = qApp->translate("LockFileDialog", static_lockFileDialog_Text);
|
|
|
|
QString infoText = qApp->translate("LockFileDialog", static_lockFileDialog_Info);
|
|
|
|
|
2020-05-03 17:02:20 +00:00
|
|
|
QMessageBox mb;
|
|
|
|
mb.setIconPixmap(QPixmap(":/app-icon-large"));
|
2020-05-07 15:49:50 +00:00
|
|
|
mb.setWindowTitle(title);
|
|
|
|
mb.setText(text);
|
|
|
|
mb.setInformativeText(infoText);
|
2020-05-03 17:02:20 +00:00
|
|
|
mb.addButton(QMessageBox::Ok);
|
|
|
|
mb.setDefaultButton(QMessageBox::Ok);
|
|
|
|
mb.addButton(QMessageBox::Reset);
|
2020-05-07 15:49:50 +00:00
|
|
|
mb.addButton(QMessageBox::Close);
|
2020-05-03 17:02:20 +00:00
|
|
|
|
|
|
|
int r = mb.exec();
|
|
|
|
if (r == QMessageBox::Reset)
|
|
|
|
return LockFileReset;
|
2020-05-07 15:49:50 +00:00
|
|
|
if (r == QMessageBox::Close)
|
|
|
|
return LockFileQuit;
|
2020-05-03 17:02:20 +00:00
|
|
|
return LockFileContinue;
|
|
|
|
}
|
|
|
|
|
2017-02-20 00:19:13 +00:00
|
|
|
} // of namespace flightgear
|
2016-01-17 19:10:22 +00:00
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
#include "QtLauncher.moc"
|