FlightGear next
QtLauncher.cxx
Go to the documentation of this file.
1// QtLauncher.cxx - GUI launcher dialog using Qt5
2//
3// Written by James Turner, started December 2014.
4//
5// Copyright (C) 2014 James Turner <zakalawe@mac.com>
6//
7// This program is free software; you can redistribute it and/or
8// modify it under the terms of the GNU General Public License as
9// published by the Free Software Foundation; either version 2 of the
10// License, or (at your option) any later version.
11//
12// This program is distributed in the hope that it will be useful, but
13// WITHOUT ANY WARRANTY; without even the implied warranty of
14// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15// General Public License for more details.
16//
17// You should have received a copy of the GNU General Public License
18// along with this program; if not, write to the Free Software
19// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20
21#include "config.h"
22
23#include "QtLauncher.hxx"
24
25#include <locale.h>
26
27// Qt
28#include <QApplication>
29#include <QDebug>
30#include <QDir>
31#include <QFileInfo>
32#include <QMessageBox>
33#include <QOffscreenSurface>
34#include <QOpenGLContext>
35#include <QPointer>
36#include <QProcess>
37#include <QProgressDialog>
38#include <QSettings>
39#include <QString>
40#include <QThread>
41#include <QTimer>
42#include <QTranslator>
43#include <QUrl>
44#include <QtGlobal>
45
46// Simgear
47#include <simgear/timing/timestamp.hxx>
48#include <simgear/props/props_io.hxx>
49#include <simgear/structure/exception.hxx>
50#include <simgear/structure/subsystem_mgr.hxx>
51#include <simgear/misc/sg_path.hxx>
52#include <simgear/misc/strutils.hxx>
53#include <simgear/package/Root.hxx>
54#include <simgear/package/Catalog.hxx>
55#include <simgear/package/Package.hxx>
56#include <simgear/package/Install.hxx>
57#include <simgear/debug/logstream.hxx>
58
60#include <Airports/airport.hxx>
61#include <Main/fg_props.hxx>
62#include <Main/globals.hxx>
65#include <Navaids/SHPParser.hxx>
66#include <Navaids/navrecord.hxx>
67
68
69#include <Main/fg_init.hxx>
70#include <Main/locale.hxx>
71#include <Main/options.hxx>
73
74#include "LaunchConfig.hxx"
77#include "PathListModel.hxx"
78#include "SetupRootDialog.hxx"
79#include "UnitsModel.hxx"
80
81#if defined(SG_MAC)
82#include <GUI/CocoaHelpers.h>
83#endif
84
85#if defined(Q_OS_WIN)
86#include <windows.h> // for GetAsyncKeyState and VK_ constants
87#endif
88
89using namespace flightgear;
90using namespace simgear::pkg;
91using std::string;
92
93#if defined(OSG_OPENGL)
94 #error "Don't include osg/GL in this file, it will cause problems"
95#endif
96// this function is defined in WindowBuilder.cxx, but we can't include that here, since
97// it will mean we include <osg/GL>, and we *also* include <QOpenGLContext> here, and including
98// both is *bad*. So we just declare this function, implement it in WindowBuilder.cxx, and ensure
99// we don't include any OSG headers here
100extern void fgqt_setPoseAsStandaloneApp(bool b);
101namespace { // anonymous namespace
102
103struct ProgressLabel {
105 const char* label;
106};
107
108static std::initializer_list<ProgressLabel> progressStrings = {
109 {NavDataCache::REBUILD_READING_APT_DAT_FILES, QT_TRANSLATE_NOOP("initNavCache","Reading airport data")},
110 {NavDataCache::REBUILD_LOADING_AIRPORTS, QT_TRANSLATE_NOOP("initNavCache","Loading airports")},
111 {NavDataCache::REBUILD_FIXES, QT_TRANSLATE_NOOP("initNavCache","Loading waypoint data")},
112 {NavDataCache::REBUILD_NAVAIDS, QT_TRANSLATE_NOOP("initNavCache","Loading navigation data")},
113 {NavDataCache::REBUILD_POIS, QT_TRANSLATE_NOOP("initNavCache","Loading point-of-interest data")}
114};
115
116bool initNavCache()
117{
118 const char* baseLabelKey = QT_TRANSLATE_NOOP("initNavCache", "Initialising navigation data, this may take several minutes");
119 QString baseLabel= qApp->translate("initNavCache", baseLabelKey);
120
121 const auto wflags = Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint | Qt::MSWindowsFixedSizeDialogHint;
122
124 const char* waitForOtherMsg = QT_TRANSLATE_NOOP("initNavCache", "Another copy of FlightGear is creating the navigation database. Waiting for it to finish.");
125 QString m = qApp->translate("initNavCache", waitForOtherMsg);
126
127 addSentryBreadcrumb("Launcher: showing wait for other process NavCache rebuild dialog", "info");
128 QProgressDialog waitForRebuild(m,
129 QString() /* cancel text */,
130 0, 0, Q_NULLPTR,
131 wflags);
132 waitForRebuild.setWindowModality(Qt::WindowModal);
133 waitForRebuild.setMinimumWidth(600);
134 waitForRebuild.setAutoReset(false);
135 waitForRebuild.setAutoClose(false);
136 waitForRebuild.show();
137
138 QTimer updateTimer;
139 updateTimer.setInterval(500);
140 bool rebuildIsDone = false;
141
142 QObject::connect(&updateTimer, &QTimer::timeout, [&waitForRebuild, &rebuildIsDone]() {
144 waitForRebuild.done(0);
145 rebuildIsDone = true;
146 return;
147 }
148 });
149
150 updateTimer.start(); // timer won't actually run until we process events
151 waitForRebuild.exec();
152 updateTimer.stop();
153
154 if (!rebuildIsDone) {
155 flightgear::addSentryBreadcrumb("Launcher wait on other process nav-cache rebuild abandoned by user", "info");
156 return false;
157 }
158
159 addSentryBreadcrumb("Launcher: done waiting for other process NavCache rebuild dialog", "info");
160 }
161
163 if (cache->isRebuildRequired()) {
164 QProgressDialog rebuildProgress(baseLabel,
165 QString() /* cancel text */,
166 0, 100, Q_NULLPTR,
167 wflags);
168 rebuildProgress.setWindowModality(Qt::WindowModal);
169 rebuildProgress.setMinimumWidth(600);
170 rebuildProgress.setAutoReset(false);
171 rebuildProgress.setAutoClose(false);
172 rebuildProgress.show();
173
174 QTimer updateTimer;
175 updateTimer.setInterval(100);
176
177 bool didComplete = false;
178
179 QObject::connect(&updateTimer, &QTimer::timeout, [&cache, &rebuildProgress, &baseLabel, &didComplete]() {
180 auto phase = cache->rebuild();
181 if (phase == NavDataCache::REBUILD_DONE) {
182 rebuildProgress.done(0);
183 didComplete = true;
184 return;
185 }
186
187 auto it = std::find_if(progressStrings.begin(), progressStrings.end(), [phase]
188 (const ProgressLabel& l) { return l.phase == phase; });
189 if (it == progressStrings.end()) {
190 rebuildProgress.setLabelText(baseLabel);
191 } else {
192 QString trans = qApp->translate("initNavCache", it->label);
193 rebuildProgress.setLabelText(trans);
194 }
195
196 if (phase == NavDataCache::REBUILD_UNKNOWN) {
197 rebuildProgress.setValue(0);
198 rebuildProgress.setMaximum(0);
199 } else {
200 rebuildProgress.setValue(static_cast<int>(cache->rebuildPhaseCompletionPercentage()));
201 rebuildProgress.setMaximum(100);
202 }
203 });
204
205 updateTimer.start(); // timer won't actually run until we process events
206 rebuildProgress.exec();
207 updateTimer.stop();
208
209 if (!didComplete) {
210 flightgear::addSentryBreadcrumb("Launcher nav-cache rebuild abandoned by user", "info");
211 return false;
212 }
213
214 flightgear::addSentryBreadcrumb("Launcher nav-cache rebuild complete", "info");
215 }
216
217 return true;
218}
219
220class NaturalEarthDataLoaderThread : public QThread
221{
222 Q_OBJECT
223public:
224
225 NaturalEarthDataLoaderThread()
226 {
227 connect(this, &QThread::finished, this, &NaturalEarthDataLoaderThread::onFinished);
228 }
229
230 void abandon()
231 {
232 m_abandoned = true;
233 }
234protected:
235 void run() override
236 {
237 struct FileAndType {
238 std::string file;
240 bool closed = false;
241 };
242
243 const std::initializer_list<FileAndType> files = {
244 {"ne_10m_coastline.shp", flightgear::PolyLine::COASTLINE, false},
245 {"ne_10m_rivers_lake_centerlines.shp", flightgear::PolyLine::RIVER, false},
246 {"ne_10m_lakes.shp", flightgear::PolyLine::LAKE, true},
247 {"ne_10m_urban_areas.shp", flightgear::PolyLine::URBAN, true}
248 };
249
250 for (const auto& d : files) {
251 if (m_abandoned) {
252 break;
253 }
254
255 loadNaturalEarthFile(d.file, d.type, d.closed);
256 }
257 }
258
259private:
260 Q_SLOT void onFinished()
261 {
262 if (m_abandoned)
263 return;
264
265
266 flightgear::PolyLine::bulkAddToSpatialIndex(m_parsedLines.begin(), m_parsedLines.end());
267 deleteLater(); // commit suicide
268 }
269
270 void loadNaturalEarthFile(const std::string& aFileName,
272 bool areClosed)
273 {
274 SGPath path(globals->get_fg_root());
275 path.append( "Geodata" );
276 path.append(aFileName);
277 if (!path.exists())
278 return; // silently fail for now
279
280 flightgear::SHPParser::parsePolyLines(path, aType, m_parsedLines, areClosed);
281 }
282
283 flightgear::PolyLineList m_parsedLines;
284 bool m_abandoned = false;
285};
286
287} // of anonymous namespace
288
289static void initQtResources()
290{
291 Q_INIT_RESOURCE(resources);
292#if defined(HAVE_QRC_TRANSLATIONS)
293 Q_INIT_RESOURCE(translations);
294#endif
295}
296
297static void simgearMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
298{
299 sgDebugPriority mappedPriority = SG_WARN;
300 switch (type) {
301 case QtDebugMsg: mappedPriority = SG_DEBUG; break;
302 case QtInfoMsg: mappedPriority = SG_INFO; break;
303 case QtWarningMsg: mappedPriority = SG_WARN; break;
304 case QtCriticalMsg: mappedPriority = SG_ALERT; break;
305 case QtFatalMsg: mappedPriority = SG_POPUP; break;
306 }
307
308 static const char* nullFile = "";
309 const char* file = context.file ? context.file : nullFile;
310 const auto s = msg.toStdString();
311 // important we copy the file name here, since QMessageLogContext doesn't
312 sglog().logCopyingFilename(SG_GUI, mappedPriority, file, context.line, "" /*function*/, s);
313 if (type == QtFatalMsg) {
314 // if we abort() here, we get a thread crash which prevents
315 // us actually seeing the error
316 // we'll attempt to continue so we do the SG_POPUP dialog, maybe it works
317 // if it crashes we're no worse off than calling abort() here
318 }
319}
320
321namespace flightgear
322{
323
324// making this a unique ptr ensures the QApplication will be deleted
325// event if we forget to call shutdownQtApp. Cleanly destroying this is
326// important so QPA resources, in particular the XCB thread, are exited
327// cleanly on quit. However, at present, the official policy is that static
328// destruction is too late to call this, hence why we have shutdownQtApp()
329
330static std::unique_ptr<QApplication> static_qApp;
331
332
333// becuase QTranslator::load (find_translation, internally) doesn't handle the
334// sciprt part of a language code like: zh-Hans-CN, use this code borrowed from
335// Qt Creator to do the search manually.
337{
338 QStringList uiLanguages = QLocale::system().uiLanguages();
339 //qWarning() << "UI languages:" << uiLanguages;
340
341 for (auto locale : uiLanguages) {
342 // remove script if it exists, eg zh-Hans-CN -> zh-CN
343 locale = QLocale(locale).name();
344 locale.replace('-', '_');
345
346 QTranslator* translator = new QTranslator;
347 ;
348
349 if (translator->load("FlightGear_" + locale, QLatin1String(":/"))) {
350 static_qApp->installTranslator(translator);
351 return;
352 }
353
354 delete translator;
355 }
356}
357
358// Only requires FGGlobals to be initialized if 'doInitQSettings' is true.
359// Safe to call several times.
360void initApp(int& argc, char** argv, bool doInitQSettings)
361{
362 static bool qtInitDone = false;
363 static int s_argc;
364
365 if (!qtInitDone) {
366 qtInitDone = true;
367
368 // Disable Qt 5.15 warnings about obsolete Connections/onFoo: syntax
369 // we cannot use the new syntax
370 // as long as we have to support Qt 5.9
371 qputenv("QT_LOGGING_RULES", "qt.qml.connections.warning=false");
372
373 initQtResources(); // can't be called from a namespace
374
375 s_argc = argc; // QApplication only stores a reference to argc,
376 // and may crash if it is freed
377 // http://doc.qt.io/qt-5/qguiapplication.html#QGuiApplication
378
379 // log to simgear instead of the console from Qt, so we go to
380 // whichever log locations SimGear has configured
381 qInstallMessageHandler(simgearMessageOutput);
382
383 // ensure we use desktop OpenGL, don't even fall back to ANGLE, since
384 // this gets into a knot on Optimus setups (since we export the magic
385 // Optimus / AMD symbols in main.cxx).
386 QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
387
388 // becuase on Windows, Qt only supports integer scaling factors,
389 // forceibly enabling HighDpiScaling is controversial.
390 // leave things unset here, so users can use env var
391 // QT_AUTO_SCREEN_SCALE_FACTOR=1 to enable it at runtime
392
393#if !defined (SG_WINDOWS) && (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
394 QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
395#endif
396 static_qApp.reset(new QApplication(s_argc, argv));
397 static_qApp->setOrganizationName("FlightGear");
398 static_qApp->setApplicationName("FlightGear");
399 static_qApp->setOrganizationDomain("flightgear.org");
400
401 static_qApp->setDesktopFileName(
402 QStringLiteral("org.flightgear.FlightGear.desktop"));
403
404 QTranslator* fallbackTranslator = new QTranslator(static_qApp.get());
405 if (!fallbackTranslator->load(QLatin1String(":/FlightGear_en_US.qm"))) {
406 qWarning() << "Failed to load the built-in launcher fallback"
407 << "translation (English). If you compiled FlightGear"
408 << "yourself, you may want to pass -DFG_DATA_DIR to"
409 << "CMake so as to allow the FlightGear build system"
410 << "to find FGData.";
411 delete fallbackTranslator;
412 } else {
413 static_qApp->installTranslator(fallbackTranslator);
414 }
415
416 // check for --language=xx option and prefer that over QLocale
417 // detection of the locale if it exists
418 auto lang = simgear::strutils::replace(
419 Options::getArgValue(argc, argv, "--language"),
420 "-",
421 "_");
422 if (!lang.empty()) {
423 QTranslator* translator = new QTranslator(static_qApp.get());
424 QString localeFile = "FlightGear_" + QString::fromStdString(lang);
425 if (translator->load(localeFile, QLatin1String(":/"))) {
426 qInfo() << "Loaded translations based on --language from:" << localeFile;
427 static_qApp->installTranslator(translator);
428 } else {
429 qInfo() << "--language was set, but no translations found at:" << localeFile;
430 delete translator;
431 }
432 } else {
434 }
435
436 // reset numeric / collation locales as described at:
437 // http://doc.qt.io/qt-5/qcoreapplication.html#details
438 ::setlocale(LC_NUMERIC, "C");
439 ::setlocale(LC_COLLATE, "C");
440
441
442#if defined(SG_MAC)
444 addSentryBreadcrumb("did show translocation warning", "info");
445 const char* titleKey = QT_TRANSLATE_NOOP("macTranslationWarning", "Application running from download location");
446 const char* key = QT_TRANSLATE_NOOP("macTranslationWarning", "FlightGear is running from the download image. For better performance and to avoid potential problems, "
447 "please copy FlightGear to some other location, such as your desktop or Applications folder.");
448 QString msg = qApp->translate("macTranslationWarning", key);
449 QString title = qApp->translate("macTranslationWarning", titleKey);
450
451 QMessageBox::warning(nullptr, title, msg);
452 }
453#endif
454 }
455
456 if (doInitQSettings) {
458 }
459}
460
462{
463 // restore default message handler, otherwise Qt logging on
464 // shutdown crashes once sglog is killed
465 qInstallMessageHandler(nullptr);
466 static_qApp.reset();
467}
468
469// Requires FGGlobals to be initialized. Safe to call several times.
471{
472 static bool qSettingsInitDone = false;
473
474 if (!qSettingsInitDone) {
475 qRegisterMetaType<QuantityValue>();
476#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
477 qRegisterMetaTypeStreamOperators<QuantityValue>("QuantityValue");
478#endif
479
480 qSettingsInitDone = true;
481 string fgHome = globals->get_fg_home().utf8Str();
482
483 QSettings::setDefaultFormat(QSettings::IniFormat);
484 QSettings::setPath(QSettings::IniFormat, QSettings::UserScope,
485 QString::fromStdString(fgHome));
486 }
487}
488
490{
492#if defined(Q_OS_WIN)
493 const auto altState = GetAsyncKeyState(VK_MENU);
494 const auto shiftState = GetAsyncKeyState(VK_SHIFT);
495 if ((altState < 0) || (shiftState < 0))
496#else
497 Qt::KeyboardModifiers mods = qApp->queryKeyboardModifiers();
498 if (mods & (Qt::AltModifier | Qt::ShiftModifier))
499#endif
500 {
501 qWarning() << "Alt/shift pressed during launch";
502 return true;
503 }
504
505 return false;
506}
507
509{
510 QStringList fgArgs;
511
512 // Spawn a new instance of myApplication:
513 QProcess proc;
514 QStringList args;
515
516 // ensure we release whatever mutex/lock file we have in home,
517 // so the new instance runs in writeable mode
519
520#if defined(Q_OS_MAC)
521 QDir dir(qApp->applicationDirPath()); // returns the 'MacOS' dir
522 dir.cdUp(); // up to 'contents' dir
523 dir.cdUp(); // up to .app dir
524 // see 'man open' for details, but '-n' ensures we launch a new instance,
525 // and we want to pass remaining arguments to us, not open.
526 args << "-n" << dir.absolutePath() << "--args" << "--launcher" << fgArgs;
527 qDebug() << "args" << args;
528 proc.startDetached("open", args);
529#else
530 args << "--launcher" << fgArgs;
531 proc.startDetached(qApp->applicationFilePath(), args);
532#endif
533 qApp->exit(-1);
534}
535
536void startLaunchOnExit(const std::vector<std::string>& originalCommandLine)
537{
538 QStringList fgArgs;
539 for (const auto& arg : originalCommandLine) {
540 fgArgs.append(QString::fromStdString(arg));
541 }
542
543 QProcess proc;
544#if defined(Q_OS_MAC)
545 QDir dir(qApp->applicationDirPath()); // returns the 'MacOS' dir
546 dir.cdUp(); // up to 'contents' dir
547 dir.cdUp(); // up to .app dir
548
549 QStringList args;
550 // see 'man open' for details, but '-n' ensures we launch a new instance,
551 // and we want to pass remaining arguments to us, not open.
552 args << "-n" << dir.absolutePath() << "--args" << fgArgs;
553 qDebug() << "args" << args;
554 proc.startDetached("open", args);
555#else
556 proc.startDetached(qApp->applicationFilePath(), fgArgs);
557#endif
558}
559
561{
562 globals->clear_fg_scenery();
563
564 // process path sthe user supplied on the existing command line
565 const auto commandLineSceneryPaths = flightgear::Options::sharedInstance()->valuesForOption("fg-scenery");
566 for (const auto& arg : commandLineSceneryPaths) {
567 // each arg can be multiple paths
568 globals->append_fg_scenery(SGPath::pathsFromUtf8(arg));
569 }
570
571// mimic what options.cxx does, so we can find airport data for parking
572// positions
573 QSettings settings;
574 // append explicit scenery paths
575 Q_FOREACH(QString path, PathListModel::readEnabledPaths("scenery-paths-v2")) {
576 globals->append_fg_scenery(path.toStdString());
577 }
578
579 // append the TerraSync path
580 QString downloadDir = settings.value("download-dir").toString();
581 if (downloadDir.isEmpty()) {
582 downloadDir = QString::fromStdString(flightgear::defaultDownloadDir().utf8Str());
583 }
584
585 SGPath terraSyncDir(downloadDir.toStdString());
586 terraSyncDir.append("TerraSync");
587 if (terraSyncDir.exists()) {
588 globals->append_fg_scenery(terraSyncDir);
589 }
590
591 // add the installation path since it contains default airport data,
592 // if terrasync is disabled or on first-launch
593 const SGPath rootScenery = globals->get_fg_root() / "Scenery";
594 if (rootScenery.exists()) {
595 globals->append_fg_scenery(rootScenery);
596 }
597}
598
600{
601 // Used for NavDataCache initialization: needed to find the apt.dat files
603 // startup the nav-cache now. This pre-empts normal startup of
604 // the cache, but no harm done. (Providing scenery paths are consistent)
605
606 bool ok = initNavCache();
607 if (!ok) {
608 return false;
609 }
610
612 if (options->isOptionSet("download-dir")) {
613 // user set download-dir on command line, don't mess with it in the
614 // launcher GUI. We'll disable the UI.
616 } else {
617 QSettings settings;
618 QString downloadDir = settings.value("download-dir").toString();
619 if (!downloadDir.isEmpty()) {
620 options->setOption("download-dir", downloadDir.toStdString());
621 }
622 }
623
625
626 // setup package language
627 auto lang = options->valueForOption("language");
628 if (lang.empty()) {
629 const auto langName = QLocale::languageToString(QLocale{}.language());
630 lang = langName.toStdString();
631 }
632
633 // we will re-do this later, but we want to access translated strings
634 // from within the launcher
635 globals->get_locale()->selectLanguage(lang);
636 globals->packageRoot()->setLocale(globals->get_locale()->getPreferredLanguage());
637
638 // startup the HTTP system now since packages needs it
640
641 QPointer<NaturalEarthDataLoaderThread> naturalEarthLoader = new NaturalEarthDataLoaderThread;
642 naturalEarthLoader->start();
643
644 // avoid double Apple menu and other weirdness if both Qt and OSG
645 // try to initialise various Cocoa structures.
647
648 LauncherMainWindow dlg(false);
649
650 if (options->isOptionSet("fullscreen")) {
651 dlg.showFullScreen();
652 } else {
653 dlg.setVisible(true);
654 }
655
656 int appResult = qApp->exec();
657 // avoid crashes / NavCache races if the loader is still running after
658 // the launcher exits
659 if (naturalEarthLoader) {
660 naturalEarthLoader->abandon();
661 }
662
663 // avoid a race-y crash on the locale, if a scan thread is
664 // still running: this reset will cancel any running scan
666
667 // don't set scenery paths twice
668 globals->clear_fg_scenery();
669 globals->get_locale()->clear();
670
671 if (appResult <= 0) {
672 return false; // quit
673 }
674
675 return true;
676}
677
679{
680 LauncherMainWindow dlg(true);
681 bool accepted = dlg.execInApp();
682 if (!accepted) {
683 return false;
684 }
685
686 return true;
687}
688
689static const char* static_lockFileDialog_Title =
690 QT_TRANSLATE_NOOP("LockFileDialog", "Multiple copies of FlightGear running");
691static const char* static_lockFileDialog_Text =
692 QT_TRANSLATE_NOOP("LockFileDialog",
693 "FlightGear has detected another copy is already running. "
694 "This copy will run in read-only mode, so downloads will not be possible, "
695 "and settings will not be saved.");
696static const char* static_lockFileDialog_Info =
697 QT_TRANSLATE_NOOP("LockFileDialog",
698 "If you are sure another copy is not running on this computer, "
699 "you can choose to reset the lock file, and run this copy as normal. "
700 "Alternatively, you can close this copy of the software.");
701
703{
704 flightgear::addSentryBreadcrumb("showing lock-file dialog", "info");
705
706 QString title = qApp->translate("LockFileDialog", static_lockFileDialog_Title);
707 QString text = qApp->translate("LockFileDialog", static_lockFileDialog_Text);
708 QString infoText = qApp->translate("LockFileDialog", static_lockFileDialog_Info);
709
710 QMessageBox mb;
711 mb.setIconPixmap(QPixmap(":/app-icon-large"));
712 mb.setWindowTitle(title);
713 mb.setText(text);
714 mb.setInformativeText(infoText);
715 mb.addButton(QMessageBox::Ok);
716 mb.setDefaultButton(QMessageBox::Ok);
717 mb.addButton(QMessageBox::Reset);
718 mb.addButton(QMessageBox::Close);
719
720 int r = mb.exec();
721 if (r == QMessageBox::Reset)
722 return LockFileReset;
723 if (r == QMessageBox::Close)
724 return LockFileQuit;
725 return LockFileContinue;
726}
727
728bool showSetupRootDialog(bool usingDefaultRoot)
729{
730 return SetupRootDialog::runDialog(usingDefaultRoot);
731}
732
737
739{
740 QMessageBox::critical(
741 nullptr,
742 "Unable to create OpenGL 4.1 core profile context",
743 "FlightGear detected that your system does not support the required OpenGL version. "
744 "This is normally due to outdated graphics drivers, please check if updates are available.");
745}
746
747} // of namespace flightgear
748
749#include "QtLauncher.moc"
bool cocoaIsRunningTranslocated()
helper to detect if we're running translocated or not.
bool options(int, char **)
Definition JSBSim.cpp:568
void fgqt_setPoseAsStandaloneApp(bool b)
static void simgearMessageOutput(QtMsgType type, const QMessageLogContext &context, const QString &msg)
static void initQtResources()
const SGPath & get_fg_root() const
Definition globals.hxx:189
static FGHTTPClient * getOrCreate()
static void setEnableDownloadDirUI(bool enableDownloadDirUI)
static QStringList readEnabledPaths(QString settingsKey)
static flightgear::SetupRootResult restoreUserSelectedRoot(SGPath &path)
static bool runDialog(bool usingDefaultRoot)
static bool isAnotherProcessRebuilding()
static NavDataCache * createInstance()
unsigned int rebuildPhaseCompletionPercentage() const
bool isRebuildRequired()
predicate - check if the cache needs to be rebuilt.
RebuildPhase rebuild()
run the cache rebuild - returns the current phase or 'done'
string_list valuesForOption(const std::string &key) const
return all values for a multi-valued option
Definition options.cxx:2944
static std::string getArgValue(int argc, char *argv[], const char *checkArg)
getArgValue - get the value of an argument if it exists, or an empty string otherwise
Definition options.cxx:3693
static Options * sharedInstance()
Definition options.cxx:2345
@ RIVER
state / province / country / department
Definition PolyLine.hxx:63
static void bulkAddToSpatialIndex(PolyLineList::const_iterator begin, PolyLineList::const_iterator end)
Definition PolyLine.cxx:103
static void parsePolyLines(const SGPath &, PolyLine::Type aTy, PolyLineList &aResult, bool aClosed)
Parse a shape file containing PolyLine data.
Definition SHPParser.cxx:91
void fgInitPackageRoot()
Definition fg_init.cxx:1496
void fgShutdownHome()
Definition fg_init.cxx:637
FGGlobals * globals
Definition globals.cxx:142
FlightGear Localization Support.
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
Definition Addon.cxx:53
bool runLauncherDialog()
std::vector< PolyLineRef > PolyLineList
Definition PolyLine.hxx:41
SGPath defaultDownloadDir()
return the default platform dependant download directory.
Definition options.cxx:3001
void restartTheApp()
restartTheApp quit the application and relaunch it, passing the –launcher flag explicitly.
bool runInAppLauncherDialog()
void startLaunchOnExit(const std::vector< std::string > &originalCommandLine)
@ brief helper to re-open the launcher once FLightGear exits cleanly
void initApp(int &argc, char **argv, bool doInitQSettings)
void selectUITranslation()
void initQSettings()
static const char * static_lockFileDialog_Title
static const char * static_lockFileDialog_Text
void addSentryBreadcrumb(const std::string &, const std::string &)
void warnAboutGLVersion()
bool showSetupRootDialog(bool usingDefaultRoot)
static std::unique_ptr< QApplication > static_qApp
bool checkKeyboardModifiersForSettingFGRoot()
SetupRootResult restoreUserSelectedRoot(SGPath &path)
static const char * static_lockFileDialog_Info
LockFileDialogResult showLockFileDialog()
void shutdownQtApp()
void launcherSetSceneryPaths()