diff --git a/CMakeLists.txt b/CMakeLists.txt index 3ee613db6..ccd58ea55 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -168,6 +168,7 @@ option(SYSTEM_SPEEX "Set to ON to build IAXClient with the system's speex a option(SYSTEM_GSM "Set to ON to build IAXClient with the system's GSM library" ${SYSTEM_GSM_DEFAULT}) option(SYSTEM_FLITE "Set to ON to build Flightgear with the system's Flite library" ${SYSTEM_FLITE_DEFAULT}) option(SYSTEM_HTS_ENGINE "Set to ON to build Flightgear with the system's HTS Engine library" ${SYSTEM_HTS_ENGINE_DEFAULT}) +option(FG_NIGHTLY "Set to ON to mark this as a nightly build" OFF) # additional utilities option(ENABLE_FGADMIN "Set to ON to build the FGADMIN application (default)" ON) @@ -287,9 +288,6 @@ if (USE_DBUS) else() endif (USE_DBUS) -# Sqlite always depends on the threading lib -list(APPEND SQLITE3_LIBRARY ${CMAKE_THREAD_LIBS_INIT}) - ############################################################################## ## Qt5 setup setup if (ENABLE_QT) diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 37c2af623..83da4b767 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -70,7 +70,7 @@ endif() if (HAVE_QT) - qt5_wrap_ui(uic_sources Launcher.ui EditRatingsFilterDialog.ui) + qt5_wrap_ui(uic_sources Launcher.ui EditRatingsFilterDialog.ui SetupRootDialog.ui) qt5_add_resources(qrc_sources resources.qrc) include_directories(${PROJECT_BINARY_DIR}/src/GUI) @@ -81,6 +81,8 @@ if (HAVE_QT) AirportDiagram.hxx EditRatingsFilterDialog.cxx EditRatingsFilterDialog.hxx + SetupRootDialog.cxx + SetupRootDialog.hxx ${uic_sources} ${qrc_sources}) diff --git a/src/GUI/QtLauncher.cxx b/src/GUI/QtLauncher.cxx index 7386f91b6..88d1727c3 100644 --- a/src/GUI/QtLauncher.cxx +++ b/src/GUI/QtLauncher.cxx @@ -27,6 +27,7 @@ #include #include #include +#include // Simgear #include @@ -42,6 +43,7 @@ #include #include // for parking #include
+#include using namespace flightgear; @@ -1066,9 +1068,26 @@ QtLauncher::~QtLauncher() } +void QtLauncher::initApp(int argc, char** argv) +{ + static bool qtInitDone = false; + if (!qtInitDone) { + qtInitDone = true; + + QApplication* app = new QApplication(argc, argv); + app->setOrganizationName("FlightGear"); + app->setApplicationName("FlightGear"); + app->setOrganizationDomain("flightgear.org"); + + // avoid double Apple menu and other weirdness if both Qt and OSG + // try to initialise various Cocoa structures. + flightgear::WindowBuilder::setPoseAsStandaloneApp(false); + } +} + bool QtLauncher::runLauncherDialog() { - Q_INIT_RESOURCE(resources); + Q_INIT_RESOURCE(resources); // startup the nav-cache now. This pre-empts normal startup of // the cache, but no harm done. (Providing scenery paths are consistent) diff --git a/src/GUI/QtLauncher.hxx b/src/GUI/QtLauncher.hxx index 81dab0ee3..8ee462035 100644 --- a/src/GUI/QtLauncher.hxx +++ b/src/GUI/QtLauncher.hxx @@ -22,7 +22,9 @@ class QtLauncher : public QDialog public: QtLauncher(); virtual ~QtLauncher(); - + + static void initApp(int argc, char** argv); + static bool runLauncherDialog(); private slots: diff --git a/src/GUI/SetupRootDialog.cxx b/src/GUI/SetupRootDialog.cxx new file mode 100644 index 000000000..fd3ee5b58 --- /dev/null +++ b/src/GUI/SetupRootDialog.cxx @@ -0,0 +1,169 @@ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include "SetupRootDialog.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ui_SetupRootDialog.h" + +#include
+#include
+#include + +SetupRootDialog::SetupRootDialog(bool usedDefaultPath) : + QDialog() +{ + m_ui.reset(new Ui::SetupRootDialog); + m_ui->setupUi(this); + + connect(m_ui->browseButton, &QPushButton::clicked, + this, &SetupRootDialog::onBrowse); + connect(m_ui->downloadButton, &QPushButton::clicked, + this, &SetupRootDialog::onDownload); + connect(m_ui->buttonBox, &QDialogButtonBox::rejected, + this, &QDialog::reject); + + m_promptState = usedDefaultPath ? DefaultPathCheckFailed : ExplicitPathCheckFailed; + std::string ver = fgBasePackageVersion(globals->get_fg_root()); + if (!ver.empty()) { + Q_ASSERT(ver != FLIGHTGEAR_VERSION); // otherwise what are we doing in here?! + m_promptState = VersionCheckFailed; + } + + m_ui->versionLabel->setText(tr("FlightGear version %1").arg(FLIGHTGEAR_VERSION)); + m_ui->bigIcon->setPixmap(QPixmap(":/app-icon-large")); + updatePromptText(); +} + +bool SetupRootDialog::restoreUserSelectedRoot() +{ + QSettings settings; + QString path = settings.value("fg-root").toString(); + if (validatePath(path) && validateVersion(path)) { + qDebug() << "Restoring FG-root:" << path; + globals->set_fg_root(path.toStdString()); + return true; + } else { + return false; + } +} + +bool SetupRootDialog::validatePath(QString path) +{ + // check assorted files exist in the root location, to avoid any chance of + // selecting an incomplete base package. This is probably overkill but does + // no harm + QStringList files = QStringList() + << "version" + << "preferences.xml" + << "Materials/base/materials-base.xml" + << "gui/menubar.xml" + << "Timezone/zone.tab"; + + QDir d(path); + if (!d.exists()) { + return false; + } + + Q_FOREACH(QString s, files) { + if (!d.exists(s)) { + return false; + } + } + + return true; +} + +bool SetupRootDialog::validateVersion(QString path) +{ + std::string ver = fgBasePackageVersion(SGPath(path.toStdString())); + return (ver == FLIGHTGEAR_VERSION); +} + +SetupRootDialog::~SetupRootDialog() +{ + +} + +void SetupRootDialog::onBrowse() +{ + m_browsedPath = QFileDialog::getExistingDirectory(this, + tr("Choose FlightGear data folder")); + if (m_browsedPath.isEmpty()) { + return; + } + + if (!validatePath(m_browsedPath)) { + m_promptState = ChoseInvalidLocation; + updatePromptText(); + return; + } + + if (!validateVersion(m_browsedPath)) { + m_promptState = ChoseInvalidVersion; + updatePromptText(); + return; + } + + globals->set_fg_root(m_browsedPath.toStdString()); + + QSettings settings; + settings.setValue("fg-root", m_browsedPath); + + accept(); // we're done +} + +void SetupRootDialog::onDownload() +{ + QUrl downloadUrl("http://download.flightgear.org/flightgear/Shared/"); + QDesktopServices::openUrl(downloadUrl); +} + +void SetupRootDialog::updatePromptText() +{ + QString t; + QString curRoot = QString::fromStdString(globals->get_fg_root()); + switch (m_promptState) { + case DefaultPathCheckFailed: + t = tr("This copy of FlightGear does not include the base data files. " \ + "Please select a suitable folder containing a previously download set of files."); + break; + + case ExplicitPathCheckFailed: + t = tr("The requested location '%1' does not appear to be a valid set of data files for FlightGear").arg(curRoot); + break; + + case VersionCheckFailed: + { + QString curVer = QString::fromStdString(fgBasePackageVersion(globals->get_fg_root())); + t = tr("Detected incompatible version of the data files: version %1 found, but this is FlightGear %2. " \ + "(At location: '%3') " \ + "Please install or select a matching set of data files.").arg(curVer).arg(QString::fromLatin1(FLIGHTGEAR_VERSION)).arg(curRoot); + break; + } + + case ChoseInvalidLocation: + t = tr("The choosen location (%1) does not appear to contain FlightGear data files. Please try another location.").arg(m_browsedPath); + break; + + case ChoseInvalidVersion: + { + QString curVer = QString::fromStdString(fgBasePackageVersion(m_browsedPath.toStdString())); + t = tr("The choosen location (%1) contains files for version %2, but this is FlightGear %3. " \ + "Please update or try another location").arg(m_browsedPath).arg(curVer).arg(QString::fromLatin1(FLIGHTGEAR_VERSION)); + break; + } + } + + m_ui->promptText->setText(t); +} + diff --git a/src/GUI/SetupRootDialog.hxx b/src/GUI/SetupRootDialog.hxx new file mode 100644 index 000000000..ba677dc26 --- /dev/null +++ b/src/GUI/SetupRootDialog.hxx @@ -0,0 +1,44 @@ + +#include +#include +#include + +namespace Ui +{ + class SetupRootDialog; +} + +class SetupRootDialog : public QDialog +{ + Q_OBJECT +public: + SetupRootDialog(bool usedDefaultPath); + + ~SetupRootDialog(); + + static bool restoreUserSelectedRoot(); +private slots: + + void onBrowse(); + + void onDownload(); + + void updatePromptText(); +private: + + static bool validatePath(QString path); + static bool validateVersion(QString path); + + enum PromptState + { + DefaultPathCheckFailed, + ExplicitPathCheckFailed, + VersionCheckFailed, + ChoseInvalidLocation, + ChoseInvalidVersion + }; + + PromptState m_promptState; + QScopedPointer m_ui; + QString m_browsedPath; +}; \ No newline at end of file diff --git a/src/GUI/SetupRootDialog.ui b/src/GUI/SetupRootDialog.ui new file mode 100644 index 000000000..572e570cb --- /dev/null +++ b/src/GUI/SetupRootDialog.ui @@ -0,0 +1,113 @@ + + + SetupRootDialog + + + Qt::ApplicationModal + + + + 0 + 0 + 618 + 338 + + + + Setup required data files + + + + + + + 128 + 0 + + + + TextLabel + + + + + + + Download + + + + + + + To download a compressed archive of the files, click the 'Download' button. Once the download is complete, extract the files to a suitabe location and choose the folder using the button above. + + + true + + + + + + + + 0 + 80 + + + + Replace me + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop + + + true + + + + + + + Choose folder... + + + true + + + + + + + FlightGear version 3.4.5 + + + + + + + Qt::Horizontal + + + QDialogButtonBox::Cancel + + + + + + + Qt::Vertical + + + + 20 + 40 + + + + + + + + + diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc index 9415e5b6c..4cbca391d 100644 --- a/src/GUI/resources.qrc +++ b/src/GUI/resources.qrc @@ -4,5 +4,6 @@ large-search-icon.png arrow-right-icon.png arrow-left-icon.png + ../../icons/128x128/apps/flightgear.png \ No newline at end of file diff --git a/src/Include/config_cmake.h.in b/src/Include/config_cmake.h.in index 20a2ca777..ec9c1bc07 100644 --- a/src/Include/config_cmake.h.in +++ b/src/Include/config_cmake.h.in @@ -1,6 +1,8 @@ #cmakedefine FG_NDEBUG +#cmakedefine FG_NIGHTLY + #cmakedefine ENABLE_SP_FDM #cmakedefine JSBSIM_USE_GROUNDREACTIONS diff --git a/src/Main/fg_init.cxx b/src/Main/fg_init.cxx index 1d648a83a..41568a42b 100644 --- a/src/Main/fg_init.cxx +++ b/src/Main/fg_init.cxx @@ -160,14 +160,14 @@ using namespace simgear::pkg; extern osg::ref_ptr viewer; // Return the current base package version -string fgBasePackageVersion() { - SGPath base_path( globals->get_fg_root() ); - base_path.append("version"); - if (!base_path.exists()) { +string fgBasePackageVersion(const SGPath& base_path) { + SGPath p(base_path); + p.append("version"); + if (!p.exists()) { return string(); } - sg_gzifstream in( base_path.str() ); + sg_gzifstream in( p.str() ); if (!in.is_open()) { return string(); } diff --git a/src/Main/fg_init.hxx b/src/Main/fg_init.hxx index ef8a6bd82..4162af9aa 100644 --- a/src/Main/fg_init.hxx +++ b/src/Main/fg_init.hxx @@ -30,9 +30,10 @@ // forward decls class SGPropertyNode; +class SGPath; // Return the current base package version -std::string fgBasePackageVersion(); +std::string fgBasePackageVersion(const SGPath& path); bool fgInitHome(); diff --git a/src/Main/main.cxx b/src/Main/main.cxx index e77e70e15..d6ab4c563 100644 --- a/src/Main/main.cxx +++ b/src/Main/main.cxx @@ -68,7 +68,6 @@ extern bool global_crashRptEnabled; #include #include #include -#include #include "fg_commands.hxx" #include "fg_io.hxx" @@ -83,7 +82,6 @@ extern bool global_crashRptEnabled; #include "options.hxx" #if defined(HAVE_QT) -#include #include #endif @@ -458,16 +456,8 @@ int fgMainInit( int argc, char **argv ) // environment variables. This avoids needed a wrapper shell-script on OS-X. showLauncher |= (::getenv("FG_LAUNCHER") != 0); - if (showLauncher) { - QApplication app(argc, argv); - app.setOrganizationName("FlightGear"); - app.setApplicationName("FlightGear"); - app.setOrganizationDomain("flightgear.org"); - - // avoid double Apple menu and other weirdness if both Qt and OSG - // try to initialise various Cocoa structures. - flightgear::WindowBuilder::setPoseAsStandaloneApp(false); - + if (showLauncher) { + QtLauncher::initApp(argc, argv); if (!QtLauncher::runLauncherDialog()) { return EXIT_SUCCESS; } diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 65419fb21..c4e7fc0ca 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -55,6 +55,8 @@ #include #include +#include +#include #include
#include "globals.hxx" @@ -71,13 +73,8 @@ #include "AircraftDirVisitorBase.hxx" #include - -#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H -# include -# include -#else -# include -#endif +#include +#include using std::string; using std::sort; @@ -1904,7 +1901,7 @@ void Options::init(int argc, char **argv, const SGPath& appDataPath) fgOptLogLevel(valueForOption("log-level", "alert").c_str()); if (!p->shouldLoadDefaultConfig) { - setupRoot(); + setupRoot(argc, argv); return; } @@ -1939,7 +1936,7 @@ void Options::init(int argc, char **argv, const SGPath& appDataPath) } // setup FG_ROOT - setupRoot(); + setupRoot(argc, argv); // system.fgfsrc handling if( ! hostname.empty() ) { @@ -2436,9 +2433,11 @@ string Options::platformDefaultRoot() const } #endif -void Options::setupRoot() +void Options::setupRoot(int argc, char **argv) { string root; + bool usingDefaultRoot = false; + if (isOptionSet("fg-root")) { root = valueForOption("fg-root"); // easy! } else { @@ -2447,16 +2446,32 @@ void Options::setupRoot() if ( envp != NULL ) { root = envp; } else { + usingDefaultRoot = true; root = platformDefaultRoot(); } } - SG_LOG(SG_INPUT, SG_INFO, "fg_root = " << root ); + SG_LOG(SG_GENERAL, SG_INFO, "fg_root = " << root ); globals->set_fg_root(root); - -// validate it - static char required_version[] = FLIGHTGEAR_VERSION; - string base_version = fgBasePackageVersion(); + static char required_version[] = FLIGHTGEAR_VERSION; + string base_version = fgBasePackageVersion(root); + +#if defined(HAVE_QT) + if (base_version != required_version) { + QtLauncher::initApp(argc, argv); + if (SetupRootDialog::restoreUserSelectedRoot()) { + SG_LOG(SG_GENERAL, SG_INFO, "restored user selected fg_root = " << globals->get_fg_root() ); + return; + } + + SetupRootDialog dlg(usingDefaultRoot); + dlg.exec(); + if (dlg.result() != QDialog::Accepted) { + exit(-1); + } + } +#else + // validate it if (base_version.empty()) { flightgear::fatalMessageBox("Base package not found", "Required data files not found, check your installation.", @@ -2465,9 +2480,7 @@ void Options::setupRoot() exit(-1); } - if (base_version != required_version) { - // tell the operator how to use this application - + if (base_version != required_version) { flightgear::fatalMessageBox("Base package version mismatch", "Version check failed: please check your installation.", "Found data files for version '" + base_version + @@ -2476,6 +2489,7 @@ void Options::setupRoot() exit(-1); } +#endif } bool Options::shouldLoadDefaultConfig() const diff --git a/src/Main/options.hxx b/src/Main/options.hxx index 40005363f..5fab50fe7 100644 --- a/src/Main/options.hxx +++ b/src/Main/options.hxx @@ -131,8 +131,14 @@ private: int parseOption(const std::string& s); void processArgResult(int result); - - void setupRoot(); + + /** + * Setup the root base, and check it's valid. Bails out with exit(-1) if + * the root package was not found or is the incorrect version. Argv/argv + * are passed since we might potentially show a GUI dialog at this point + * to help the user our (finding a base package), and hence need to init Qt. + */ + void setupRoot(int argc, char **argv); std::string platformDefaultRoot() const;