diff --git a/CMakeLists.txt b/CMakeLists.txt index b83550e22..9caa653b5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -311,7 +311,7 @@ endif (USE_DBUS) ## Qt5 setup setup if (ENABLE_QT) message(STATUS "Qt launcher enabled, checking for Qt 5.1 / qmake") - find_package(Qt5 5.1 COMPONENTS Widgets Network) + find_package(Qt5 5.1 COMPONENTS Widgets Network Qml) if (Qt5Widgets_FOUND) message(STATUS "Will enable Qt launcher GUI") message(STATUS " Qt5Widgets version: ${Qt5Widgets_VERSION_STRING}") diff --git a/src/GUI/AdditionalSettings.cpp b/src/GUI/AdditionalSettings.cpp deleted file mode 100644 index c106be364..000000000 --- a/src/GUI/AdditionalSettings.cpp +++ /dev/null @@ -1,16 +0,0 @@ -#include "AdditionalSettings.h" - -#include "ui_AdditionalSettings.h" - -AdditionalSettings::AdditionalSettings(QWidget *parent) : - SettingsSection(parent), - ui(new Ui::AdditionalSettings) -{ - ui->setupUi(this); - insertSettingsHeader(); -} - -AdditionalSettings::~AdditionalSettings() -{ - delete ui; -} diff --git a/src/GUI/AdditionalSettings.h b/src/GUI/AdditionalSettings.h deleted file mode 100644 index 797fc387a..000000000 --- a/src/GUI/AdditionalSettings.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef ADDITIONAL_SETTINGS_H -#define ADDITIONAL_SETTINGS_H - -#include - -namespace Ui { -class AdditionalSettings; -} - -class AdditionalSettings : public SettingsSection -{ - Q_OBJECT - -public: - explicit AdditionalSettings(QWidget *parent = 0); - ~AdditionalSettings(); - -private: - Ui::AdditionalSettings *ui; -}; - -#endif // ADDITIONAL_SETTINGS_H diff --git a/src/GUI/AdditionalSettings.ui b/src/GUI/AdditionalSettings.ui deleted file mode 100644 index a26f48c7e..000000000 --- a/src/GUI/AdditionalSettings.ui +++ /dev/null @@ -1,52 +0,0 @@ - - - AdditionalSettings - - - - 0 - 0 - 771 - 331 - - - - Form - - - Additional settings - - - - - - Additional settings can be entered here. For information on available settings see <this page> - - - true - - - true - - - - - - - --prop:foo=42 - - - - - - - - SettingsSection - QWidget -
GUI/settingssection.h
- 1 -
-
- - -
diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index 13ae1ea56..c56535c8e 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -78,14 +78,7 @@ if (HAVE_QT) LocationWidget.ui NoOfficialHangar.ui InstallSceneryDialog.ui - EditCustomMPServerDialog.ui UpdateAllAircraft.ui - RenderingSettings.ui - ViewSettings.ui - MPSettings.ui - DownloadSettings.ui - EnvironmentPage.ui - AdditionalSettings.ui ) qt5_add_resources(qrc_sources resources.qrc) @@ -102,6 +95,8 @@ if (HAVE_QT) NavaidDiagram.hxx EditRatingsFilterDialog.cxx EditRatingsFilterDialog.hxx + ExtraSettingsSection.cxx + ExtraSettingsSection.hxx SetupRootDialog.cxx SetupRootDialog.hxx AircraftItemDelegate.hxx @@ -122,40 +117,37 @@ if (HAVE_QT) QtFileDialog.hxx InstallSceneryDialog.hxx InstallSceneryDialog.cxx - EditCustomMPServerDialog.cxx - EditCustomMPServerDialog.hxx previewwindow.cpp previewwindow.h SettingsSection.cpp SettingsSection.h - renderingsettings.cpp - renderingsettings.h - ViewSettings.cpp - ViewSettings.h - MPSettings.cpp - MPSettings.h + SettingsSectionQML.cxx + SettingsSectionQML.hxx AdvancedSettingsButton.h AdvancedSettingsButton.cpp - DownloadSettings.cpp - DownloadSettings.h ToolboxButton.cpp ToolboxButton.h - EnvironmentPage.cpp - EnvironmentPage.h - AdditionalSettings.cpp - AdditionalSettings.h LauncherArgumentTokenizer.cxx LauncherArgumentTokenizer.hxx AircraftSearchFilterModel.cxx AircraftSearchFilterModel.hxx DefaultAircraftLocator.cxx DefaultAircraftLocator.hxx + SettingsWidgets.cxx + SettingsWidgets.hxx + LaunchConfig.cxx + LaunchConfig.hxx + ViewCommandLinePage.cxx + ViewCommandLinePage.hxx + MPServersModel.cpp + MPServersModel.h ${uic_sources} ${qrc_sources}) set_property(TARGET fglauncher PROPERTY AUTOMOC ON) - target_link_libraries(fglauncher Qt5::Core Qt5::Widgets Qt5::Network SimGearCore) + target_link_libraries(fglauncher Qt5::Core Qt5::Widgets Qt5::Network Qt5::Qml SimGearCore) target_include_directories(fglauncher PRIVATE ${PROJECT_BINARY_DIR}/src/GUI) + target_include_directories(fglauncher PRIVATE ${Qt5Qml_PRIVATE_INCLUDE_DIRS}) endif() diff --git a/src/GUI/DefaultAircraftLocator.cxx b/src/GUI/DefaultAircraftLocator.cxx index 118a74c40..e20de371b 100644 --- a/src/GUI/DefaultAircraftLocator.cxx +++ b/src/GUI/DefaultAircraftLocator.cxx @@ -65,4 +65,82 @@ DefaultAircraftLocator::visit(const SGPath& p) return VISIT_CONTINUE; } +WeatherScenariosModel::WeatherScenariosModel(QObject *pr) : + QAbstractListModel(pr) +{ + SGPropertyNode_ptr root = loadXMLDefaults(); + if (root) { + SGPropertyNode_ptr scenarios = root->getNode("environment/weather-scenarios"); + Q_ASSERT(scenarios); + int nChildren = scenarios->nChildren(); + for (int i = 0; i < nChildren; i++) { + SGPropertyNode_ptr scenario = scenarios->getChild(i); + if (strcmp(scenario->getName(), "scenario") != 0) { + continue; + } + + if (scenario->getStringValue("local-weather/tile-type") == std::string("live")) { + continue; + } + + WeatherScenario ws; + ws.name = QString::fromStdString(scenario->getStringValue("name")); + ws.description = QString::fromStdString(scenario->getStringValue("description")).simplified(); + ws.metar = QString::fromStdString(scenario->getStringValue("metar")); + m_scenarios.push_back(ws); + } + } +} + +int WeatherScenariosModel::rowCount(const QModelIndex &index) const +{ + return m_scenarios.size(); +} + +QVariant WeatherScenariosModel::data(const QModelIndex &index, int role) const +{ + const int row = index.row(); + if ((row < 0) || (row >= m_scenarios.size())) { + return QVariant(); + } + + const WeatherScenario& scenario(m_scenarios.at(row)); + if ((role == Qt::DisplayRole) || (role == NameRole)) { + return scenario.name; + } else if (role == DescriptionRole) { + return scenario.description; + } else if (role == MetarRole) { + return scenario.metar; + } + + return QVariant(); +} + +QHash WeatherScenariosModel::roleNames() const +{ + QHash result; + result[NameRole] = "name"; + result[DescriptionRole] = "description"; + result[MetarRole] = "metar"; + return result; +} + +QString WeatherScenariosModel::metarForItem(int index) const +{ + if ((index < 0) || (index >= m_scenarios.size())) { + return QString(); + } + + return m_scenarios.at(index).metar; +} + +QString WeatherScenariosModel::descriptionForItem(int index) const +{ + if ((index < 0) || (index >= m_scenarios.size())) { + return QString(); + } + + return m_scenarios.at(index).description; +} + } // of namespace flightgear diff --git a/src/GUI/DefaultAircraftLocator.hxx b/src/GUI/DefaultAircraftLocator.hxx index 94cfaabe9..2cfe4addc 100644 --- a/src/GUI/DefaultAircraftLocator.hxx +++ b/src/GUI/DefaultAircraftLocator.hxx @@ -6,6 +6,8 @@ #include
+#include + namespace flightgear { @@ -30,6 +32,38 @@ private: SGPath _foundPath; }; +class WeatherScenariosModel : public QAbstractListModel +{ + Q_OBJECT +public: + WeatherScenariosModel(QObject* pr = nullptr); + + int rowCount(const QModelIndex& index) const override; + + QVariant data(const QModelIndex& index, int role) const override; + + QHash roleNames() const override; + + Q_INVOKABLE QString metarForItem(int index) const; + + Q_INVOKABLE QString descriptionForItem(int index) const; +private: + struct WeatherScenario + { + QString name; + QString description; + QString metar; + }; + + std::vector m_scenarios; + + enum { + NameRole = Qt::UserRole + 1, + DescriptionRole, + MetarRole + }; +}; + } #endif // DEFAULTAIRCRAFTLOCATOR_HXX diff --git a/src/GUI/DownloadSettings.cpp b/src/GUI/DownloadSettings.cpp deleted file mode 100644 index e80e47e53..000000000 --- a/src/GUI/DownloadSettings.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "DownloadSettings.h" -#include "ui_DownloadSettings.h" - -DownloadSettings::DownloadSettings(QWidget *parent) : - SettingsSection(parent), - ui(new Ui::DownloadSettings) -{ - ui->setupUi(this); - insertSettingsHeader(); -} - -DownloadSettings::~DownloadSettings() -{ - delete ui; -} diff --git a/src/GUI/DownloadSettings.h b/src/GUI/DownloadSettings.h deleted file mode 100644 index 113cf4b3d..000000000 --- a/src/GUI/DownloadSettings.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef DOWNLOAD_SETTINGS_H -#define DOWNLOAD_SETTINGS_H - -#include - -namespace Ui { -class DownloadSettings; -} - -class DownloadSettings : public SettingsSection -{ - Q_OBJECT - -public: - explicit DownloadSettings(QWidget *parent = 0); - ~DownloadSettings(); - -private: - Ui::DownloadSettings *ui; -}; - -#endif // DOWNLOAD_SETTINGS_H diff --git a/src/GUI/DownloadSettings.qml b/src/GUI/DownloadSettings.qml new file mode 100644 index 000000000..979dfc386 --- /dev/null +++ b/src/GUI/DownloadSettings.qml @@ -0,0 +1,36 @@ +import FlightGear.Launcher 1.0 + +Section { + id: downloadSettings + title: "Downloads" + + Checkbox { + id: terrasync + label: "Download scenery automatically" + description: "FlightGear can automatically download scenery as needed, and check for updates to " + + "the scenery. If you disable this option, you will need to download & install scenery " + + "using an alternative method." + keywords: ["terrasync", "download"] + option: "terrasync" + } + + // file path chooser for downloads directory + + PathChooser { + id: downloadDir + label: "Download location" + description: "FlightGear stores downloaded files (scenery and aircraft) in this location. " + + "Depending on your settings, it may grow to a considerable size (many gigabytes)." + + "If you change the download location, files will need to be downloaded again." + advanced: true + chooseDirectory: true + defaultPath: _config.defaultDownloadDir + option: "download-dir" + dialogPrompt: "Choose a location to store download files." + } + + onApply: { + } + + summary: terrasync.checked ? "scenery downloads;" : "" +} diff --git a/src/GUI/DownloadSettings.ui b/src/GUI/DownloadSettings.ui deleted file mode 100644 index 106f304b3..000000000 --- a/src/GUI/DownloadSettings.ui +++ /dev/null @@ -1,52 +0,0 @@ - - - DownloadSettings - - - - 0 - 0 - 771 - 94 - - - - Form - - - Downloads - - - - - - Download scenery automatically - - - - - - - FlightGear can automatically download and update scenery wherever you fly in the world. This can take some time depening on your Internet connection's speed, but is faster than download the entire world's scenery. - - - true - - - true - - - - - - - - SettingsSection - QWidget -
GUI/settingssection.h
- 1 -
-
- - -
diff --git a/src/GUI/EditCustomMPServerDialog.cxx b/src/GUI/EditCustomMPServerDialog.cxx deleted file mode 100644 index f7599ad6a..000000000 --- a/src/GUI/EditCustomMPServerDialog.cxx +++ /dev/null @@ -1,48 +0,0 @@ -#include "EditCustomMPServerDialog.hxx" -#include "ui_EditCustomMPServerDialog.h" - -#include -#include - -#include "Main/fg_props.hxx" - -EditCustomMPServerDialog::EditCustomMPServerDialog(QWidget *parent) : - QDialog(parent), - ui(new Ui::EditCustomMPServerDialog) -{ - ui->setupUi(this); - QSettings settings; - ui->mpServer->setText(settings.value("mp-custom-host").toString()); - ui->port->setText(settings.value("mp-custom-port").toString()); -} - -EditCustomMPServerDialog::~EditCustomMPServerDialog() -{ - delete ui; -} - -QString EditCustomMPServerDialog::hostname() const -{ - return ui->mpServer->text(); -} - -void EditCustomMPServerDialog::accept() -{ - QSettings settings; - settings.setValue("mp-custom-host", ui->mpServer->text()); - settings.setValue("mp-custom-port", ui->port->text()); - QDialog::accept(); -} - -void EditCustomMPServerDialog::addCustomItem(QComboBox* combo) -{ - QSettings settings; - QString customMPHost = settings.value("mp-custom-host").toString(); - - if (customMPHost.isEmpty()) { - combo->addItem(tr("Custom server..."), "custom"); - return; - } - - combo->addItem(tr("Custom - %1").arg(customMPHost), "custom"); -} \ No newline at end of file diff --git a/src/GUI/EditCustomMPServerDialog.hxx b/src/GUI/EditCustomMPServerDialog.hxx deleted file mode 100644 index e8fda64ba..000000000 --- a/src/GUI/EditCustomMPServerDialog.hxx +++ /dev/null @@ -1,29 +0,0 @@ -#ifndef EDITCUSTOMMPSERVERDIALOG_HXX -#define EDITCUSTOMMPSERVERDIALOG_HXX - -#include - -namespace Ui { -class EditCustomMPServerDialog; -} - -class QComboBox; - -class EditCustomMPServerDialog : public QDialog -{ - Q_OBJECT - -public: - explicit EditCustomMPServerDialog(QWidget *parent = 0); - ~EditCustomMPServerDialog(); - - QString hostname() const; - - virtual void accept(); - - static void addCustomItem(QComboBox* combo); -private: - Ui::EditCustomMPServerDialog *ui; -}; - -#endif // EDITCUSTOMMPSERVERDIALOG_HXX diff --git a/src/GUI/EditCustomMPServerDialog.ui b/src/GUI/EditCustomMPServerDialog.ui deleted file mode 100644 index 887c0a11f..000000000 --- a/src/GUI/EditCustomMPServerDialog.ui +++ /dev/null @@ -1,120 +0,0 @@ - - - EditCustomMPServerDialog - - - - 0 - 0 - 369 - 172 - - - - Enter custom server - - - - :/app-icon-large:/app-icon-large - - - - - - Enter the host name and optional port of the multi-player server you wish to connect to. - - - Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop - - - true - - - - - - - Server: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - flightgear.example.com - - - - - - - Port: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - 5000 - - - 5000 - - - - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - - - - - - buttonBox - accepted() - EditCustomMPServerDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - buttonBox - rejected() - EditCustomMPServerDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/src/GUI/EnvironmentPage.cpp b/src/GUI/EnvironmentPage.cpp deleted file mode 100644 index 07a2ff7b1..000000000 --- a/src/GUI/EnvironmentPage.cpp +++ /dev/null @@ -1,17 +0,0 @@ -#include "EnvironmentPage.h" -#include "ui_EnvironmentPage.h" - -EnvironmentPage::EnvironmentPage(QWidget *parent) : - QWidget(parent), - ui(new Ui::EnvironmentPage) -{ - ui->setupUi(this); - - ui->weatherSection->insertSettingsHeader(); - ui->timeSection->insertSettingsHeader(); -} - -EnvironmentPage::~EnvironmentPage() -{ - delete ui; -} diff --git a/src/GUI/EnvironmentPage.h b/src/GUI/EnvironmentPage.h deleted file mode 100644 index 161646dd8..000000000 --- a/src/GUI/EnvironmentPage.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef ENVIRONMENTPAGE_H -#define ENVIRONMENTPAGE_H - -#include - -namespace Ui { -class EnvironmentPage; -} - -class EnvironmentPage : public QWidget -{ - Q_OBJECT - -public: - explicit EnvironmentPage(QWidget *parent = 0); - ~EnvironmentPage(); - -private: - Ui::EnvironmentPage *ui; -}; - -#endif // ENVIRONMENTPAGE_H diff --git a/src/GUI/EnvironmentPage.ui b/src/GUI/EnvironmentPage.ui deleted file mode 100644 index 86084e5b3..000000000 --- a/src/GUI/EnvironmentPage.ui +++ /dev/null @@ -1,262 +0,0 @@ - - - EnvironmentPage - - - - 0 - 0 - 728 - 580 - - - - Form - - - - - - Time and Date - - - - - - - - Predefined time - - - true - - - - - - - Manually selected date and time - - - true - - - - - - - - Current local time - - - - - Dawn - - - - - Morning - - - - - Noon - - - - - Dusk - - - - - Midnight - - - - - - - - true - - - - - - - Time of day: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - true - - - - - - - - - By default the simulator uses your computer's local time as the local time at your chosen startup location. You can select a standard time of day, or enter an exact time and date. - - - true - - - true - - - - - - - - - - Weather - - - - - - - - Weather: - - - - - - - - Real-world weather - - - - - Clear and calm - - - - - Thunderstorm - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Choose a standard weather scenario, or download weather reports (METAR) automatically online, from the closest airports. - - - true - - - true - - - - - - - - - Season: - - - - - - - - Summer - - - - - Winter - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Changing the season affects which textures are used for scenery, but otherwise has no effect. - - - true - - - true - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - timeSection - label_2 - comboBox_2 - weatherSection - - - - SettingsSection - QWidget -
GUI/SettingsSection.h
- 1 -
-
- - -
diff --git a/src/GUI/ExtraSettingsSection.cxx b/src/GUI/ExtraSettingsSection.cxx new file mode 100644 index 000000000..081446c18 --- /dev/null +++ b/src/GUI/ExtraSettingsSection.cxx @@ -0,0 +1,79 @@ +#include "ExtraSettingsSection.hxx" + +#include +#include +#include +#include + +#include "LauncherArgumentTokenizer.hxx" +#include "LaunchConfig.hxx" +#include "AdvancedSettingsButton.h" + +ExtraSettingsSection::ExtraSettingsSection(QWidget* pr) : + SettingsSection(pr) +{ + setTitle(tr("Additional Settings")); + + QVBoxLayout* topLevelVBox = qobject_cast(layout()); + QLabel* prompt = new QLabel(this); + prompt->setText("Enter additional command-line arguments if any are required. " + "See here " + "for documentation on possible arguments."); + prompt->setWordWrap(true); + prompt->setOpenExternalLinks(true); + + topLevelVBox->addWidget(prompt); + m_argsEdit = new QTextEdit(this); + m_argsEdit->setAcceptRichText(false); + +#if QT_VERSION >= 0x050300 + // don't require Qt 5.3 + m_argsEdit->setPlaceholderText("--option=value --prop:/sim/name=value"); +#endif + topLevelVBox->addWidget(m_argsEdit); + + insertSettingsHeader(); + setShowAdvanced(false); + m_advancedModeToggle->setVisible(false); +} + +QString ExtraSettingsSection::argsText() const +{ + return m_argsEdit->toPlainText(); +} + +void ExtraSettingsSection::setLaunchConfig(LaunchConfig* config) +{ + m_config = config; + SettingsSection::setLaunchConfig(config); +} + +void ExtraSettingsSection::doApply() +{ + LauncherArgumentTokenizer tk; + + Q_FOREACH(auto arg, tk.tokenize(m_argsEdit->toPlainText())) { + m_config->setArg(arg.arg, arg.value); + } +} + +void ExtraSettingsSection::saveState(QSettings &settings) const +{ + settings.setValue("extra-args", m_argsEdit->toPlainText()); +} + +void ExtraSettingsSection::restoreState(QSettings &settings) +{ + m_argsEdit->setText(settings.value("extra-args").toString()); +} + +QString ExtraSettingsSection::summary() const +{ + return QString(); +} + +void ExtraSettingsSection::validateText() +{ + +} + diff --git a/src/GUI/ExtraSettingsSection.hxx b/src/GUI/ExtraSettingsSection.hxx new file mode 100644 index 000000000..ca79fa8d3 --- /dev/null +++ b/src/GUI/ExtraSettingsSection.hxx @@ -0,0 +1,36 @@ +#ifndef EXTRASETTINGSSECTION_HXX +#define EXTRASETTINGSSECTION_HXX + +#include "settingssection.h" + +class QTextEdit; +class LaunchConfig; + +class ExtraSettingsSection : public SettingsSection +{ + Q_OBJECT +public: + ExtraSettingsSection(QWidget* pr = nullptr); + + QString argsText() const; + + void setLaunchConfig(LaunchConfig* config) override; + + virtual void doApply() override; + + void saveState(QSettings &settings) const override; + + void restoreState(QSettings &settings) override; + + QString summary() const; + +protected: + +private: + void validateText(); + + QTextEdit* m_argsEdit; + LaunchConfig* m_config; +}; + +#endif // EXTRASETTINGSSECTION_HXX diff --git a/src/GUI/GeneralSettings.qml b/src/GUI/GeneralSettings.qml new file mode 100644 index 000000000..d16d5b46b --- /dev/null +++ b/src/GUI/GeneralSettings.qml @@ -0,0 +1,32 @@ +import FlightGear.Launcher 1.0 + +Section { + id: generalSettings + title: "General" + + Checkbox { + id: startPaused + label: "Start paused" + description: "Automatically pause the simulator when launching. This is useful " + + "when starting in the air." + keywords: ["pause", "freeze"] + } + + Checkbox { + id: autoCoordination + label: "Enable auto-coordination" + description: "When flying with the mouse, or a joystick lacking a rudder axis, " + + "it's difficult to manually coordinate aileron and rudder movements during " + + "turn. This option automatically commands the rudder to maintain zero " + + "slip angle when banking"; + advanced: true + keywords: ["input", "mouse", "control", "rudder"] + option: "auto-coordination" + } + + onApply: { + if (startPaused.checked) { + _config.setArg("enable-freeze") + } + } +} diff --git a/src/GUI/LaunchConfig.cxx b/src/GUI/LaunchConfig.cxx new file mode 100644 index 000000000..a93290d33 --- /dev/null +++ b/src/GUI/LaunchConfig.cxx @@ -0,0 +1,53 @@ +#include "LaunchConfig.hxx" + +#include
+#include + +LaunchConfig::LaunchConfig(QObject* parent) : + QObject(parent) +{ +} + +void LaunchConfig::reset() +{ + m_values.clear(); +} + +void LaunchConfig::applyToOptions() const +{ + flightgear::Options* options = flightgear::Options::sharedInstance(); + std::for_each(m_values.begin(), m_values.end(), [options](const Arg& arg) + { + options->addOption(arg.arg.toStdString(), arg.value.toStdString()); + }); +} + +void LaunchConfig::setArg(QString name, QString value) +{ + m_values.push_back(Arg(name, value)); +} + +void LaunchConfig::setArg(const std::string &name, const std::string &value) +{ + setArg(QString::fromStdString(name), QString::fromStdString(value)); +} + +void LaunchConfig::setProperty(QString path, QVariant value) +{ + m_values.push_back(Arg("prop", path + "=" + value.toString())); +} + +void LaunchConfig::setEnableDisableOption(QString name, bool value) +{ + m_values.push_back(Arg((value ? "enable-" : "disable-") + name)); +} + +QString LaunchConfig::defaultDownloadDir() const +{ + return QString::fromStdString(flightgear::defaultDownloadDir().utf8Str()); +} + +auto LaunchConfig::values() const -> std::vector +{ + return m_values; +} diff --git a/src/GUI/LaunchConfig.hxx b/src/GUI/LaunchConfig.hxx new file mode 100644 index 000000000..8540fccc6 --- /dev/null +++ b/src/GUI/LaunchConfig.hxx @@ -0,0 +1,54 @@ +#ifndef FG_GUI_LAUNCHCONFIG_HXX +#define FG_GUI_LAUNCHCONFIG_HXX + +#include +#include + +namespace flightgear { class Options; } + +class LaunchConfig : public QObject +{ + Q_OBJECT + + Q_PROPERTY(QString defaultDownloadDir READ defaultDownloadDir CONSTANT) +public: + class Arg + { + public: + explicit Arg(QString k, QString v = QString()) : arg(k), value(v) {} + + QString arg; + QString value; + }; + + + LaunchConfig(QObject* parent = nullptr); + + void reset(); + void applyToOptions() const; + + std::vector values() const; + + Q_INVOKABLE void setArg(QString name, QString value = QString()); + + Q_INVOKABLE void setArg(const std::string& name, const std::string& value = std::string()); + + Q_INVOKABLE void setProperty(QString path, QVariant value); + + Q_INVOKABLE void setEnableDisableOption(QString name, bool value); + + // ensure a property is /not/ set? + + // save and restore API? + + QString defaultDownloadDir() const; + +signals: + void collect(); + +private: + std::vector m_values; + QString m_defaultDownloadDir; +}; + +#endif diff --git a/src/GUI/Launcher.ui b/src/GUI/Launcher.ui index 22fea7a5d..5b26a07ff 100644 --- a/src/GUI/Launcher.ui +++ b/src/GUI/Launcher.ui @@ -198,6 +198,20 @@ + + + + Add-ons + + + + :/toolbox-addons:/toolbox-addons + + + 5 + + + @@ -227,6 +241,9 @@ + + QFrame::NoFrame + 0 @@ -403,11 +420,14 @@ - ©2017, Curtis L Olson. Licensed under the GNU Public License (GPL) version 2. See <here> for more information + <html><head/><body><p>©2017 FlightGear contributiors. Licensed under the GNU Public License. See <a href="http://www.flightgear.org"><span style=" text-decoration: underline; color:#0000ff;">here</span></a> for more information</p></body></html> true + + true + @@ -428,16 +448,16 @@ 4 - 0 + 4 - 2 + 4 - 0 + 4 - 2 + 4 @@ -526,7 +546,39 @@ - + + + + 4 + + + 4 + + + 4 + + + 4 + + + + + true + + + + + 0 + 0 + 643 + 644 + + + + + + + @@ -551,8 +603,8 @@ 0 0 - 173 - 28 + 643 + 644 @@ -598,7 +650,7 @@ - + Search... @@ -629,12 +681,6 @@ QPushButton
GUI/ToolboxButton.h
- - EnvironmentPage - QWidget -
GUI/EnvironmentPage.h
- 1 -
diff --git a/src/GUI/launchermainwindow.cxx b/src/GUI/LauncherMainWindow.cxx similarity index 69% rename from src/GUI/launchermainwindow.cxx rename to src/GUI/LauncherMainWindow.cxx index 0667d2dc3..64687b804 100644 --- a/src/GUI/launchermainwindow.cxx +++ b/src/GUI/LauncherMainWindow.cxx @@ -5,12 +5,19 @@ #include #include #include +#include +#include + +#include +#include +#include + +#include // simgear headers #include // FlightGear headers -#include #include #include
#include @@ -20,19 +27,19 @@ // launcher headers #include "QtLauncher.hxx" -#include "renderingsettings.h" -#include "ViewSettings.h" -#include "MPSettings.h" -#include "DownloadSettings.h" -#include "AdditionalSettings.h" #include "EditRatingsFilterDialog.hxx" #include "AircraftItemDelegate.hxx" #include "AircraftModel.hxx" #include "PathsDialog.hxx" -#include "EditCustomMPServerDialog.hxx" -#include "LauncherArgumentTokenizer.hxx" #include "AircraftSearchFilterModel.hxx" #include "DefaultAircraftLocator.hxx" +#include "SettingsWidgets.hxx" +#include "previewwindow.h" +#include "LaunchConfig.hxx" +#include "SettingsSectionQML.hxx" +#include "ExtraSettingsSection.hxx" +#include "ViewCommandLinePage.hxx" +#include "MPServersModel.h" #include "ui_Launcher.h" #include "ui_NoOfficialHangar.h" @@ -86,29 +93,52 @@ private: #include "LauncherMainWindow.moc" +QQmlPrivate::AutoParentResult launcher_autoParent(QObject* thing, QObject* parent) +{ + SettingsSection* ss = qobject_cast(parent); + SettingsControl* sc = qobject_cast(thing); + if (ss && sc) { + qInfo() << "let's do this!" << ss << sc; + sc->setParent(ss); + return QQmlPrivate::Parented; + } + + qWarning() << "Unsuitable" << thing << parent; + return QQmlPrivate::IncompatibleObject; +} + ////////////////////////////////////////////////////////////////////////////// LauncherMainWindow::LauncherMainWindow() : QMainWindow(), m_ui(NULL), - m_subsystemIdleTimer(NULL), - m_doRestoreMPServer(false) + m_subsystemIdleTimer(NULL) { m_ui.reset(new Ui::Launcher); m_ui->setupUi(this); -#if QT_VERSION >= 0x050300 - // don't require Qt 5.3 - //m_ui->commandLineArgs->setPlaceholderText("--option=value --prop:/sim/name=value"); -#endif + QMenuBar* mb = menuBar(); + QMenu* toolsMenu = mb->addMenu(tr("Tools")); + QAction* restoreDefaultsAction = toolsMenu->addAction(tr("Restore defaults...")); + connect(restoreDefaultsAction, &QAction::triggered, + this, &LauncherMainWindow::onRestoreDefaults); + + QAction* changeDataAction = toolsMenu->addAction(tr("Select data files location...")); + connect(changeDataAction, &QAction::triggered, + this, &LauncherMainWindow::onChangeDataDir); + + QAction* viewCommandLineAction = toolsMenu->addAction(tr("View command-line")); + connect(viewCommandLineAction, &QAction::triggered, + this, &LauncherMainWindow::onViewCommandLine); #if QT_VERSION >= 0x050200 m_ui->aircraftFilter->setClearButtonEnabled(true); #endif - for (int i=0; i<4; ++i) { - m_ratingFilters[i] = 3; - } + m_serversModel = new MPServersModel(this); + m_serversModel->refresh(); + + initQML(); m_subsystemIdleTimer = new QTimer(this); m_subsystemIdleTimer->setInterval(0); @@ -134,9 +164,7 @@ LauncherMainWindow::LauncherMainWindow() : connect(m_ui->locationButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton); connect(m_ui->environmentButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton); connect(m_ui->settingsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton); - - - // connect(m_ui->quitButton, SIGNAL(clicked()), this, SLOT(onQuit())); + connect(m_ui->addOnsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton); connect(m_ui->aircraftHistory, &QPushButton::clicked, this, &LauncherMainWindow::onPopupAircraftHistory); @@ -160,39 +188,6 @@ LauncherMainWindow::LauncherMainWindow() : m_ui->aircraftHistory->setIcon(historyIcon); m_ui->locationHistory->setIcon(historyIcon); -#if 0 - connect(m_ui->timeOfDayCombo, SIGNAL(currentIndexChanged(int)), - this, SLOT(updateSettingsSummary())); - connect(m_ui->seasonCombo, SIGNAL(currentIndexChanged(int)), - this, SLOT(updateSettingsSummary())); - connect(m_ui->fetchRealWxrCheckbox, SIGNAL(toggled(bool)), - this, SLOT(updateSettingsSummary())); - connect(m_ui->rembrandtCheckbox, SIGNAL(toggled(bool)), - this, SLOT(updateSettingsSummary())); - connect(m_ui->terrasyncCheck, SIGNAL(toggled(bool)), - this, SLOT(updateSettingsSummary())); - connect(m_ui->startPausedCheck, SIGNAL(toggled(bool)), - this, SLOT(updateSettingsSummary())); - connect(m_ui->msaaCheckbox, SIGNAL(toggled(bool)), - this, SLOT(updateSettingsSummary())); - - connect(m_ui->mpBox, SIGNAL(toggled(bool)), - this, SLOT(updateSettingsSummary())); - connect(m_ui->mpCallsign, SIGNAL(textChanged(QString)), - this, SLOT(updateSettingsSummary())); - - connect(m_ui->rembrandtCheckbox, SIGNAL(toggled(bool)), - this, SLOT(onRembrandtToggled(bool))); - connect(m_ui->terrasyncCheck, &QCheckBox::toggled, - this, &LauncherMainWindow::onToggleTerrasync); -#endif - - updateSettingsSummary(); - -#if 0 - connect(m_ui->mpServerCombo, SIGNAL(activated(int)), - this, SLOT(onMPServerActivated(int))); -#endif m_aircraftModel = new AircraftItemModel(this); m_aircraftProxy->setSourceModel(m_aircraftModel); @@ -216,6 +211,8 @@ LauncherMainWindow::LauncherMainWindow() : this, &LauncherMainWindow::onRequestPackageUninstall); connect(delegate, &AircraftItemDelegate::cancelDownload, this, &LauncherMainWindow::onCancelDownload); + connect(delegate, &AircraftItemDelegate::showPreviews, + this, &LauncherMainWindow::onShowPreviews); connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted, this, &LauncherMainWindow::onAircraftInstalledCompleted); @@ -223,18 +220,14 @@ LauncherMainWindow::LauncherMainWindow() : this, &LauncherMainWindow::onAircraftInstallFailed); connect(m_aircraftModel, &AircraftItemModel::scanCompleted, this, &LauncherMainWindow::updateSelectedAircraft); -#if 0 - connect(m_ui->restoreDefaultsButton, &QPushButton::clicked, - this, &LauncherMainWindow::onRestoreDefaults); -#endif AddOnsPage* addOnsPage = new AddOnsPage(NULL, globals->packageRoot()); - connect(addOnsPage, &AddOnsPage::downloadDirChanged, - this, &LauncherMainWindow::onDownloadDirChanged); connect(addOnsPage, &AddOnsPage::sceneryPathsChanged, this, &LauncherMainWindow::setSceneryPaths); + connect(addOnsPage, &AddOnsPage::aircraftPathsChanged, + this, &LauncherMainWindow::onAircraftPathsChanged); + m_ui->stack->addWidget(addOnsPage); - // m_ui->tabWidget->addTab(addOnsPage, tr("Add-ons")); // after any kind of reset, try to restore selection and scroll // to match the m_selectedAircraft. This needs to be delayed // fractionally otherwise the scrollTo seems to be ignored, @@ -247,49 +240,120 @@ LauncherMainWindow::LauncherMainWindow() : m_aircraftModel->setPackageRoot(globals->packageRoot()); m_aircraftModel->scanDirs(); + buildSettingsSections(); + buildEnvironmentSections(); + + m_viewCommandLinePage = new ViewCommandLinePage; + m_viewCommandLinePage->setLaunchConfig(m_config); + m_ui->stack->addWidget(m_viewCommandLinePage); + checkOfficialCatalogMessage(); restoreSettings(); + updateSettingsSummary(); +} - onRefreshMPServers(); +void LauncherMainWindow::initQML() +{ + QQmlPrivate::RegisterAutoParent autoparent = { 0, &launcher_autoParent }; + QQmlPrivate::qmlregister(QQmlPrivate::AutoParentRegistration, &autoparent); - RenderingSettings* renderSettings = new RenderingSettings(m_ui->settingsScrollContents); + qmlRegisterType("FlightGear.Launcher", 1, 0, "Section"); + qmlRegisterType("FlightGear.Launcher", 1, 0, "Checkbox"); + qmlRegisterType("FlightGear.Launcher", 1, 0, "Combo"); + qmlRegisterType("FlightGear.Launcher", 1, 0, "Spinbox"); + qmlRegisterType("FlightGear.Launcher", 1, 0, "LineEdit"); + qmlRegisterType("FlightGear.Launcher", 1, 0, "DateTime"); + qmlRegisterType("FlightGear.Launcher", 1, 0, "PathChooser"); + qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "QAIM", "no"); + + qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "Control", "Base class"); + qmlRegisterUncreatableType("FlightGear.Launcher", 1, 0, "LaunchConfig", "Singleton API"); + + m_config = new LaunchConfig(this); + connect(m_config, &LaunchConfig::collect, this, &LauncherMainWindow::collectAircraftArgs); + m_ui->location->setLaunchConfig(m_config); + + m_qmlEngine = new QQmlEngine(this); + m_qmlEngine->rootContext()->setContextProperty("_config", m_config); + m_qmlEngine->rootContext()->setContextProperty("_launcher", this); + m_qmlEngine->rootContext()->setContextProperty("_mpServers", m_serversModel); + + flightgear::WeatherScenariosModel* weatherScenariosModel = new flightgear::WeatherScenariosModel(this); + m_qmlEngine->rootContext()->setContextProperty("_weatherScenarios", weatherScenariosModel); +} + +void LauncherMainWindow::buildSettingsSections() +{ QVBoxLayout* settingsVBox = static_cast(m_ui->settingsScrollContents->layout()); - settingsVBox->addWidget(renderSettings); - ViewSettings* viewSettings = new ViewSettings(m_ui->settingsScrollContents); - settingsVBox->addWidget(viewSettings); + QStringList sections = QStringList() << "general" << "mp" << "downloads" << "view" << "render"; + Q_FOREACH (QString section, sections) { + QQmlComponent* comp = new QQmlComponent(m_qmlEngine, "qrc:/settings/" + section, this); + if (comp->isError()) { + qWarning() << "Errors parsing settings section:" << section << "\n" << comp->errorString(); + } else { + SettingsSection* ss = qobject_cast(comp->create()); + if (!ss) { + qWarning() << "failed to create settings section from" << section; + } else { + ss->insertSettingsHeader(); + ss->setLaunchConfig(m_config); + ss->setParent(m_ui->settingsScrollContents); + settingsVBox->addWidget(ss); + connect(ss, &SettingsSection::summaryChanged, + this, &LauncherMainWindow::updateSettingsSummary); + } + } + } - MPSettings* mpSettings = new MPSettings(m_ui->settingsScrollContents); - settingsVBox->addWidget(mpSettings); + m_extraSettings = new ExtraSettingsSection(m_ui->settingsScrollContents); + m_extraSettings->setLaunchConfig(m_config); + settingsVBox->addWidget(m_extraSettings); + settingsVBox->addStretch(1); - DownloadSettings* downloadSettings = new DownloadSettings(m_ui->settingsScrollContents); - settingsVBox->addWidget(downloadSettings); + // disable search for the moment, not implemented + m_ui->settingsSearchEdit->hide(); +} - AdditionalSettings* addSettings = new AdditionalSettings(m_ui->settingsScrollContents); - settingsVBox->addWidget(addSettings); +void LauncherMainWindow::buildEnvironmentSections() +{ + QVBoxLayout* settingsVBox = new QVBoxLayout; + m_ui->environmentScrollContents->setLayout(settingsVBox); + + QStringList sections = QStringList() << "time" << "weather"; + Q_FOREACH (QString section, sections) { + QQmlComponent* comp = new QQmlComponent(m_qmlEngine, "qrc:/environment/" + section, this); + if (comp->isError()) { + qWarning() << "Errors parsing environment section:" << section << "\n" << comp->errorString(); + } else { + SettingsSection* ss = qobject_cast(comp->create()); + if (!ss) { + qWarning() << "failed to create environment section from" << section; + } else { + ss->insertSettingsHeader(); + ss->setLaunchConfig(m_config); + ss->setParent(m_ui->environmentScrollContents); + settingsVBox->addWidget(ss); + connect(ss, &SettingsSection::summaryChanged, + this, &LauncherMainWindow::updateSettingsSummary); + } + } + } settingsVBox->addStretch(1); } LauncherMainWindow::~LauncherMainWindow() { - // if we don't cancel this now, it may complete after we are gone, - // causing a crash when the SGCallback fires (SGCallbacks don't clean up - // when their subject is deleted) - globals->get_subsystem()->client()->cancelRequest(m_mpServerRequest); } bool LauncherMainWindow::execInApp() { - m_inAppMode = true; - // m_ui->tabWidget->removeTab(3); - // m_ui->tabWidget->removeTab(3); - - // m_ui->runButton->setText(tr("Apply")); - // m_ui->quitButton->setText(tr("Cancel")); - - disconnect(m_ui->flyButton, SIGNAL(clicked()), this, SLOT(onRun())); - connect(m_ui->flyButton, SIGNAL(clicked()), this, SLOT(onApply())); + m_inAppMode = true; + m_ui->addOnsButton->hide(); + m_ui->settingsButton->hide(); + disconnect(m_ui->flyButton, SIGNAL(clicked()), this, SLOT(onRun())); + connect(m_ui->flyButton, SIGNAL(clicked()), this, SLOT(onApply())); m_runInApp = true; show(); @@ -307,16 +371,6 @@ void LauncherMainWindow::restoreSettings() restoreGeometry(settings.value("window-geometry").toByteArray()); -#if 0 - m_ui->rembrandtCheckbox->setChecked(settings.value("enable-rembrandt", false).toBool()); - m_ui->terrasyncCheck->setChecked(settings.value("enable-terrasync", true).toBool()); - m_ui->fullScreenCheckbox->setChecked(settings.value("start-fullscreen", false).toBool()); - m_ui->msaaCheckbox->setChecked(settings.value("enable-msaa", false).toBool()); - m_ui->fetchRealWxrCheckbox->setChecked(settings.value("enable-realwx", true).toBool()); - m_ui->startPausedCheck->setChecked(settings.value("start-paused", false).toBool()); - m_ui->timeOfDayCombo->setCurrentIndex(settings.value("timeofday", 0).toInt()); - m_ui->seasonCombo->setCurrentIndex(settings.value("season", 0).toInt()); -#endif // full paths to -set.xml files m_recentAircraft = QUrl::fromStringList(settings.value("recent-aircraft").toStringList()); @@ -373,15 +427,13 @@ void LauncherMainWindow::restoreSettings() updateSelectedAircraft(); maybeRestoreAircraftSelection(); -#if 0 - m_ui->commandLineArgs->setPlainText(settings.value("additional-args").toString()); - m_ui->mpBox->setChecked(settings.value("mp-enabled").toBool()); - m_ui->mpCallsign->setText(settings.value("mp-callsign").toString()); -#endif - // don't restore MP server here, we do it after a refresh - m_doRestoreMPServer = true; -} + Q_FOREACH(SettingsSection* ss, findChildren()) { + ss->restoreState(settings); + } + + m_serversModel->requestRestore(); + } void LauncherMainWindow::delayedAircraftModelReset() { @@ -407,40 +459,16 @@ void LauncherMainWindow::maybeRestoreAircraftSelection() void LauncherMainWindow::saveSettings() { QSettings settings; -#if 0 - settings.setValue("enable-rembrandt", m_ui->rembrandtCheckbox->isChecked()); - settings.setValue("enable-terrasync", m_ui->terrasyncCheck->isChecked()); - settings.setValue("enable-msaa", m_ui->msaaCheckbox->isChecked()); - settings.setValue("start-fullscreen", m_ui->fullScreenCheckbox->isChecked()); - settings.setValue("enable-realwx", m_ui->fetchRealWxrCheckbox->isChecked()); - settings.setValue("start-paused", m_ui->startPausedCheck->isChecked()); -#endif settings.setValue("ratings-filter", m_ui->ratingsFilterCheck->isChecked()); settings.setValue("only-show-installed", m_ui->onlyShowInstalledCheck->isChecked()); settings.setValue("recent-aircraft", QUrl::toStringList(m_recentAircraft)); settings.setValue("recent-location-sets", m_recentLocations); -#if 0 - settings.setValue("timeofday", m_ui->timeOfDayCombo->currentIndex()); - settings.setValue("season", m_ui->seasonCombo->currentIndex()); - settings.setValue("additional-args", m_ui->commandLineArgs->toPlainText()); - - settings.setValue("mp-callsign", m_ui->mpCallsign->text()); - settings.setValue("mp-server", m_ui->mpServerCombo->currentData()); - settings.setValue("mp-enabled", m_ui->mpBox->isChecked()); -#endif settings.setValue("window-geometry", saveGeometry()); -} -void LauncherMainWindow::setEnableDisableOptionFromCheckbox(QCheckBox* cbox, QString name) const -{ - flightgear::Options* opt = flightgear::Options::sharedInstance(); - std::string stdName(name.toStdString()); - if (cbox->isChecked()) { - opt->addOption("enable-" + stdName, ""); - } else { - opt->addOption("disable-" + stdName, ""); + Q_FOREACH(SettingsSection* ss, findChildren()) { + ss->saveState(settings); } } @@ -449,53 +477,35 @@ void LauncherMainWindow::closeEvent(QCloseEvent *event) qApp->exit(-1); } -void LauncherMainWindow::onRun() +void LauncherMainWindow::collectAircraftArgs() { - flightgear::Options* opt = flightgear::Options::sharedInstance(); -#if 0 - setEnableDisableOptionFromCheckbox(m_ui->terrasyncCheck, "terrasync"); - setEnableDisableOptionFromCheckbox(m_ui->fetchRealWxrCheckbox, "real-weather-fetch"); - setEnableDisableOptionFromCheckbox(m_ui->rembrandtCheckbox, "rembrandt"); - setEnableDisableOptionFromCheckbox(m_ui->fullScreenCheckbox, "fullscreen"); -// setEnableDisableOptionFromCheckbox(m_ui->startPausedCheck, "freeze"); - - bool startPaused = m_ui->startPausedCheck->isChecked() || - m_ui->location->shouldStartPaused(); - if (startPaused) { - opt->addOption("enable-freeze", ""); - } -#endif - -#if 0 - // MSAA is more complex - if (!m_ui->rembrandtCheckbox->isChecked()) { - if (m_ui->msaaCheckbox->isChecked()) { - globals->get_props()->setIntValue("/sim/rendering/multi-sample-buffers", 1); - globals->get_props()->setIntValue("/sim/rendering/multi-samples", 4); - } else { - globals->get_props()->setIntValue("/sim/rendering/multi-sample-buffers", 0); - } - } -#endif - // aircraft if (!m_selectedAircraft.isEmpty()) { if (m_selectedAircraft.isLocalFile()) { QFileInfo setFileInfo(m_selectedAircraft.toLocalFile()); - opt->addOption("aircraft-dir", setFileInfo.dir().absolutePath().toStdString()); + m_config->setArg("aircraft-dir", setFileInfo.dir().absolutePath()); QString setFile = setFileInfo.fileName(); Q_ASSERT(setFile.endsWith("-set.xml")); setFile.truncate(setFile.count() - 8); // drop the '-set.xml' portion - opt->addOption("aircraft", setFile.toStdString()); + m_config->setArg("aircraft", setFile); } else if (m_selectedAircraft.scheme() == "package") { - QString qualifiedId = m_selectedAircraft.path(); // no need to set aircraft-dir, handled by the corresponding code // in fgInitAircraft - opt->addOption("aircraft", qualifiedId.toStdString()); + m_config->setArg("aircraft", m_selectedAircraft.path()); } else { qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft; } + } +} +void LauncherMainWindow::onRun() +{ + flightgear::Options* opt = flightgear::Options::sharedInstance(); + m_config->reset(); + m_config->collect(); + + // aircraft + if (!m_selectedAircraft.isEmpty()) { // manage aircraft history if (m_recentAircraft.contains(m_selectedAircraft)) m_recentAircraft.removeOne(m_selectedAircraft); @@ -504,46 +514,9 @@ void LauncherMainWindow::onRun() m_recentAircraft.pop_back(); } -#if 0 - if (m_ui->mpBox->isChecked()) { - std::string callSign = m_ui->mpCallsign->text().toStdString(); - if (!callSign.empty()) { - opt->addOption("callsign", callSign); - } - - QString host = m_ui->mpServerCombo->currentData().toString(); - int port = DEFAULT_MP_PORT; - if (host == "custom") { - QSettings settings; - host = settings.value("mp-custom-host").toString(); - } else { - port = findMPServerPort(host.toStdString()); - } - - if (port == 0) { - port = DEFAULT_MP_PORT; - } - globals->get_props()->setStringValue("/sim/multiplay/txhost", host.toStdString()); - globals->get_props()->setIntValue("/sim/multiplay/txport", port); - } -#endif - - m_ui->location->setLocationProperties(); +// m_ui->location->setLocationProperties(); updateLocationHistory(); -#if 0 - // time of day - if (m_ui->timeOfDayCombo->currentIndex() != 0) { - QString dayval = m_ui->timeOfDayCombo->currentText().toLower(); - opt->addOption("timeofday", dayval.toStdString()); - } - - if (m_ui->seasonCombo->currentIndex() != 0) { - QString seasonName = m_ui->seasonCombo->currentText().toLower(); - opt->addOption("season", seasonName.toStdString()); - } -#endif - QSettings settings; QString downloadDir = settings.value("download-dir").toString(); if (!downloadDir.isEmpty()) { @@ -560,8 +533,6 @@ void LauncherMainWindow::onRun() d.mkpath(downloadDir); } } - - opt->addOption("download-dir", downloadDir.toStdString()); } // scenery paths @@ -575,24 +546,12 @@ void LauncherMainWindow::onRun() globals->append_aircraft_path(path.toStdString()); } - // additional arguments -#if 0 - ArgumentsTokenizer tk; - Q_FOREACH(ArgumentsTokenizer::Arg a, tk.tokenize(m_ui->commandLineArgs->toPlainText())) { - if (a.arg.startsWith("prop:")) { - QString v = a.arg.mid(5) + "=" + a.value; - opt->addOption("prop", v.toStdString()); - } else { - opt->addOption(a.arg.toStdString(), a.value.toStdString()); - } - } -#endif - if (settings.contains("restore-defaults-on-run")) { settings.remove("restore-defaults-on-run"); opt->addOption("restore-defaults", ""); } + m_config->applyToOptions(); saveSettings(); // set a positive value here so we can detect this case in runLauncherDialog @@ -667,6 +626,7 @@ void LauncherMainWindow::onQuit() } } +#if 0 void LauncherMainWindow::onToggleTerrasync(bool enabled) { if (enabled) { @@ -695,6 +655,7 @@ void LauncherMainWindow::onToggleTerrasync(bool enabled) } } // of is enabled } +#endif void LauncherMainWindow::onAircraftInstalledCompleted(QModelIndex index) { @@ -766,6 +727,14 @@ void LauncherMainWindow::onRequestPackageUninstall(const QModelIndex& index) } } +void LauncherMainWindow::onShowPreviews(const QModelIndex &index) +{ + QVariant urls = index.data(AircraftPreviewsRole); + + PreviewWindow* previewWindow = new PreviewWindow; + previewWindow->setUrls(urls.toList()); +} + void LauncherMainWindow::onCancelDownload(const QModelIndex& index) { QString pkg = index.data(AircraftPackageIdRole).toString(); @@ -798,6 +767,15 @@ void LauncherMainWindow::onRestoreDefaults() flightgear::restartTheApp(); } +void LauncherMainWindow::onViewCommandLine() +{ + m_ui->stack->setCurrentIndex(6); + Q_FOREACH (ToolboxButton* tb, findChildren()) { + tb->setChecked(false); + } + m_viewCommandLinePage->update(); +} + void LauncherMainWindow::maybeUpdateSelectedAircraft(QModelIndex index) { QUrl u = index.data(AircraftURIRole).toUrl(); @@ -860,6 +838,7 @@ void LauncherMainWindow::onClickToolboxButton() Q_FOREACH (ToolboxButton* tb, findChildren()) { tb->setChecked(tb->property("pageIndex").toInt() == pageIndex); } + saveSettings(); } void LauncherMainWindow::setSceneryPaths() @@ -952,47 +931,18 @@ void LauncherMainWindow::onEditRatingsFilter() void LauncherMainWindow::updateSettingsSummary() { QStringList summary; -#if 0 - if (m_ui->timeOfDayCombo->currentIndex() > 0) { - summary.append(QString(m_ui->timeOfDayCombo->currentText().toLower())); - } - if (m_ui->seasonCombo->currentIndex() > 0) { - summary.append(QString(m_ui->seasonCombo->currentText().toLower())); - } - - if (m_ui->rembrandtCheckbox->isChecked()) { - summary.append("Rembrandt enabled"); - } else if (m_ui->msaaCheckbox->isChecked()) { - summary.append("anti-aliasing"); - } - - if (m_ui->fetchRealWxrCheckbox->isChecked()) { - summary.append("live weather"); - } - - if (m_ui->terrasyncCheck->isChecked()) { - summary.append("automatic scenery downloads"); - } - - if (m_ui->startPausedCheck->isChecked()) { - summary.append("paused"); - } - - if (m_ui->mpBox->isChecked()) { - summary.append(tr("multiplayer: %1").arg(m_ui->mpCallsign->text())); + Q_FOREACH(SettingsSection* ss, findChildren()) { + QString s = ss->summary(); + if (!s.isEmpty()) { + QStringList pieces = s.split(';', QString::SkipEmptyParts); + summary.append(pieces); + } } QString s = summary.join(", "); s[0] = s[0].toUpper(); m_ui->settingsDescription->setText(s); -#endif -} - -void LauncherMainWindow::onRembrandtToggled(bool b) -{ - // Rembrandt and multi-sample are exclusive - // m_ui->msaaCheckbox->setEnabled(!b); } void LauncherMainWindow::onShowInstalledAircraftToggled(bool b) @@ -1060,7 +1010,7 @@ void LauncherMainWindow::onOfficialCatalogMessageLink(QUrl link) QSettings settings; settings.setValue("hide-official-catalog-message", true); } else if (s == "action:add-official") { - AddOnsPage::addDefaultCatalog(this); + AddOnsPage::addDefaultCatalog(this, false /* not silent */); } checkOfficialCatalogMessage(); @@ -1084,110 +1034,6 @@ void LauncherMainWindow::checkUpdateAircraft() } } -void LauncherMainWindow::onRefreshMPServers() -{ - if (m_mpServerRequest.get()) { - return; // in-progress - } - - string url(fgGetString("/sim/multiplay/serverlist-url", - "http://liveries.flightgear.org/mpstatus/mpservers.xml")); - - if (url.empty()) { - SG_LOG(SG_IO, SG_ALERT, "do_multiplayer.refreshserverlist: no URL given"); - return; - } - - SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true); - m_mpServerRequest.reset(new RemoteXMLRequest(url, targetnode)); - m_mpServerRequest->done(this, &LauncherMainWindow::onRefreshMPServersDone); - m_mpServerRequest->fail(this, &LauncherMainWindow::onRefreshMPServersFailed); - globals->get_subsystem()->makeRequest(m_mpServerRequest); -} - -void LauncherMainWindow::onRefreshMPServersDone(simgear::HTTP::Request*) -{ -#if 0 - // parse the properties - SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true); - m_ui->mpServerCombo->clear(); - - for (int i=0; inChildren(); ++i) { - SGPropertyNode* c = targetnode->getChild(i); - if (c->getName() != std::string("server")) { - continue; - } - - if (c->getBoolValue("online") != true) { - // only list online servers - continue; - } - - QString name = QString::fromStdString(c->getStringValue("name")); - QString loc = QString::fromStdString(c->getStringValue("location")); - QString host = QString::fromStdString(c->getStringValue("hostname")); - m_ui->mpServerCombo->addItem(tr("%1 - %2").arg(name,loc), host); - } - - EditCustomMPServerDialog::addCustomItem(m_ui->mpServerCombo); - restoreMPServerSelection(); -#endif - m_mpServerRequest.clear(); -} - -void LauncherMainWindow::onRefreshMPServersFailed(simgear::HTTP::Request*) -{ - qWarning() << "refreshing MP servers failed:" << QString::fromStdString(m_mpServerRequest->responseReason()); - m_mpServerRequest.clear(); -#if 0 - EditCustomMPServerDialog::addCustomItem(m_ui->mpServerCombo); - restoreMPServerSelection(); -#endif -} - -void LauncherMainWindow::restoreMPServerSelection() -{ -#if 0 - if (m_doRestoreMPServer) { - QSettings settings; - int index = m_ui->mpServerCombo->findData(settings.value("mp-server")); - if (index >= 0) { - m_ui->mpServerCombo->setCurrentIndex(index); - } - m_doRestoreMPServer = false; - } -#endif -} - -void LauncherMainWindow::onMPServerActivated(int index) -{ -#if 0 - if (m_ui->mpServerCombo->itemData(index) == "custom") { - EditCustomMPServerDialog dlg(this); - dlg.exec(); - if (dlg.result() == QDialog::Accepted) { - m_ui->mpServerCombo->setItemText(index, tr("Custom - %1").arg(dlg.hostname())); - } - } -#endif -} - -int LauncherMainWindow::findMPServerPort(const std::string& host) -{ - SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true); - for (int i=0; inChildren(); ++i) { - SGPropertyNode* c = targetnode->getChild(i); - if (c->getName() != std::string("server")) { - continue; - } - - if (c->getStringValue("hostname") == host) { - return c->getIntValue("port"); - } - } - - return 0; -} simgear::pkg::PackageRef LauncherMainWindow::packageForAircraftURI(QUrl uri) const { @@ -1206,3 +1052,40 @@ void LauncherMainWindow::onAircraftPathsChanged() m_aircraftModel->setPaths(settings.value("aircraft-paths").toStringList()); m_aircraftModel->scanDirs(); } + +void LauncherMainWindow::onChangeDataDir() +{ + QString currentLocText; + QSettings settings; + QString root = settings.value("fg-root").toString(); + if (root.isNull()) { + currentLocText = tr("Currently the built-in data files are being used"); + } else { + currentLocText = tr("Currently using location: %1").arg(root); + } + + QMessageBox mbox(this); + mbox.setText(tr("Change the data files used by FlightGear?")); + mbox.setInformativeText(tr("FlightGear requires additional files to operate. " + "(Also called the base package, or fg-data) " + "You can restart FlightGear and choose a " + "different data files location, or restore the default setting. %1").arg(currentLocText)); + QPushButton* quitButton = mbox.addButton(tr("Restart FlightGear now"), QMessageBox::YesRole); + mbox.addButton(QMessageBox::Cancel); + mbox.setDefaultButton(QMessageBox::Cancel); + mbox.setIconPixmap(QPixmap(":/app-icon-large")); + + mbox.exec(); + if (mbox.clickedButton() != quitButton) { + return; + } + + { + QSettings settings; + // set the option to the magic marker value + settings.setValue("fg-root", "!ask"); + } // scope the ensure settings are written nicely + + flightgear::restartTheApp(); +} + diff --git a/src/GUI/LauncherMainWindow.hxx b/src/GUI/LauncherMainWindow.hxx index 3135f4564..9870dac54 100644 --- a/src/GUI/LauncherMainWindow.hxx +++ b/src/GUI/LauncherMainWindow.hxx @@ -41,7 +41,11 @@ class AircraftProxyModel; class AircraftItemModel; class QCheckBox; class CatalogListModel; -class RemoteXMLRequest; +class QQmlEngine; +class LaunchConfig; +class ExtraSettingsSection; +class ViewCommandLinePage; +class MPServersModel; class LauncherMainWindow : public QMainWindow { @@ -67,7 +71,6 @@ private slots: void onQuit(); - void onAircraftSelected(const QModelIndex& index); void onRequestPackageInstall(const QModelIndex& index); void onRequestPackageUninstall(const QModelIndex& index); @@ -81,10 +84,6 @@ private slots: void updateSettingsSummary(); - - void onRembrandtToggled(bool b); - void onToggleTerrasync(bool enabled); - void onSubsytemIdleTimeout(); void onAircraftInstalledCompleted(QModelIndex index); @@ -95,11 +94,9 @@ private slots: void maybeRestoreAircraftSelection(); void onRestoreDefaults(); + void onViewCommandLine(); - void onDownloadDirChanged(); - - void onRefreshMPServers(); - void onMPServerActivated(int index); + Q_INVOKABLE void onDownloadDirChanged(); void onUpdateAllAircraft(); @@ -109,6 +106,8 @@ private slots: void setSceneryPaths(); void onAircraftPathsChanged(); + + void onChangeDataDir(); private: /** @@ -124,20 +123,12 @@ private: QModelIndex proxyIndexForAircraftURI(QUrl uri) const; QModelIndex sourceIndexForAircraftURI(QUrl uri) const; - void setEnableDisableOptionFromCheckbox(QCheckBox* cbox, QString name) const; - simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const; void checkOfficialCatalogMessage(); void onOfficialCatalogMessageLink(QUrl link); - void checkUpdateAircraft(); - void onRefreshMPServersDone(simgear::HTTP::Request*); - void onRefreshMPServersFailed(simgear::HTTP::Request*); - int findMPServerPort(const std::string& host); - void restoreMPServerSelection(); - // need to wait after a model reset before restoring selection and // scrolling, to give the view time it seems. void delayedAircraftModelReset(); @@ -146,9 +137,15 @@ private: void updateLocationHistory(); bool shouldShowOfficialCatalogMessage() const; + void buildSettingsSections(); + void buildEnvironmentSections(); + void collectAircraftArgs(); + void initQML(); + QScopedPointer m_ui; AircraftProxyModel* m_aircraftProxy; AircraftItemModel* m_aircraftModel; + MPServersModel* m_serversModel = nullptr; QUrl m_selectedAircraft; QList m_recentAircraft; @@ -156,13 +153,12 @@ private: bool m_inAppMode = false; bool m_runInApp = false; bool m_accepted = false; - - int m_ratingFilters[4]; - - SGSharedPtr m_mpServerRequest; - bool m_doRestoreMPServer; - + int m_ratingFilters[4] = {3, 3, 3, 3}; QVariantList m_recentLocations; + QQmlEngine* m_qmlEngine = nullptr; + LaunchConfig* m_config = nullptr; + ExtraSettingsSection* m_extraSettings = nullptr; + ViewCommandLinePage* m_viewCommandLinePage = nullptr; }; #endif // of LAUNCHER_MAIN_WINDOW_HXX diff --git a/src/GUI/LocationWidget.cxx b/src/GUI/LocationWidget.cxx index 1c5d3b3be..622f65f8c 100644 --- a/src/GUI/LocationWidget.cxx +++ b/src/GUI/LocationWidget.cxx @@ -31,6 +31,7 @@ #include "AirportDiagram.hxx" #include "NavaidDiagram.hxx" +#include "LaunchConfig.hxx" #include #include @@ -423,6 +424,12 @@ LocationWidget::~LocationWidget() delete m_ui; } +void LocationWidget::setLaunchConfig(LaunchConfig *config) +{ + m_config = config; + connect(m_config, &LaunchConfig::collect, this, &LocationWidget::onCollectConfig); +} + void LocationWidget::restoreSettings() { QSettings settings; @@ -489,8 +496,6 @@ bool LocationWidget::shouldStartPaused() const // navaid, start paused return true; } - - return false; } QVariantMap LocationWidget::saveLocation() const @@ -646,42 +651,120 @@ void LocationWidget::setLocationProperties() void LocationWidget::applyPositionOffset() { - fgSetDouble("/sim/presets/altitude-ft", m_ui->altitudeSpinbox->value()); - fgSetBool("/sim/presets/on-ground", m_ui->altitudeSpinbox->value() > 0); + if (m_ui->altitudeSpinbox->value() > 0) { + m_config->setArg("altitude", QString::number(m_ui->altitudeSpinbox->value())); + } - fgSetString("/sim/presets/speed-set", "knots"); - fgSetDouble("/sim/presets/airspeed-kt", m_ui->airspeedSpinbox->value()); - - fgSetDouble("/sim/presets/heading-deg", m_ui->headingSpinbox->value()); + m_config->setArg("vc", QString::number(m_ui->airspeedSpinbox->value())); + m_config->setArg("heading", QString::number(m_ui->headingSpinbox->value())); if (m_ui->offsetGroup->isChecked()) { // flip direction of azimuth to balance the flip done in fgApplyStartOffset // I don't know why that flip exists but changing it there will break // command-line compatability so compensating here instead int offsetAzimuth = m_ui->offsetBearingSpinbox->value() - 180; - - fgSetDouble("/sim/presets/offset-azimuth-deg", offsetAzimuth); - fgSetDouble("/sim/presets/offset-distance-nm", m_ui->offsetNmSpinbox->value()); + m_config->setArg("offset-azimuth", QString::number(offsetAzimuth)); + m_config->setArg("offset-distance", QString::number(m_ui->offsetNmSpinbox->value())); } } +void LocationWidget::onCollectConfig() +{ + if (m_locationIsLatLon) { + m_config->setArg("lat", QString::number(m_geodLocation.getLatitudeDeg())); + m_config->setArg("lon", QString::number(m_geodLocation.getLongitudeDeg())); + applyPositionOffset(); + return; + } + + if (!m_location) { + return; + } + + if (FGAirport::isAirportType(m_location.ptr())) { + FGAirport* apt = static_cast(m_location.ptr()); + m_config->setArg("airport", QString::fromStdString(apt->ident())); + + if (m_ui->runwayRadio->isChecked()) { + if (apt->type() == FGPositioned::AIRPORT) { + int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt(); + if (index >= 0) { + // explicit runway choice + FGRunwayRef runway = apt->getRunwayByIndex(index); + m_config->setArg("runway", QString::fromStdString(runway->ident())); + + // set nav-radio 1 based on selected runway + if (runway->ILS()) { + double mhz = runway->ILS()->get_freq() / 100.0; + m_config->setArg("nav1", QString("%1:%2").arg(runway->headingDeg()).arg(mhz)); + } + } + + if (m_ui->onFinalCheckbox->isChecked()) { + m_config->setArg("glideslope", std::string("3.0")); + m_config->setArg("offset-distance", QString::number(m_ui->approachDistanceSpin->value())); + m_config->setArg("on-ground", std::string("false")); + } + } else if (apt->type() == FGPositioned::HELIPORT) { + int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt(); + if (index >= 0) { + // explicit pad choice + FGHelipadRef pad = apt->getHelipadByIndex(index); + m_config->setArg("runway", pad->ident()); + } + } else { + qWarning() << Q_FUNC_INFO << "implement me"; + } + + } else if (m_ui->parkingRadio->isChecked()) { + // parking selection + m_config->setArg("parkpos", m_ui->parkingCombo->currentText()); + } + // of location is an airport + } else { + // location is a navaid + // note setting the ident here is ambigious, we really only need and + // want the 'navaid-id' property. However setting the 'real' option + // gives a better UI experience (eg existing Position in Air dialog) + FGPositioned::Type ty = m_location->type(); + switch (ty) { + case FGPositioned::VOR: + m_config->setArg("vor", m_location->ident()); + setNavRadioOption(); + break; + + case FGPositioned::NDB: + m_config->setArg("ndb", m_location->ident()); + setNavRadioOption(); + break; + + case FGPositioned::FIX: + m_config->setArg("fix", m_location->ident()); + break; + default: + break; + }; + + // set disambiguation property + m_config->setProperty("/sim/presets/navaid-id", QString::number(m_location->guid())); + applyPositionOffset(); + } // of navaid location +} + void LocationWidget::setNavRadioOption() { - flightgear::Options* opt = flightgear::Options::sharedInstance(); - if (m_location->type() == FGPositioned::VOR) { FGNavRecordRef nav(static_cast(m_location.ptr())); double mhz = nav->get_freq() / 100.0; int heading = 0; // add heading support QString navOpt = QString("%1:%2").arg(heading).arg(mhz); - opt->addOption("nav1", navOpt.toStdString()); + m_config->setArg("nav1", navOpt); } else { FGNavRecordRef nav(static_cast(m_location.ptr())); int khz = nav->get_freq() / 100; int heading = 0; QString adfOpt = QString("%1:%2").arg(heading).arg(khz); - qDebug() << "ADF opt is:" << adfOpt; - opt->addOption("adf1", adfOpt.toStdString()); + m_config->setArg("adf1", adfOpt); } } diff --git a/src/GUI/LocationWidget.hxx b/src/GUI/LocationWidget.hxx index 408e98afa..0a192fa32 100644 --- a/src/GUI/LocationWidget.hxx +++ b/src/GUI/LocationWidget.hxx @@ -28,6 +28,7 @@ #include #include +#include "LaunchConfig.hxx" #include "QtLauncher_fwd.hxx" namespace Ui { @@ -44,6 +45,8 @@ public: explicit LocationWidget(QWidget *parent = 0); ~LocationWidget(); + void setLaunchConfig(LaunchConfig* config); + QString locationDescription() const; void setBaseLocation(FGPositionedRef ref); @@ -66,6 +69,8 @@ private Q_SLOTS: void onLocationChanged(); void onOffsetDataChanged(); void onHeadingChanged(); + + void onCollectConfig(); private: void onSearch(); @@ -98,6 +103,8 @@ private: FGPositionedList m_recentLocations; LauncherAircraftType m_aircraftType; + + LaunchConfig* m_config = nullptr; }; #endif // LOCATIONWIDGET_H diff --git a/src/GUI/MPServersModel.cpp b/src/GUI/MPServersModel.cpp new file mode 100644 index 000000000..0d28d6b6f --- /dev/null +++ b/src/GUI/MPServersModel.cpp @@ -0,0 +1,184 @@ +#include "MPServersModel.h" + +#include +#include + +#include +#include + +#include
+#include
+ +#include "LaunchConfig.hxx" + +const int IsCustomIndexRole = Qt::UserRole + 1; + +MPServersModel::MPServersModel(QObject* parent) : + QAbstractListModel(parent) +{ + +} + +MPServersModel::~MPServersModel() +{ + // if we don't cancel this now, it may complete after we are gone, + // causing a crash when the SGCallback fires (SGCallbacks don't clean up + // when their subject is deleted) + globals->get_subsystem()->client()->cancelRequest(m_mpServerRequest); +} + +int MPServersModel::rowCount(const QModelIndex&) const +{ + return m_servers.size() + 1; +} + +QVariant MPServersModel::data(const QModelIndex &index, int role) const +{ + int row = index.row(); + if ((row < 0) || (row > m_servers.size())) { + return QVariant(); + } + + if (row == m_servers.size()) { + if (role == Qt::DisplayRole) { + return tr("Custom server"); + } else if (role == IsCustomIndexRole) { + return true; + } + + return QVariant(); + } + + const ServerInfo& sv(m_servers.at(row)); + if (role == Qt::DisplayRole) { + return tr("%1 - %2").arg(sv.name).arg(sv.location); + } else if (role == IsCustomIndexRole) { + return false; + } + + return QVariant(); +} + +QHash MPServersModel::roleNames() const +{ + QHash result; + result[IsCustomIndexRole] = "isCustomIndex"; + return result; +} + +void MPServersModel::refresh() +{ + if (m_mpServerRequest.get()) { + return; // in-progress + } + + string url(fgGetString("/sim/multiplay/serverlist-url", + "http://liveries.flightgear.org/mpstatus/mpservers.xml")); + + if (url.empty()) { + return; + } + + SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true); + m_mpServerRequest.reset(new RemoteXMLRequest(url, targetnode)); + m_mpServerRequest->done(this, &MPServersModel::onRefreshMPServersDone); + m_mpServerRequest->fail(this, &MPServersModel::onRefreshMPServersFailed); + globals->get_subsystem()->makeRequest(m_mpServerRequest); +} + +void MPServersModel::onRefreshMPServersDone(simgear::HTTP::Request*) +{ + beginResetModel(); + // parse the properties + SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true); + m_servers.clear(); + + for (int i=0; inChildren(); ++i) { + SGPropertyNode* c = targetnode->getChild(i); + if (c->getName() != std::string("server")) { + continue; + } + + if (c->getBoolValue("online") != true) { + // only list online servers + continue; + } + + QString name = QString::fromStdString(c->getStringValue("name")); + QString loc = QString::fromStdString(c->getStringValue("location")); + QString host = QString::fromStdString(c->getStringValue("hostname")); + int port = c->getIntValue("port"); + m_servers.push_back(ServerInfo(name, loc, host, port)); + } + endResetModel(); + + restoreMPServerSelection(); + m_mpServerRequest.clear(); +} + +void MPServersModel::onRefreshMPServersFailed(simgear::HTTP::Request*) +{ + qWarning() << "refreshing MP servers failed:" << QString::fromStdString(m_mpServerRequest->responseReason()); + m_mpServerRequest.clear(); + beginResetModel(); + m_servers.clear(); + endResetModel(); + restoreMPServerSelection(); +} + +void MPServersModel::restoreMPServerSelection() +{ + if (m_doRestoreMPServer) { + QSettings settings; + settings.beginGroup("mpSettings"); + QString host = settings.value("mp-server").toString(); + if (host == "__custom__") { + emit restoreIndex(m_servers.size()); + } else { + // restore a built-in server + auto it = std::find_if(m_servers.begin(), m_servers.end(), [host](const ServerInfo& info) + { return (info.host == host); }); + + if (it != m_servers.end()) { + emit restoreIndex(std::distance(m_servers.begin(), it)); + } + } + + m_doRestoreMPServer = false; + } +} + +void MPServersModel::requestRestore() +{ + m_doRestoreMPServer = true; +} + +QString MPServersModel::serverForIndex(int index) const +{ + if ((index < 0) || (index > m_servers.size())) { + return QString(); + } + + if (index == m_servers.size()) { + return "__custom__"; + } + + return m_servers.at(index).host; +} + +int MPServersModel::portForIndex(int index) const +{ + if ((index < 0) || (index >= m_servers.size())) { + return 0; + } + + return m_servers.at(index).port; +} + +MPServersModel::ServerInfo::ServerInfo(QString n, QString l, QString h, int p) +{ + name = n; + location = l; + host = h; + port = p; +} diff --git a/src/GUI/MPServersModel.h b/src/GUI/MPServersModel.h new file mode 100644 index 000000000..496e7a08d --- /dev/null +++ b/src/GUI/MPServersModel.h @@ -0,0 +1,53 @@ +#ifndef MPSERVERSMODEL_H +#define MPSERVERSMODEL_H + +#include + +#include + + +class MPServersModel : public QAbstractListModel +{ + Q_OBJECT + +public: + MPServersModel(QObject* parent = nullptr); + ~MPServersModel(); + + int rowCount(const QModelIndex& index) const override; + + QVariant data(const QModelIndex& index, int role) const override; + + QHash roleNames() const override; + + void onRefreshMPServersDone(simgear::HTTP::Request*); + void onRefreshMPServersFailed(simgear::HTTP::Request*); + int findMPServerPort(const std::string& host); + void restoreMPServerSelection(); + + void refresh(); + + void requestRestore(); + + Q_INVOKABLE QString serverForIndex(int index) const; + Q_INVOKABLE int portForIndex(int index) const; +signals: + void restoreIndex(int index); + +private: + + SGSharedPtr m_mpServerRequest; + bool m_doRestoreMPServer = false; + + struct ServerInfo + { + ServerInfo(QString n, QString l, QString h, int port); + + QString name, location, host; + int port = 0; + }; + + std::vector m_servers; +}; + +#endif // MPSERVERSMODEL_H diff --git a/src/GUI/MPSettings.cpp b/src/GUI/MPSettings.cpp deleted file mode 100644 index 648f45388..000000000 --- a/src/GUI/MPSettings.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "MPSettings.h" -#include "ui_MPSettings.h" - -MPSettings::MPSettings(QWidget *parent) : - SettingsSection(parent), - ui(new Ui::MPSettings) -{ - ui->setupUi(this); - insertSettingsHeader(); -} - -MPSettings::~MPSettings() -{ - delete ui; -} diff --git a/src/GUI/MPSettings.h b/src/GUI/MPSettings.h deleted file mode 100644 index 9f01e553a..000000000 --- a/src/GUI/MPSettings.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef MP_SETTINGS_H -#define MP_SETTINGS_H - -#include - -namespace Ui { -class MPSettings; -} - -class MPSettings : public SettingsSection -{ - Q_OBJECT - -public: - explicit MPSettings(QWidget *parent = 0); - ~MPSettings(); - -private: - Ui::MPSettings *ui; -}; - -#endif // MP_SETTINGS_H diff --git a/src/GUI/MPSettings.qml b/src/GUI/MPSettings.qml new file mode 100644 index 000000000..225147cc2 --- /dev/null +++ b/src/GUI/MPSettings.qml @@ -0,0 +1,88 @@ +import FlightGear.Launcher 1.0 +import QtQml 2.0 + +Section { + // note this id is used, hard-coded, in MPServersModel + id: mpSettings + title: "Multi-player" + + Checkbox { + id: enableMP + label: "Connect to the multi-player network" + description: "Flightgear supporters maintain a network of server to enable global multi-user " + + "flight. This requires a moderately fast Inernet connection to be usable. Your aircraft " + + "will be visible to other users online, and you will see their aircraft." + keywords: ["network", "mp"] + } + + LineEdit { + id: callSign + enabled: enableMP.checked + label: "Call-sign" + description: "Enter a call-sign you will use online. This is visible to all users and is " + + "how ATC services and other pilots will refer to you. " + + "(Maximum of ten charatcers permitted)" + placeholder: "D-FGFS" + } + + Combo { + id: mpServer + label: "Server" + enabled: enableMP.checked + description: "Select a server close to you for better responsiveness and reduced lag when flying online." + model: _mpServers + + readonly property bool currentIsCustom: (model.serverForIndex(selectedIndex) == "__custom__") + } + + Connections + { + target: _mpServers + onRestoreIndex: { + mpServer.selectedIndex = index + } + } + + LineEdit { + id: mpCustomServer + enabled: enableMP.checked + label: "Custom server" + visible: mpServer.currentIsCustom + description: "Enter a server hostname or IP address, and a port number. For example 'localhost:5001'" + placeholder: "localhost:5001" + } + + onApply: { + if (enableMP.checked) { + if (mpServer.currentIsCustom) { + var pieces = mpCustomServer.value.split(':') + _config.setProperty("/sim/multiplay/txhost", pieces[0]); + _config.setProperty("/sim/multiplay/txport", pieces[1]); + } else { + var sel = mpServer.selectedIndex + _config.setProperty("/sim/multiplay/txhost", _mpServers.serverForIndex(sel)); + var port = _mpServers.portForIndex(sel); + if (port == 0) { + port = 5000; // default MP port + } + + _config.setProperty("/sim/multiplay/txport", port); + } + + if (callSign.value.length > 0) { + _config.setArg("callsign", callSign.value) + } + } + } + + onRestore: { + // nothing to do, restoration is done by the C++ code + // in MPServersModel::restoreMPServerSelection + } + + onSave: { + saveSetting("mp-server", _mpServers.serverForIndex(mpServer.selectedIndex)); + } + + summary: enableMP.checked ? "multi-player;" : "" +} diff --git a/src/GUI/MPSettings.ui b/src/GUI/MPSettings.ui deleted file mode 100644 index 6583e9a4a..000000000 --- a/src/GUI/MPSettings.ui +++ /dev/null @@ -1,160 +0,0 @@ - - - MPSettings - - - - 0 - 0 - 771 - 331 - - - - Form - - - Multi-player - - - - - - Connect to the multi-player network - - - - - - - Fly with hundreds of other pilots around the world. - - - true - - - true - - - - - - - - - Callsign: - - - false - - - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Your call-sign identifies you to other pliots and controllers on the network. Callsigns are limited to ten characters. - - - true - - - true - - - - - - - - - Server: - - - true - - - - - - - true - - - - Automatic - - - - - Thread for culling and drawing - - - - - Separate threads for culling and drawing - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Choose a specific multiplayer server, or enter a custom server - - - true - - - true - - - true - - - - - - - - SettingsSection - QWidget -
GUI/settingssection.h
- 1 -
-
- - -
diff --git a/src/GUI/PathsDialog.cxx b/src/GUI/PathsDialog.cxx index 678b83fcd..33c57a367 100644 --- a/src/GUI/PathsDialog.cxx +++ b/src/GUI/PathsDialog.cxx @@ -53,26 +53,12 @@ AddOnsPage::AddOnsPage(QWidget *parent, simgear::pkg::RootRef root) : connect(m_ui->removeAircraftPath, &QToolButton::clicked, this, &AddOnsPage::onRemoveAircraftPath); - connect(m_ui->changeDownloadDir, &QPushButton::clicked, - this, &AddOnsPage::onChangeDownloadDir); - - connect(m_ui->clearDownloadDir, &QPushButton::clicked, - this, &AddOnsPage::onClearDownloadDir); - - connect(m_ui->changeDataDir, &QPushButton::clicked, - this, &AddOnsPage::onChangeDataDir); connect(m_ui->installSceneryButton, &QPushButton::clicked, this, &AddOnsPage::onInstallScenery); m_ui->sceneryPathsList->setToolTip( tr("After changing this list, please restart the launcher to avoid " "possibly inconsistent behavior.")); - m_ui->changeDownloadDir->setToolTip( - tr("After changing this location, you may have to restart the launcher " - "to avoid inconsistent behavior.")); - m_ui->clearDownloadDir->setToolTip( - tr("If you use this button, you may have to restart the launcher " - "to avoid inconsistent behavior.")); m_ui->installSceneryButton->setToolTip( tr("After installing scenery, you may have to restart the launcher " "to avoid inconsistent behavior.")); @@ -85,11 +71,6 @@ AddOnsPage::AddOnsPage(QWidget *parent, simgear::pkg::RootRef root) : QStringList aircraftPaths = settings.value("aircraft-paths").toStringList(); m_ui->aircraftPathsList->addItems(aircraftPaths); - QVariant downloadDir = settings.value("download-dir"); - if (downloadDir.isValid()) { - m_downloadDir = downloadDir.toString(); - } - updateUi(); } @@ -294,76 +275,11 @@ void AddOnsPage::onRemoveCatalog() updateUi(); } -void AddOnsPage::onChangeDownloadDir() -{ - QString path = QFileDialog::getExistingDirectory(this, - tr("Choose downloads folder"), - m_downloadDir); - if (path.isEmpty()) { - return; // user cancelled - } - - m_downloadDir = path; - setDownloadDir(); -} - -void AddOnsPage::onClearDownloadDir() -{ - // does this need an 'are you sure'? - m_downloadDir.clear(); - - setDownloadDir(); -} - -void AddOnsPage::setDownloadDir() -{ - QSettings settings; - if (m_downloadDir.isEmpty()) { - settings.remove("download-dir"); - } else { - settings.setValue("download-dir", m_downloadDir); - } - - if (m_downloadDir.isEmpty()) { - flightgear::Options::sharedInstance()->clearOption("download-dir"); - } else { - flightgear::Options::sharedInstance()->setOption("download-dir", m_downloadDir.toStdString()); - } - - emit downloadDirChanged(); - updateUi(); -} - -void AddOnsPage::onChangeDataDir() -{ - QMessageBox mbox(this); - mbox.setText(tr("Change the data files used by FlightGear?")); - mbox.setInformativeText(tr("FlightGear requires additional files to operate. " - "(Also called the base package, or fg-data) " - "You can restart FlightGear and choose a " - "different data files location, or restore the default setting.")); - QPushButton* quitButton = mbox.addButton(tr("Restart FlightGear now"), QMessageBox::YesRole); - mbox.addButton(QMessageBox::Cancel); - mbox.setDefaultButton(QMessageBox::Cancel); - mbox.setIconPixmap(QPixmap(":/app-icon-large")); - - mbox.exec(); - if (mbox.clickedButton() != quitButton) { - return; - } - - { - QSettings settings; - // set the option to the magic marker value - settings.setValue("fg-root", "!ask"); - } // scope the ensure settings are written nicely - - flightgear::restartTheApp(); -} - void AddOnsPage::onInstallScenery() { - InstallSceneryDialog dlg(this, m_downloadDir); + QSettings settings; + QString downloadDir = settings.value("download-dir").toString(); + InstallSceneryDialog dlg(this, downloadDir); if (dlg.exec() == QDialog::Accepted) { if (!haveSceneryPath(dlg.sceneryPath())) { m_ui->sceneryPathsList->addItem(dlg.sceneryPath()); @@ -374,30 +290,6 @@ void AddOnsPage::onInstallScenery() void AddOnsPage::updateUi() { - QString s = m_downloadDir; - if (s.isEmpty()) { - s = QString::fromStdString(flightgear::defaultDownloadDir().utf8Str()); - s.append(tr(" (default)")); - m_ui->clearDownloadDir->setEnabled(false); - } else { - m_ui->clearDownloadDir->setEnabled(true); - } - - QString m = tr("Download location: %1").arg(s); - m_ui->downloadLocation->setText(m); - - QString dataLoc; - QSettings settings; - QString root = settings.value("fg-root").toString(); - if (root.isNull()) { - dataLoc = tr("built-in"); - } else { - dataLoc = root; - } - - m_ui->dataLocation->setText(tr("Data location: %1").arg(dataLoc)); - - FGHTTPClient* http = globals->get_subsystem(); m_ui->addDefaultCatalogButton->setEnabled(!http->isDefaultCatalogInstalled()); } diff --git a/src/GUI/PathsDialog.hxx b/src/GUI/PathsDialog.hxx index aa1a2fb21..14102dd66 100644 --- a/src/GUI/PathsDialog.hxx +++ b/src/GUI/PathsDialog.hxx @@ -23,7 +23,6 @@ public: static void addDefaultCatalog(QWidget* pr, bool silent); signals: - void downloadDirChanged(); void sceneryPathsChanged(); void aircraftPathsChanged(); @@ -38,14 +37,9 @@ private slots: void onRemoveCatalog(); void onAddDefaultCatalog(); - void onChangeDownloadDir(); - void onClearDownloadDir(); - - void onChangeDataDir(); void onInstallScenery(); private: void updateUi(); - void setDownloadDir(); void saveAircraftPaths(); void saveSceneryPaths(); @@ -54,7 +48,6 @@ private: Ui::AddOnsPage* m_ui; CatalogListModel* m_catalogsModel; simgear::pkg::RootRef m_packageRoot; - QString m_downloadDir; }; diff --git a/src/GUI/PathsDialog.ui b/src/GUI/PathsDialog.ui index eb6167d16..d2143f7bd 100644 --- a/src/GUI/PathsDialog.ui +++ b/src/GUI/PathsDialog.ui @@ -29,108 +29,6 @@ 4 - - - - TextLabel - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 16 - 20 - - - - - - - - - 11 - - - - FlightGear needs certain files (sometimes called 'fg-data') to function - these are included as part of stable releases. - - - true - - - - - - - Change... - - - - - - - - - TextLabel - - - - - - - - - Qt::Horizontal - - - - 16 - 20 - - - - - - - - - 11 - - - - Aircraft hangars and automatic scenery downloads may cause this location to contain large numbers of files. Changing this location will cause files to be downloaded again. - - - true - - - - - - - Change... - - - - - - - Use default - - - - - diff --git a/src/GUI/RenderSettings.qml b/src/GUI/RenderSettings.qml new file mode 100644 index 000000000..861d9096f --- /dev/null +++ b/src/GUI/RenderSettings.qml @@ -0,0 +1,54 @@ +import FlightGear.Launcher 1.0 + +Section { + id: renderingSetttings + title: "Rendering" + + readonly property bool rembrandt: (renderer.selectedIndex == 2) + readonly property bool alsEnabled: (renderer.selectedIndex == 1) + readonly property bool msaaEnabled: !rembrandt && (msaa.selectedIndex > 0) + + Combo { + id: renderer + label: "Renderer" + choices: ["Default", "Atmospheric Light-Scattering", "Rembrandt"] + description: descriptions[selectedIndex] + defaultIndex: 0 + + readonly property var descriptions: [ + "The default renderer provides standard visuals with maximum compatability", + "The ALS renderer uses a sophisticated physical atmospheric model and several " + + "other effects to give realistic rendering of large distances.", + "Rembrandt is a configurable multi-pass renderer which supports shadow-maps, cinematic " + + "effects and more. However, not all aircraft appear correctly and performance will " + + "depend greatly on your system hardware." + ] + } + + Combo { + id: msaa + label: "Anti-aliasing" + description: "Anti-aliasing improves the appearance of high-contrast edges and lines." + + "This is especially noticeable on sloping or diagonal egdes. " + + "Higher settings can reduce performance." + keywords: ["msaa"] + choices: ["Off", "2x", "4x"] + enabled: !rembrandt + property var data: [0, 2, 4]; + defaultIndex: 0 + } + + onApply: { + _config.setProperty("/sim/rendering/multi-sample-buffers", msaaEnabled) + _config.setProperty("/sim/rendering/multi-samples", msaa.data[msaa.selectedIndex]) + + _config.setEnableDisableOption("rembrandt", rembrandt); + + if (alsEnabled) { + _config.setProperty("/sim/rendering/shaders/skydome", true); + } + } + + summary: (rembrandt ? "Rembrandt;" : (alsEnabled ? "ALS;" : "")) + + (msaaEnabled ? "anti-aliasing;" : "") +} diff --git a/src/GUI/SettingsSectionQML.cxx b/src/GUI/SettingsSectionQML.cxx new file mode 100644 index 000000000..429e01ef4 --- /dev/null +++ b/src/GUI/SettingsSectionQML.cxx @@ -0,0 +1,161 @@ +#include "SettingsSectionQML.hxx" + +#include +#include +#include +#include +#include + +#include "AdvancedSettingsButton.h" +#include "SettingsWidgets.hxx" +#include "LaunchConfig.hxx" + +SettingsSectionQML::SettingsSectionQML() +{ + +} + +void SettingsSectionQML::internalUpdateAdvanced() +{ + Q_FOREACH (SettingsControl* w, controls()) { + if (w->advanced()) { + w->setVisible(m_showAdvanced); + } + + if (w->property("simple").toBool()) { + w->setVisible(!m_showAdvanced); + } + } +} + +void SettingsSectionQML::controls_append(QQmlListProperty *prop, QObject *item) +{ + SettingsSectionQML* self = qobject_cast(prop->object); + QVBoxLayout* topLevelVBox = qobject_cast(self->layout()); + self->m_controls.append(item); + + SettingsControl* control = qobject_cast(item); + if (control) { + // following two lines would not be needed if the custom + // setParent function was working :( + control->setParent(nullptr); + control->setParent(self); + topLevelVBox->addWidget(control); + } +} + +void SettingsSectionQML::controls_clear(QQmlListProperty *prop) +{ + SettingsSectionQML* self = qobject_cast(prop->object); + QVBoxLayout* topLevelVBox = qobject_cast(self->layout()); + + Q_FOREACH (QObject* c, self->m_controls) { + SettingsControl* control = qobject_cast(c); + if (control) { + topLevelVBox->removeWidget(control); + } + } + +} + +int SettingsSectionQML::controls_count(QQmlListProperty *prop) +{ + SettingsSectionQML* self = qobject_cast(prop->object); + return self->m_controls.count(); +} + +QObject *SettingsSectionQML::control_at(QQmlListProperty *prop, int index) +{ + SettingsSectionQML* self = qobject_cast(prop->object); + return self->m_controls.at(index); +} + +QQmlListProperty SettingsSectionQML::qmlControls() +{ + return QQmlListProperty(this, nullptr, + &SettingsSectionQML::controls_append, + &SettingsSectionQML::controls_count, + &SettingsSectionQML::control_at, + &SettingsSectionQML::controls_clear); +} + +QList SettingsSectionQML::controls() const +{ + return findChildren(); +} + +void SettingsSectionQML::updateShowAdvanced() +{ + bool needsShowAdvanced = false; + Q_FOREACH (SettingsControl* w, controls()) { + needsShowAdvanced |= w->advanced(); + } + + m_advancedModeToggle->setVisible(needsShowAdvanced); +} + +void SettingsSectionQML::saveState(QSettings &settings) const +{ + QQmlContext* context = QQmlEngine::contextForObject(this); + QString s = context->nameForObject(const_cast(this)); + settings.beginGroup(s); + Q_FOREACH (SettingsControl* control, controls()) { + control->saveState(settings); + } + const_cast(this)->save(); + settings.endGroup(); +} + +void SettingsSectionQML::restoreState(QSettings &settings) +{ + QQmlContext* context = QQmlEngine::contextForObject(this); + QString s = context->nameForObject(const_cast(this)); + settings.beginGroup(s); + Q_FOREACH (SettingsControl* control, controls()) { + control->restoreState(settings); + } + settings.endGroup(); + emit restore(); +} + +void SettingsSectionQML::doApply() +{ + LaunchConfig* config = qobject_cast(sender()); + Q_FOREACH (SettingsControl* control, controls()) { + control->apply(config); + } + emit apply(); +} + +QString SettingsSectionQML::summary() const +{ + return m_summary; +} + +void SettingsSectionQML::saveSetting(QString key, QVariant value) +{ + QSettings settings; + QQmlContext* context = QQmlEngine::contextForObject(this); + QString s = context->nameForObject(const_cast(this)); + settings.beginGroup(s); + settings.setValue(key, value); +} + +QVariant SettingsSectionQML::restoreSetting(QString key) +{ + QSettings settings; + QQmlContext* context = QQmlEngine::contextForObject(this); + QString s = context->nameForObject(const_cast(this)); + settings.beginGroup(s); + return settings.value(key); +} + +void SettingsSectionQML::setSummary(QString summary) +{ + if (m_summary == summary) + return; + + m_summary = summary; + emit qmlSummaryChanged(summary); + emit summaryChanged(summary); +} diff --git a/src/GUI/SettingsSectionQML.hxx b/src/GUI/SettingsSectionQML.hxx new file mode 100644 index 000000000..cd9c58767 --- /dev/null +++ b/src/GUI/SettingsSectionQML.hxx @@ -0,0 +1,68 @@ +#ifndef SETTINGSSECTIONQML_HXX +#define SETTINGSSECTIONQML_HXX + +#include "settingssection.h" + +#include + +class SettingsControl; + +class SettingsSectionQML : public SettingsSection +{ + Q_OBJECT + + + Q_PROPERTY(QQmlListProperty controls READ qmlControls) + Q_PROPERTY(QString summary READ summary WRITE setSummary NOTIFY qmlSummaryChanged) + + Q_CLASSINFO("DefaultProperty", "controls") +public: + SettingsSectionQML(); + + QQmlListProperty qmlControls(); + + QList controls() const; + + void saveState(QSettings& settings) const override; + + void restoreState(QSettings& settings) override; + + void doApply() override; + + virtual QString summary() const override; + + Q_INVOKABLE void saveSetting(QString key, QVariant value); + Q_INVOKABLE QVariant restoreSetting(QString key); +public slots: + void setSummary(QString summary); + +signals: + void controlsChanged(); + + /** + * @brief apply - change the launch configuration according to the values + * in this settings section. + */ + void apply(); + + void save(); + + void restore(); + + void qmlSummaryChanged(QString summary); + +private: + + static void controls_append( QQmlListProperty* prop, + QObject* item ); + static void controls_clear( QQmlListProperty* prop ); + static int controls_count( QQmlListProperty* prop ); + static QObject* control_at( QQmlListProperty* prop, int index ); + + void internalUpdateAdvanced() override; + void updateShowAdvanced() override; + QString m_summary; + QObjectList m_controls; +}; + +#endif // SETTINGSSECTIONQML_HXX diff --git a/src/GUI/SettingsWidgets.cxx b/src/GUI/SettingsWidgets.cxx new file mode 100644 index 000000000..47ce6bc7c --- /dev/null +++ b/src/GUI/SettingsWidgets.cxx @@ -0,0 +1,627 @@ +#include "SettingsWidgets.hxx" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "LaunchConfig.hxx" + +SettingsCheckbox::SettingsCheckbox(QWidget* parent) : + SettingsControl(parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + setLayout(vbox); + m_check = new QCheckBox(this); + vbox->addWidget(m_check); + createDescription(); + vbox->addWidget(m_description); + connect(m_check, &QCheckBox::toggled, this, &SettingsCheckbox::checkedChanged); +} + +QString SettingsCheckbox::label() const +{ + return m_check->text(); +} + +bool SettingsCheckbox::isChecked() const +{ + return m_check->isChecked(); +} + +void SettingsCheckbox::setChecked(bool checked) +{ + if (checked == isChecked()) { + return; + } + + m_check->setChecked(checked); + emit checkedChanged(checked); +} + +void SettingsCheckbox::setLabel(QString label) +{ + m_check->setText(label); +} + +void SettingsCheckbox::apply(LaunchConfig* lconfig) const +{ + if (option().isEmpty()) { + return; + } + + lconfig->setEnableDisableOption(option(), isChecked()); +} + +void SettingsCheckbox::saveState(QSettings &settings) const +{ + settings.setValue(qmlName(), isChecked()); +} + +void SettingsCheckbox::restoreState(QSettings &settings) +{ + setChecked(settings.value(qmlName(), isChecked()).toBool()); +} + +QString SettingsControl::label() const +{ + return QString(); +} + +QString SettingsControl::description() const +{ + if (m_description) { + return m_description->text(); + } + + return QString(); +} + +QStringList SettingsControl::keywords() const +{ + return m_keywords; +} + +QString SettingsControl::option() const +{ + return m_option; +} + +void SettingsControl::setAdvanced(bool advanced) +{ + if (m_advanced == advanced) + return; + + m_advanced = advanced; + emit advancedChanged(advanced); +} + +void SettingsControl::setDescription(QString desc) +{ + if (m_description) { + m_description->setText(desc); + } + emit descriptionChanged(); +} + +void SettingsControl::apply(LaunchConfig *lconfig) const +{ + Q_UNUSED(lconfig) +} + +void SettingsControl::setKeywords(QStringList keywords) +{ + if (m_keywords == keywords) + return; + + m_keywords = keywords; + emit keywordsChanged(keywords); +} + +void SettingsControl::setOption(QString option) +{ + if (m_option == option) + return; + + m_option = option; + emit optionChanged(option); +} + +SettingsControl::SettingsControl(QWidget *pr) : + QWidget(pr) +{ +} + +void SettingsControl::createDescription() +{ + m_description = new QLabel(this); + m_description->setWordWrap(true); + QFont f = m_description->font(); + f.setPointSize(f.pointSize() - 2); + m_description->setFont(f); +} + +QString SettingsControl::qmlName() const +{ + QQmlContext* context = QQmlEngine::contextForObject(this); + QString s = context->nameForObject(const_cast(this)); + return s; +} + +SettingsComboBox::SettingsComboBox(QWidget *pr) : + SettingsControl(pr) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + setLayout(vbox); + + QHBoxLayout* hbox = new QHBoxLayout; + vbox->addLayout(hbox); + m_combo = new QComboBox(this); + + m_label = new QLabel(this); + hbox->addWidget(m_label); + hbox->addWidget(m_combo); + hbox->addStretch(1); + + createDescription(); + vbox->addWidget(m_description); + + connect(m_combo, SIGNAL(currentIndexChanged(int)), + this, SIGNAL(selectedIndexChanged(int))); +} + +QStringList SettingsComboBox::choices() const +{ + QStringList result; + for (int i=0; i < m_combo->count(); ++i) { + result.append(m_combo->itemText(i)); + } + return result; +} + +int SettingsComboBox::selectedIndex() const +{ + return m_combo->currentIndex(); +} + +void SettingsComboBox::saveState(QSettings &settings) const +{ + // if selected index is custom, need to save something else? + if (selectedIndex() == m_defaultIndex) { + settings.remove(qmlName()); + } else { + settings.setValue(qmlName(), selectedIndex()); + } +} + +void SettingsComboBox::restoreState(QSettings &settings) +{ + QString id = qmlName(); + setSelectedIndex(settings.value(id, m_defaultIndex).toInt()); +} + +void SettingsComboBox::setChoices(QStringList aChoices) +{ + if (choices() == aChoices) + return; + + m_combo->clear(); + Q_FOREACH (QString choice, aChoices) { + m_combo->addItem(choice); + } + + emit choicesChanged(aChoices); +} + +void SettingsComboBox::setSelectedIndex(int selectedIndex) +{ + if (m_combo->currentIndex() == selectedIndex) + return; + + m_combo->setCurrentIndex(selectedIndex); + emit selectedIndexChanged(selectedIndex); +} + +void SettingsComboBox::setLabel(QString label) +{ + m_label->setText(label); + emit labelChanged(); +} + + +QAbstractItemModel *SettingsComboBox::model() const +{ + return m_combo->model(); +} + +int SettingsComboBox::defaultIndex() const +{ + return m_defaultIndex; +} + +void SettingsComboBox::setModel(QAbstractItemModel *model) +{ + if (model == m_combo->model()) { + return; + } + + m_combo->setModel(model); + emit modelChanged(model); +} + +void SettingsComboBox::setDefaultIndex(int defaultIndex) +{ + if (m_defaultIndex == defaultIndex) + return; + + m_defaultIndex = defaultIndex; + emit defaultIndexChanged(defaultIndex); +} + +///////////////////////////////////////////////////////////////////////////////// + +SettingsIntSpinbox::SettingsIntSpinbox(QWidget *pr) : + SettingsControl(pr) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + setLayout(vbox); + + QHBoxLayout* hbox = new QHBoxLayout; + vbox->addLayout(hbox); + m_spin = new QSpinBox(this); + + m_label = new QLabel(this); + hbox->addWidget(m_label); + hbox->addWidget(m_spin); + hbox->addStretch(1); + + createDescription(); + vbox->addWidget(m_description); + + connect(m_spin, SIGNAL(valueChanged(int)), this, SIGNAL(valueChanged(int))); +} + +int SettingsIntSpinbox::value() const +{ + return m_spin->value(); +} + +int SettingsIntSpinbox::min() const +{ + return m_spin->minimum(); +} + +int SettingsIntSpinbox::max() const +{ + return m_spin->maximum(); +} + +void SettingsIntSpinbox::apply(LaunchConfig *lconfig) const +{ + if (option().isEmpty()) { + return; + } + + lconfig->setArg(option(), QString::number(value())); +} + +void SettingsIntSpinbox::saveState(QSettings &settings) const +{ + settings.setValue(qmlName(), value()); +} + +void SettingsIntSpinbox::restoreState(QSettings &settings) +{ + setValue(settings.value(qmlName(), value()).toInt()); +} + +void SettingsIntSpinbox::setValue(int aValue) +{ + if (value() == aValue) + return; + + m_spin->setValue(aValue); + emit valueChanged(aValue); +} + +void SettingsIntSpinbox::setMin(int aMin) +{ + if (min() == aMin) + return; + + m_spin->setMinimum(aMin); + emit minChanged(aMin); +} + +void SettingsIntSpinbox::setMax(int aMax) +{ + if (max() == aMax) + return; + + m_spin->setMaximum(aMax); + emit maxChanged(aMax); +} + +void SettingsIntSpinbox::setLabel(QString label) +{ + m_label->setText(label); + emit labelChanged(); +} + +SettingsText::SettingsText(QWidget *pr) : + SettingsControl(pr) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + setLayout(vbox); + + QHBoxLayout* hbox = new QHBoxLayout; + vbox->addLayout(hbox); + m_edit = new QLineEdit(this); + + m_label = new QLabel(this); + hbox->addWidget(m_label); + hbox->addWidget(m_edit, 1); + // hbox->addStretch(1); + + createDescription(); + vbox->addWidget(m_description); + + connect(m_edit, &QLineEdit::textChanged, this, &SettingsText::valueChanged); +} + +QString SettingsText::value() const +{ + return m_edit->text(); +} + +void SettingsText::apply(LaunchConfig *lconfig) const +{ + if (option().isEmpty()) { + return; + } + + lconfig->setArg(option(), value()); +} + +void SettingsText::saveState(QSettings &settings) const +{ + settings.setValue(qmlName(), value()); +} + +void SettingsText::restoreState(QSettings &settings) +{ + setValue(settings.value(qmlName(), value()).toString()); +} + +void SettingsText::setLabel(QString label) +{ + m_label->setText(label); +} + +void SettingsText::setValue(QString newVal) +{ + if (value() == newVal) + return; + + m_edit->setText(newVal); + emit valueChanged(newVal); +} + +QString SettingsText::placeholder() const +{ +#if QT_VERSION >= 0x050300 + return m_edit->placeholderText(); +#else + return QString(); +#endif +} + +void SettingsText::setPlaceholder(QString hold) +{ + if (placeholder() == hold) + return; + +#if QT_VERSION >= 0x050300 + // don't require Qt 5.3 + m_edit->setPlaceholderText(hold); +#endif + emit placeholderChanged(hold); +} + +SettingsPath::SettingsPath(QWidget *pr) : + SettingsControl(pr) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + setLayout(vbox); + + QHBoxLayout* hbox = new QHBoxLayout; + vbox->addLayout(hbox); + + m_changeButton = new QPushButton(tr("Change"), this); + connect(m_changeButton, &QPushButton::clicked, this, &SettingsPath::choosePath); + + m_defaultButton = new QPushButton(tr("Default"), this); + connect(m_defaultButton, &QPushButton::clicked, this, &SettingsPath::restoreDefaultPath); + + m_label = new QLabel(this); + hbox->addWidget(m_label, 1); + hbox->addWidget(m_changeButton); + hbox->addWidget(m_defaultButton); + + createDescription(); + vbox->addWidget(m_description); +} + +QString SettingsPath::path() const +{ + return m_path; +} + +void SettingsPath::setPath(QString path) +{ + if (m_path == path) + return; + + m_path = path; + emit pathChanged(path); + updateLabel(); +} + +void SettingsPath::setLabel(QString label) +{ + m_labelPrefix = label; + updateLabel(); +} + +void SettingsPath::apply(LaunchConfig *lconfig) const +{ + if (m_option.isEmpty()) { + return; + } + + if (m_path.isEmpty()) { + return; + } + + lconfig->setArg(option(), path()); +} + +void SettingsPath::setDefaultPath(QString path) +{ + if (m_defaultPath == path) + return; + + m_defaultPath = path; + m_defaultButton->setVisible(!m_defaultPath.isEmpty()); + emit defaultPathChanged(path); + updateLabel(); +} + +void SettingsPath::setChooseDirectory(bool chooseDirectory) +{ + if (m_chooseDirectory == chooseDirectory) + return; + + m_chooseDirectory = chooseDirectory; + emit chooseDirectoryChanged(chooseDirectory); +} + +void SettingsPath::choosePath() +{ + QString path; + if (m_chooseDirectory) { + path = QFileDialog::getExistingDirectory(this, + m_dialogPrompt, + m_path); + } else { + // if we're going to use this, add filter support + path = QFileDialog::getOpenFileName(this, m_dialogPrompt, m_path); + } + + if (path.isEmpty()) { + return; // user cancelled + } + setPath(path); +} + +void SettingsPath::restoreDefaultPath() +{ + setPath(QString()); +} + +void SettingsPath::setDialogPrompt(QString dialogPrompt) +{ + if (m_dialogPrompt == dialogPrompt) + return; + + m_dialogPrompt = dialogPrompt; + emit dialogPromptChanged(dialogPrompt); +} + +void SettingsPath::setOption(QString option) +{ + if (m_option == option) + return; + + m_option = option; + emit optionChanged(option); +} + +void SettingsPath::updateLabel() +{ + const bool isDefault = (m_path.isEmpty()); + QString s = isDefault ? tr("%1: %2 (default)") : tr("%1: %2"); + QString path = isDefault ? defaultPath() : m_path; + m_label->setText(s.arg(m_labelPrefix).arg(path)); + m_defaultButton->setEnabled(!isDefault); +} + +void SettingsPath::saveState(QSettings &settings) const +{ + settings.setValue(qmlName(), m_path); +} + +void SettingsPath::restoreState(QSettings &settings) +{ + QString s = settings.value(qmlName(), QString()).toString(); + setPath(s); +} + +SettingsDateTime::SettingsDateTime(QWidget *pr) : + SettingsControl(pr) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + setLayout(vbox); + + QHBoxLayout* hbox = new QHBoxLayout; + vbox->addLayout(hbox); + + m_edit = new QDateTimeEdit; + m_label = new QLabel(this); + hbox->addWidget(m_label); + hbox->addWidget(m_edit); + hbox->addStretch(1); + + createDescription(); + vbox->addWidget(m_description); +} + +void SettingsDateTime::saveState(QSettings &settings) const +{ + settings.setValue(qmlName(), value()); +} + +void SettingsDateTime::restoreState(QSettings &settings) +{ + m_edit->setDateTime(settings.value(qmlName()).toDateTime()); +} + +void SettingsDateTime::setLabel(QString label) +{ + m_label->setText(label); +} + +QDateTime SettingsDateTime::value() const +{ + return m_edit->dateTime(); +} + +void SettingsDateTime::setValue(QDateTime value) +{ + if (m_edit->dateTime() == value) { + + } + + emit valueChanged(value); +} + diff --git a/src/GUI/SettingsWidgets.hxx b/src/GUI/SettingsWidgets.hxx new file mode 100644 index 000000000..dd7876b86 --- /dev/null +++ b/src/GUI/SettingsWidgets.hxx @@ -0,0 +1,352 @@ +#ifndef SETTINGSWIDGETS_HXX +#define SETTINGSWIDGETS_HXX + +#include +#include + +class QCheckBox; +class QComboBox; +class QSpinBox; +class QLineEdit; +class QLabel; +class QSettings; +class LaunchConfig; +class QPushButton; +class QAbstractItemModel; +class QDateTimeEdit; + +class SettingsControl : public QWidget +{ + Q_OBJECT + + Q_PROPERTY(bool advanced READ advanced WRITE setAdvanced NOTIFY advancedChanged) + Q_PROPERTY(QString label READ label WRITE setLabel NOTIFY labelChanged) + Q_PROPERTY(QString description READ description WRITE setDescription NOTIFY descriptionChanged) + Q_PROPERTY(QStringList keywords READ keywords WRITE setKeywords NOTIFY keywordsChanged) + Q_PROPERTY(QString option READ option WRITE setOption NOTIFY optionChanged) +public: + + bool advanced() const + { + return m_advanced; + } + + virtual QString label() const; + virtual QString description() const; + + QStringList keywords() const; + + QString option() const; + + virtual void saveState(QSettings& settings) const = 0; + + virtual void restoreState(QSettings& settings) = 0; + + +public slots: + void setAdvanced(bool advanced); + + virtual void setDescription(QString desc); + virtual void setLabel(QString label) = 0; + + virtual void apply(LaunchConfig* lconfig) const; + + void setKeywords(QStringList keywords); + + void setOption(QString option); + +signals: + void advancedChanged(bool advanced); + void labelChanged(); + void descriptionChanged(); + void keywordsChanged(QStringList keywords); + void optionChanged(QString option); + +protected: + SettingsControl(QWidget* pr = nullptr); + + void createDescription(); + + QString qmlName() const; + + QLabel* m_description = nullptr; +private: + bool m_advanced = false; + QStringList m_keywords; + QString m_option; +}; + +class SettingsCheckbox : public SettingsControl +{ + Q_OBJECT + + Q_PROPERTY(bool checked READ isChecked WRITE setChecked NOTIFY checkedChanged) + + bool isChecked() const; + +public: + SettingsCheckbox(QWidget* pr = nullptr); + + virtual QString label() const override; + + virtual void setLabel(QString label) override; + + virtual void apply(LaunchConfig* lconfig) const override; + + virtual void saveState(QSettings& settings) const override; + + virtual void restoreState(QSettings& settings) override; +public slots: + void setChecked(bool checked); + +signals: + void checkedChanged(bool checked); + +private: + QCheckBox* m_check; +}; + +class SettingsComboBox : public SettingsControl +{ + Q_OBJECT + + Q_PROPERTY(int selectedIndex READ selectedIndex WRITE setSelectedIndex NOTIFY selectedIndexChanged) + Q_PROPERTY(QStringList choices READ choices WRITE setChoices NOTIFY choicesChanged) + Q_PROPERTY(QAbstractItemModel* model READ model WRITE setModel NOTIFY modelChanged) + Q_PROPERTY(int defaultIndex READ defaultIndex WRITE setDefaultIndex NOTIFY defaultIndexChanged) +public: + SettingsComboBox(QWidget* pr = nullptr); + + QStringList choices() const; + + int selectedIndex() const; + + virtual void saveState(QSettings& settings) const override; + + virtual void restoreState(QSettings& settings) override; + + QAbstractItemModel* model() const; + + int defaultIndex() const; + +public slots: + void setChoices(QStringList choices); + + void setSelectedIndex(int selectedIndex); + + void setModel(QAbstractItemModel* model); + + void setDefaultIndex(int defaultIndex); + +signals: + void choicesChanged(QStringList choices); + + void selectedIndexChanged(int selectedIndex); + + void modelChanged(QAbstractItemModel* model); + + void defaultIndexChanged(int defaultIndex); + +protected: + virtual void setLabel(QString label) override; + +private: + QLabel* m_label; + QComboBox* m_combo; + QStringList m_choices; + int m_selectedIndex = 0; + int m_defaultIndex = -1; +}; + +class SettingsIntSpinbox : public SettingsControl +{ + Q_OBJECT + + Q_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged) + Q_PROPERTY(int min READ min WRITE setMin NOTIFY minChanged) + Q_PROPERTY(int max READ max WRITE setMax NOTIFY maxChanged) + + // suffix / wrap information + +public: + SettingsIntSpinbox(QWidget* pr = nullptr); + + int value() const; + int min() const; + int max() const; + + virtual void apply(LaunchConfig* lconfig) const override; + + virtual void saveState(QSettings& settings) const override; + + virtual void restoreState(QSettings& settings) override; +public slots: + void setValue(int value); + void setMin(int min); + void setMax(int max); + +signals: + void valueChanged(int value); + void minChanged(int min); + void maxChanged(int max); + +protected: + virtual void setLabel(QString label) override; + +private: + QSpinBox* m_spin; + QLabel* m_label; + int m_value; + int m_min; + int m_max; +}; + +class SettingsText : public SettingsControl +{ + Q_OBJECT + + Q_PROPERTY(QString value READ value WRITE setValue NOTIFY valueChanged) + Q_PROPERTY(QString placeholder READ placeholder WRITE setPlaceholder NOTIFY placeholderChanged) +public: + SettingsText(QWidget *pr = nullptr); + + QString value() const; + + virtual void apply(LaunchConfig* lconfig) const override; + + virtual void saveState(QSettings& settings) const override; + + virtual void restoreState(QSettings& settings) override; + + virtual void setLabel(QString label) override; + + QString placeholder() const; + +public slots: + void setValue(QString value); + + void setPlaceholder(QString placeholder); + +signals: + void valueChanged(QString value); + + void placeholderChanged(QString placeholder); + +private: + QLineEdit* m_edit; + QLabel* m_label; +}; + +class SettingsPath : public SettingsControl +{ + Q_OBJECT + + Q_PROPERTY(QString path READ path WRITE setPath NOTIFY pathChanged) + Q_PROPERTY(QString defaultPath READ defaultPath WRITE setDefaultPath + NOTIFY defaultPathChanged) + Q_PROPERTY(bool chooseDirectory READ chooseDirectory + WRITE setChooseDirectory NOTIFY chooseDirectoryChanged) + Q_PROPERTY(QString dialogPrompt READ dialogPrompt + WRITE setDialogPrompt NOTIFY dialogPromptChanged) + Q_PROPERTY(QString option READ option WRITE setOption NOTIFY optionChanged) + + +public: + SettingsPath(QWidget *pr = nullptr); + + QString path() const; + + virtual void saveState(QSettings& settings) const override; + + virtual void restoreState(QSettings& settings) override; + + virtual void setLabel(QString label) override; + + virtual void apply(LaunchConfig* lconfig) const override; + + QString defaultPath() const + { + return m_defaultPath; + } + + bool chooseDirectory() const + { + return m_chooseDirectory; + } + + QString dialogPrompt() const + { + return m_dialogPrompt; + } + + QString option() const + { + return m_option; + } + +public slots: + void setPath(QString path); + void setDefaultPath(QString path); + + void setChooseDirectory(bool chooseDirectory); + + void choosePath(); + void restoreDefaultPath(); + void setDialogPrompt(QString dialogPrompt); + + void setOption(QString option); + +signals: + void defaultPathChanged(QString defaultPath); + + void pathChanged(QString path); + + void chooseDirectoryChanged(bool chooseDirectory); + + void dialogPromptChanged(QString dialogPrompt); + + void optionChanged(QString option); + +private: + void updateLabel(); + + QPushButton* m_changeButton; + QPushButton* m_defaultButton; + QString m_path; + QString m_defaultPath; + QLabel* m_label; + QString m_labelPrefix; + bool m_chooseDirectory = false; + QString m_dialogPrompt; + QString m_option; +}; + +class SettingsDateTime : public SettingsControl +{ + Q_OBJECT + + Q_PROPERTY(QDateTime value READ value WRITE setValue NOTIFY valueChanged) +public: + SettingsDateTime(QWidget *pr = nullptr); + + virtual void saveState(QSettings& settings) const override; + virtual void restoreState(QSettings& settings) override; + + virtual void setLabel(QString label) override; + + QDateTime value() const; + +public slots: + + void setValue(QDateTime value); + +signals: + void valueChanged(QDateTime value); + +private: + QDateTimeEdit* m_edit; + QLabel* m_label; + QDateTime m_value; +}; + +#endif // SETTINGSWIDGETS_HXX diff --git a/src/GUI/TimeSettings.qml b/src/GUI/TimeSettings.qml new file mode 100644 index 000000000..4a9fdcd08 --- /dev/null +++ b/src/GUI/TimeSettings.qml @@ -0,0 +1,72 @@ +import FlightGear.Launcher 1.0 + +Section { + id: timeSettings + title: "Time & Date" + + Combo { + id: timeOfDay + label: "Time of day" + description: "Select the time of day used when the simulator starts, or enter a " + + "custom date and time." + choices: ["Current time", "Dawn", "Morning", "Noon", "Afternoon", + "Dusk", "Evening", "Midnight", "Custom time & date"] + defaultIndex: 0 + + readonly property var args: ["", "dawn", "morning", "noon", "afternoon", + "dusk", "evening", "midnight"] + + readonly property bool isCustom: (selectedIndex == 8) + readonly property bool isDefault: (selectedIndex == 0) + + function summary() + { + if (!timeOfDay.isCustom && !timeOfDay.isDefault) { + return choices[selectedIndex].toLowerCase() + ";"; + } + + return ""; + } + } + + DateTime { + id: customTime + label: "Enter custom time & date" + visible: timeOfDay.isCustom +// description: "Enter a date and time." + } + + Checkbox { + id: customTimeIsGMT + label: "Custom time is GMT / UTC" + visible: timeOfDay.isCustom + } + + Combo { + id: season + label: "Season" + description: "Select if normal (summer) or winter textures are used for the scenery. " + + "This does not affect other aspects of the simulation at present." + keywords: ["season", "scenery", "texture", "winter"] + choices: ["Summer (default)", "Winter"] + defaultIndex: 0 + readonly property var args: ["summer", "winter"] + } + + onApply: { + if (timeOfDay.isCustom) { + var timeString = Qt.formatDateTime(customTime.value, "yyyy:MM:dd:hh:mm:ss"); + if (customTimeIsGMT.checked) { + _config.setArg("start-date-gmt", timeString) + } else { + _config.setArg("start-date-sys", timeString) + } + } else if (timeOfDay.selectedIndex > 0) { + _config.setArg("timeofday", timeOfDay.args[timeOfDay.selectedIndex]) + } + + _config.setArg("season", season.args[season.selectedIndex]) + } + + summary: timeOfDay.summary() +} diff --git a/src/GUI/ToolboxButton.h b/src/GUI/ToolboxButton.h index 6946b710c..8aa522137 100644 --- a/src/GUI/ToolboxButton.h +++ b/src/GUI/ToolboxButton.h @@ -5,6 +5,7 @@ class ToolboxButton : public QAbstractButton { + Q_OBJECT public: ToolboxButton(QWidget* pr = nullptr); diff --git a/src/GUI/ViewCommandLinePage.cxx b/src/GUI/ViewCommandLinePage.cxx new file mode 100644 index 000000000..8597eea79 --- /dev/null +++ b/src/GUI/ViewCommandLinePage.cxx @@ -0,0 +1,73 @@ +#include "ViewCommandLinePage.hxx" + +#include +#include + +#include
+ +#include "LaunchConfig.hxx" + +#if 0 +#include "ExtraSettingsSection.hxx" +#include "LauncherArgumentTokenizer.hxx" +#endif + +ViewCommandLinePage::ViewCommandLinePage(QWidget *parent) : QWidget(parent) +{ + QVBoxLayout* vbox = new QVBoxLayout(this); + setLayout(vbox); + m_browser = new QTextEdit(this); + m_browser->setReadOnly(true); + vbox->addWidget(m_browser); +} + +#if 0 +void ViewCommandLinePage::setExtraSettingsSection(ExtraSettingsSection *ess) +{ + m_extraSettings = ess; +} +#endif + +void ViewCommandLinePage::setLaunchConfig(LaunchConfig *config) +{ + m_config = config; +} + +void ViewCommandLinePage::update() +{ + QString html; + string_list commandLineOpts = flightgear::Options::sharedInstance()->extractOptions(); + if (!commandLineOpts.empty()) { + html += "

Options passed on the command line:

\n"; + html += "
    \n"; + for (auto opt : commandLineOpts) { + html += QString("
  • --") + QString::fromStdString(opt) + "
  • \n"; + } + html += "
\n"; + } +#if 0 + if (m_extraSettings) { + LauncherArgumentTokenizer tk; + Q_FOREACH(auto arg, tk.tokenize(m_extraSettings->argsText())) { + // m_config->setArg(arg.arg, arg.value); + } + } +#endif + m_config->reset(); + m_config->collect(); + + html += "

Options set in the launcher:

\n"; + html += "
    \n"; + for (auto arg : m_config->values()) { + if (arg.value.isEmpty()) { + html += QString("
  • --") + arg.arg + "
  • \n"; + } else if (arg.arg == "prop") { + html += QString("
  • --") + arg.arg + ":" + arg.value + "
  • \n"; + } else { + html += QString("
  • --") + arg.arg + "=" + arg.value + "
  • \n"; + } + } + html += "
\n"; + + m_browser->setHtml(html); +} diff --git a/src/GUI/ViewCommandLinePage.hxx b/src/GUI/ViewCommandLinePage.hxx new file mode 100644 index 000000000..e52625891 --- /dev/null +++ b/src/GUI/ViewCommandLinePage.hxx @@ -0,0 +1,27 @@ +#ifndef VIEWCOMMANDLINEPAGE_HXX +#define VIEWCOMMANDLINEPAGE_HXX + +#include + +class QTextEdit; +class LaunchConfig; + +class ViewCommandLinePage : public QWidget +{ + Q_OBJECT +public: + explicit ViewCommandLinePage(QWidget *parent = 0); + + void setLaunchConfig(LaunchConfig* config); + + void update(); +signals: + +public slots: + +private: + QTextEdit* m_browser; + LaunchConfig* m_config = nullptr; +}; + +#endif // VIEWCOMMANDLINEPAGE_HXX diff --git a/src/GUI/ViewSettings.cpp b/src/GUI/ViewSettings.cpp deleted file mode 100644 index 6ea6d8843..000000000 --- a/src/GUI/ViewSettings.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "ViewSettings.h" -#include "ui_ViewSettings.h" - -ViewSettings::ViewSettings(QWidget *parent) : - SettingsSection(parent), - ui(new Ui::ViewSettings) -{ - ui->setupUi(this); - insertSettingsHeader(); -} - -ViewSettings::~ViewSettings() -{ - delete ui; -} diff --git a/src/GUI/ViewSettings.h b/src/GUI/ViewSettings.h deleted file mode 100644 index f1f64e7f9..000000000 --- a/src/GUI/ViewSettings.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef VIEWSETTINGS_H -#define VIEWSETTINGS_H - -#include - -namespace Ui { -class ViewSettings; -} - -class ViewSettings : public SettingsSection -{ - Q_OBJECT - -public: - explicit ViewSettings(QWidget *parent = 0); - ~ViewSettings(); - -private: - Ui::ViewSettings *ui; -}; - -#endif // VIEWSETTINGS_H diff --git a/src/GUI/ViewSettings.qml b/src/GUI/ViewSettings.qml new file mode 100644 index 000000000..0945c3a80 --- /dev/null +++ b/src/GUI/ViewSettings.qml @@ -0,0 +1,33 @@ +import FlightGear.Launcher 1.0 + +Section { + id: viewSettings + title: "View & Window" + + Checkbox { + id: fullscreen + label: "Start full-screen" + description: "Start the simulator in full-screen mode" + keywords: ["window", "full", "screen"] + option: "fullscreen" + } + + Combo { + id: windowSize + enabled: !fullscreen.checked + label: "Window size" + description: "Select the initial size of the window. (This has no effct in full-screen mode)" + advanced: true + choices: ["640x480", "800x600", "1024x768", "1920x1080", "2560x1600" ] + defaultIndex: 2 + readonly property bool isDefault: selectedIndex == defaultIndex + } + + onApply: { + if (!windowSize.isDefault) { + _config.setArg("geometry", windowSize.choices[windowSize.selectedIndex]); + } + } + + summary: fullscreen.checked ? "full-screen;" : "" +} diff --git a/src/GUI/ViewSettings.ui b/src/GUI/ViewSettings.ui deleted file mode 100644 index 6e732fba6..000000000 --- a/src/GUI/ViewSettings.ui +++ /dev/null @@ -1,157 +0,0 @@ - - - ViewSettings - - - - 0 - 0 - 740 - 338 - - - - Form - - - View - - - - - - Start full-screen - - - - - - - Full-screen mode can be toggled inside the simulator by pressing F10. - - - true - - - true - - - - - - - - - Window size: - - - true - - - - - - - true - - - - 800 x 600 - - - - - 1024 x 768 - - - - - Custom size... - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Field-of-view angle: - - - true - - - - - - - 180 - - - 90 - - - true - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Field-of-view depends on your monitor size. It can be adjusted in the simulator using the x/X keys - - - true - - - true - - - true - - - - - - - - SettingsSection - QWidget -
GUI/settingssection.h
- 1 -
-
- - -
diff --git a/src/GUI/Weather.qml b/src/GUI/Weather.qml new file mode 100644 index 000000000..7cdf50032 --- /dev/null +++ b/src/GUI/Weather.qml @@ -0,0 +1,44 @@ +import FlightGear.Launcher 1.0 + +Section { + id: weatherSettings + title: "Weather" + + Checkbox { + id: fetchMetar + label: "Real-world weather" + description: "Download real-world weather from the NOAA servers based on location." + option: "real-weather-fetch" + } + + Combo { + id: weatherScenario + enabled: !fetchMetar.checked + label: "Weather scenario" + model: _weatherScenarios + readonly property bool isCustomMETAR: (selectedIndex == 0); + description: _weatherScenarios.descriptionForItem(selectedIndex) + defaultIndex: 1 + } + + LineEdit { + id: customMETAR + visible: weatherScenario.isCustomMETAR + enabled: !fetchMetar.checked + label: "METAR" + placeholder: "XXXX 012345Z 28035G50KT 250V300 9999 TSRA SCT022CB BKN030 13/09 Q1005" + description: "Enter a custom METAR string" + } + + onApply: { + if (!fetchMetar.checked) { + if (weatherScenario.isCustomMETAR) { + _config.setArg("metar", customMETAR.value) + } else { + _config.setArg("metar", _weatherScenarios.metarForItem(weatherScenario.selectedIndex)) + } + } + } + + summary: fetchMetar.checked ? "real-world weather;" : "" +} diff --git a/src/GUI/renderingsettings.cpp b/src/GUI/renderingsettings.cpp deleted file mode 100644 index 2d00bfd94..000000000 --- a/src/GUI/renderingsettings.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "renderingsettings.h" -#include "ui_renderingsettings.h" - -RenderingSettings::RenderingSettings(QWidget *parent) : - SettingsSection(parent), - ui(new Ui::RenderingSettings) -{ - ui->setupUi(this); - insertSettingsHeader(); -} - -RenderingSettings::~RenderingSettings() -{ - delete ui; -} diff --git a/src/GUI/renderingsettings.h b/src/GUI/renderingsettings.h deleted file mode 100644 index fb16ef323..000000000 --- a/src/GUI/renderingsettings.h +++ /dev/null @@ -1,22 +0,0 @@ -#ifndef RENDERINGSETTINGS_H -#define RENDERINGSETTINGS_H - -#include - -namespace Ui { -class RenderingSettings; -} - -class RenderingSettings : public SettingsSection -{ - Q_OBJECT - -public: - explicit RenderingSettings(QWidget *parent = 0); - ~RenderingSettings(); - -private: - Ui::RenderingSettings *ui; -}; - -#endif // RENDERINGSETTINGS_H diff --git a/src/GUI/renderingsettings.ui b/src/GUI/renderingsettings.ui deleted file mode 100644 index d141c060e..000000000 --- a/src/GUI/renderingsettings.ui +++ /dev/null @@ -1,229 +0,0 @@ - - - RenderingSettings - - - - 0 - 0 - 771 - 331 - - - - Form - - - Rendering - - - - - - - - Renderer: - - - - - - - - ALS - - - - - Rembrandt - - - - - Default - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Lots of information about which renderer is selcted - - - true - - - - - - - Enable multi-sample anti-aliasingg - - - - - - - Multi-sample anti-aliasing (MSAA) reduces the appearance of jagged edges. Depending on your graphics card, this may reduce framerates - - - true - - - true - - - - - - - - - Multi-sampling setting: - - - true - - - - - - - true - - - - 2x - - - - - 4x - - - - - 8x - - - - - 16x - - - - - Custom... - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - - - Renderer threading model: - - - true - - - - - - - true - - - - Automatic - - - - - Thread for culling and drawing - - - - - Separate threads for culling and drawing - - - - - - - - Qt::Horizontal - - - - 40 - 20 - - - - - - - - - - Choose how the rendering code uses multiple CPU cores to get the best performance. Automatic selection is usually recommended, and some settings can lead to problems including crashes. - - - true - - - true - - - true - - - - - - - - SettingsSection - QWidget -
GUI/settingssection.h
- 1 -
-
- - -
diff --git a/src/GUI/resources.qrc b/src/GUI/resources.qrc index a726a7c01..dff1ed61e 100644 --- a/src/GUI/resources.qrc +++ b/src/GUI/resources.qrc @@ -27,10 +27,22 @@ toolbox-location.png toolbox-settings.png toolbox-summary.png + toolbox-addons.png preview-close.png preview-left-arrow.png preview-right-arrow.png + + DownloadSettings.qml + GeneralSettings.qml + MPSettings.qml + RenderSettings.qml + ViewSettings.qml + + + TimeSettings.qml + Weather.qml + diff --git a/src/GUI/settingssection.cpp b/src/GUI/settingssection.cpp index e6b79424d..bb3ee3454 100644 --- a/src/GUI/settingssection.cpp +++ b/src/GUI/settingssection.cpp @@ -6,8 +6,12 @@ #include #include #include +#include +#include #include "AdvancedSettingsButton.h" +#include "SettingsWidgets.hxx" +#include "LaunchConfig.hxx" SettingsSection::SettingsSection(QWidget* pr) : QFrame(pr) @@ -21,6 +25,16 @@ SettingsSection::SettingsSection(QWidget* pr) : QPalette pal = palette(); pal.setColor(QPalette::Normal, QPalette::WindowText, Qt::white); m_titleLabel->setPalette(pal); + + if (!layout()) { + QVBoxLayout* vbox = new QVBoxLayout(this); + setLayout(vbox); + } +} + +void SettingsSection::setLaunchConfig(LaunchConfig* config) +{ + connect(config, &LaunchConfig::collect, this, &SettingsSection::doApply); } void SettingsSection::setShowAdvanced(bool showAdvanced) @@ -76,34 +90,25 @@ void SettingsSection::insertSettingsHeader() m_advancedModeToggle = new AdvancedSettingsButton; connect(m_advancedModeToggle, &QPushButton::toggled, this, &SettingsSection::toggleShowAdvanced); - hbox->addWidget(m_advancedModeToggle); - QFont helpLabelFont; - helpLabelFont.setPointSize(helpLabelFont.pointSize() - 1); - - QPalette pal = palette(); - pal.setColor(QPalette::Normal, QPalette::WindowText, QColor(0x3f, 0x3f, 0x3f)); - - Q_FOREACH(QLabel* w, findChildren()) { - if (w->property("help").toBool()) { - w->setFont(helpLabelFont); - w->setPalette(pal); - } - } - + updateShowAdvanced(); internalUpdateAdvanced(); } void SettingsSection::internalUpdateAdvanced() { - Q_FOREACH(QWidget* w, findChildren()) { - if (w->property("advanced").toBool()) { - w->setVisible(m_showAdvanced); - } - - if (w->property("simple").toBool()) { - w->setVisible(!m_showAdvanced); - } - } +} + + +void SettingsSection::saveState(QSettings &settings) const +{ +} + +void SettingsSection::restoreState(QSettings &settings) +{ +} + +void SettingsSection::updateShowAdvanced() +{ } diff --git a/src/GUI/settingssection.h b/src/GUI/settingssection.h index cbb20ccff..f09eb7c90 100644 --- a/src/GUI/settingssection.h +++ b/src/GUI/settingssection.h @@ -5,6 +5,8 @@ #include class AdvancedSettingsButton; +class QSettings; +class LaunchConfig; class SettingsSection : public QFrame { @@ -17,6 +19,8 @@ class SettingsSection : public QFrame public: SettingsSection(QWidget* pr = nullptr); + virtual void setLaunchConfig(LaunchConfig* config); + bool showAdvanced() const { return m_showAdvanced; @@ -29,19 +33,31 @@ public: void insertSettingsHeader(); + virtual void saveState(QSettings& settings) const; + + virtual void restoreState(QSettings& settings); + + virtual void doApply() = 0; + + virtual QString summary() const = 0; + public slots: void setShowAdvanced(bool showAdvanced); void setTitle(QString title); void toggleShowAdvanced(); + signals: void showAdvancedChanged(bool showAdvanced); void titleChanged(QString title); -private: - void internalUpdateAdvanced(); + void summaryChanged(QString summary); + +protected: + virtual void internalUpdateAdvanced(); + virtual void updateShowAdvanced(); QString m_title; bool m_showAdvanced = false; diff --git a/src/GUI/toolbox-addons.png b/src/GUI/toolbox-addons.png new file mode 100644 index 000000000..e1c1fd54a Binary files /dev/null and b/src/GUI/toolbox-addons.png differ diff --git a/src/Main/options.cxx b/src/Main/options.cxx index 85eedcc84..c62f7baca 100644 --- a/src/Main/options.cxx +++ b/src/Main/options.cxx @@ -2773,10 +2773,31 @@ SGPath Options::platformDefaultRoot() const #else SGPath Options::platformDefaultRoot() const { - return SGPath::fromUtf8(PKGLIBDIR); + return SGPath::fromUtf8(PKGLIBDIR); } + #endif +string_list Options::extractOptions() const +{ + string_list result; + for (auto opt : p->values) { + if (opt.desc == nullptr) { + continue; + } + + if (!strcmp(opt.desc->option,"prop")) { + result.push_back("prop:" + opt.value); + } else if (opt.value.empty()) { + result.push_back(opt.desc->option); + } else { + result.push_back(std::string(opt.desc->option) + "=" + opt.value); + } + } + + return result; +} + void Options::setupRoot(int argc, char **argv) { SGPath root; diff --git a/src/Main/options.hxx b/src/Main/options.hxx index 4ce9fe067..ad8d848bf 100644 --- a/src/Main/options.hxx +++ b/src/Main/options.hxx @@ -156,6 +156,14 @@ public: static bool checkForArg(int argc, char* argv[], const char* arg); SGPath platformDefaultRoot() const; + + /** + * @brief extractOptions - extract the currently set options as + * a string array. This can be used to examine what options were + * requested / set so far. + * @return + */ + string_list extractOptions() const; private: void showUsage() const; void showVersion() const;