1
0
Fork 0

Split launcher code to fix ownership issues

This avoids passing a QmainWindow into the QML engines, which don’t
seem to handle that very well.
This commit is contained in:
James Turner 2018-03-22 12:24:57 +00:00
parent f75f413970
commit 01f840487d
6 changed files with 943 additions and 801 deletions

View file

@ -124,6 +124,8 @@ if (HAVE_QT)
RecentAircraftModel.hxx
RecentLocationsModel.cxx
RecentLocationsModel.hxx
LauncherController.cxx
LauncherController.hxx
${uic_sources}
${qrc_sources}
${qml_sources})

View file

@ -0,0 +1,645 @@
#include "LauncherController.hxx"
// Qt headers
#include <QDebug>
#include <QSettings>
#include <QNetworkAccessManager>
#include <QNetworkDiskCache>
#include <QDesktopServices>
#include <QMessageBox>
#include <QSettings>
#include <QQuickWindow>
// simgear headers
#include <simgear/package/Install.hxx>
#include <simgear/environment/metar.hxx>
#include <simgear/structure/exception.hxx>
// FlightGear headers
#include <Network/HTTPClient.hxx>
#include <Main/globals.hxx>
#include <Airports/airport.hxx>
#include <Main/options.hxx>
#include <Main/fg_init.hxx>
#include <Main/fg_props.hxx>
#include "version.h"
#include "QtLauncher.hxx"
#include "QmlAircraftInfo.hxx"
#include "LauncherArgumentTokenizer.hxx"
#include "PathUrlHelper.hxx"
#include "PopupWindowTracker.hxx"
#include "RecentAircraftModel.hxx"
#include "RecentLocationsModel.hxx"
#include "ThumbnailImageItem.hxx"
#include "PreviewImageItem.hxx"
#include "FlickableExtentQuery.hxx"
#include "MPServersModel.h"
#include "AircraftSearchFilterModel.hxx"
#include "DefaultAircraftLocator.hxx"
#include "LaunchConfig.hxx"
#include "AircraftModel.hxx"
// remove me once location widget is ported to Quick
#include "LocationWidget.hxx"
#include "PathsDialog.hxx"
using namespace simgear::pkg;
LauncherController::LauncherController(QObject *parent,
LocationWidget* loc) :
QObject(parent),
m_locationWidget_FIXME(loc)
{
m_serversModel = new MPServersModel(this);
m_locationHistory = new RecentLocationsModel(this);
m_selectedAircraftInfo = new QmlAircraftInfo(this);
m_config = new LaunchConfig(this);
connect(m_config, &LaunchConfig::collect, this, &LauncherController::collectAircraftArgs);
connect(m_locationWidget_FIXME, &LocationWidget::descriptionChanged,
this, &LauncherController::summaryChanged);
initQML();
m_aircraftModel = new AircraftItemModel(this);
m_installedAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
m_installedAircraftModel->setInstalledFilterEnabled(true);
m_browseAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
m_browseAircraftModel->setRatingFilterEnabled(true);
m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel);
m_aircraftHistory = new RecentAircraftModel(m_aircraftModel, this);
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
this, &LauncherController::onAircraftInstalledCompleted);
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallFailed,
this, &LauncherController::onAircraftInstallFailed);
connect(LocalAircraftCache::instance(),
&LocalAircraftCache::scanCompleted,
this, &LauncherController::updateSelectedAircraft);
QSettings settings;
LocalAircraftCache::instance()->setPaths(settings.value("aircraft-paths").toStringList());
LocalAircraftCache::instance()->scanDirs();
m_aircraftModel->setPackageRoot(globals->packageRoot());
}
void LauncherController::initQML()
{
qmlRegisterType<LauncherArgumentTokenizer>("FlightGear.Launcher", 1, 0, "ArgumentTokenizer");
qmlRegisterUncreatableType<QAbstractItemModel>("FlightGear.Launcher", 1, 0, "QAIM", "no");
qmlRegisterUncreatableType<AircraftProxyModel>("FlightGear.Launcher", 1, 0, "AircraftProxyModel", "no");
qmlRegisterUncreatableType<RecentAircraftModel>("FlightGear.Launcher", 1, 0, "RecentAircraftModel", "no");
qmlRegisterUncreatableType<RecentLocationsModel>("FlightGear.Launcher", 1, 0, "RecentLocationsModel", "no");
qmlRegisterUncreatableType<LaunchConfig>("FlightGear.Launcher", 1, 0, "LaunchConfig", "Singleton API");
qmlRegisterUncreatableType<MPServersModel>("FlightGear.Launcher", 1, 0, "MPServers", "Singleton API");
qmlRegisterType<FileDialogWrapper>("FlightGear.Launcher", 1, 0, "FileDialog");
qmlRegisterType<FlickableExtentQuery>("FlightGear.Launcher", 1, 0, "FlickableExtentQuery");
qmlRegisterType<QmlAircraftInfo>("FlightGear.Launcher", 1, 0, "AircraftInfo");
qmlRegisterType<PopupWindowTracker>("FlightGear.Launcher", 1, 0, "PopupWindowTracker");
qmlRegisterUncreatableType<LocalAircraftCache>("FlightGear.Launcher", 1, 0, "LocalAircraftCache", "Aircraft cache");
qmlRegisterUncreatableType<AircraftItemModel>("FlightGear.Launcher", 1, 0, "AircraftModel", "Built-in model");
qmlRegisterType<ThumbnailImageItem>("FlightGear.Launcher", 1, 0, "ThumbnailImage");
qmlRegisterType<PreviewImageItem>("FlightGear.Launcher", 1, 0, "PreviewImage");
QNetworkDiskCache* diskCache = new QNetworkDiskCache(this);
SGPath cachePath = globals->get_fg_home() / "PreviewsCache";
diskCache->setCacheDirectory(QString::fromStdString(cachePath.utf8Str()));
QNetworkAccessManager* netAccess = new QNetworkAccessManager(this);
netAccess->setCache(diskCache);
PreviewImageItem::setGlobalNetworkAccess(netAccess);
}
void LauncherController::restoreSettings()
{
m_selectedAircraft = m_aircraftHistory->mostRecent();
if (m_selectedAircraft.isEmpty()) {
// select the default aircraft specified in defaults.xml
flightgear::DefaultAircraftLocator da;
if (da.foundPath().exists()) {
m_selectedAircraft = QUrl::fromLocalFile(QString::fromStdString(da.foundPath().utf8Str()));
qDebug() << "Restored default aircraft:" << m_selectedAircraft;
} else {
qWarning() << "Completely failed to find the default aircraft";
}
}
m_locationWidget_FIXME->restoreSettings();
QVariantMap currentLocation = m_locationHistory->mostRecent();
if (currentLocation.isEmpty()) {
// use the default
std::string defaultAirport = flightgear::defaultAirportICAO();
FGAirportRef apt = FGAirport::findByIdent(defaultAirport);
if (apt) {
currentLocation["location-id"] = static_cast<qlonglong>(apt->guid());
currentLocation["location-apt-runway"] = "active";
} // otherwise we failed to find the default airport in the nav-db :(
}
m_locationWidget_FIXME->restoreLocation(currentLocation);
updateSelectedAircraft();
m_serversModel->requestRestore();
emit summaryChanged();
emit showNoOfficialHangarChanged();
}
void LauncherController::saveSettings()
{
emit requestSaveState();
m_aircraftHistory->saveToSettings();
m_locationHistory->saveToSettings();
}
void LauncherController::collectAircraftArgs()
{
// aircraft
if (!m_selectedAircraft.isEmpty()) {
if (m_selectedAircraft.isLocalFile()) {
QFileInfo setFileInfo(m_selectedAircraft.toLocalFile());
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
m_config->setArg("aircraft", setFile);
} else if (m_selectedAircraft.scheme() == "package") {
// no need to set aircraft-dir, handled by the corresponding code
// in fgInitAircraft
m_config->setArg("aircraft", m_selectedAircraft.path());
} else {
qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
}
}
// scenery paths
QSettings settings;
Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) {
m_config->setArg("fg-scenery", path);
}
// aircraft paths
Q_FOREACH(QString path, settings.value("aircraft-paths").toStringList()) {
m_config->setArg("fg-aircraft", path);
}
}
void LauncherController::doRun()
{
flightgear::Options* opt = flightgear::Options::sharedInstance();
m_config->reset();
m_config->collect();
m_aircraftHistory->insert(m_selectedAircraft);
QVariant locSet = m_locationWidget_FIXME->saveLocation();
m_locationHistory->insert(locSet);
// aircraft paths
QSettings settings;
QString downloadDir = settings.value("downloadSettings/downloadDir").toString();
if (!downloadDir.isEmpty()) {
QDir d(downloadDir);
if (!d.exists()) {
int result = QMessageBox::question(nullptr, tr("Create download folder?"),
tr("The selected location for downloads does not exist. (%1) Create it?").arg(downloadDir),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if (result == QMessageBox::Cancel) {
return;
}
if (result == QMessageBox::Yes) {
d.mkpath(downloadDir);
}
}
}
if (settings.contains("restore-defaults-on-run")) {
settings.remove("restore-defaults-on-run");
opt->addOption("restore-defaults", "");
}
m_config->applyToOptions();
saveSettings();
}
void LauncherController::doApply()
{
// aircraft
if (!m_selectedAircraft.isEmpty()) {
std::string aircraftPropValue,
aircraftDir;
if (m_selectedAircraft.isLocalFile()) {
QFileInfo setFileInfo(m_selectedAircraft.toLocalFile());
QString setFile = setFileInfo.fileName();
Q_ASSERT(setFile.endsWith("-set.xml"));
setFile.truncate(setFile.count() - 8); // drop the '-set.xml' portion
aircraftDir = setFileInfo.dir().absolutePath().toStdString();
aircraftPropValue = setFile.toStdString();
} else if (m_selectedAircraft.scheme() == "package") {
// no need to set aircraft-dir, handled by the corresponding code
// in fgInitAircraft
aircraftPropValue = m_selectedAircraft.path().toStdString();
} else {
qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
}
m_aircraftHistory->insert(m_selectedAircraft);
globals->get_props()->setStringValue("/sim/aircraft", aircraftPropValue);
globals->get_props()->setStringValue("/sim/aircraft-dir", aircraftDir);
}
// location
m_locationWidget_FIXME->setLocationProperties();
saveSettings();
}
QString LauncherController::selectAircraftStateAutomatically()
{
if (m_locationWidget_FIXME->isAirborneLocation()) {
return "approach";
}
if (m_locationWidget_FIXME->isParkedLocation()) {
return "parked";
} else {
return "take-off";
}
return {}; // failed to compute, give up
}
void LauncherController::maybeUpdateSelectedAircraft(QModelIndex index)
{
QUrl u = index.data(AircraftURIRole).toUrl();
if (u == m_selectedAircraft) {
// potentially enable the run button now!
updateSelectedAircraft();
}
}
void LauncherController::updateSelectedAircraft()
{
m_selectedAircraftInfo->setUri(m_selectedAircraft);
QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
if (index.isValid()) {
LauncherAircraftType aircraftType = Airplane;
if (index.data(AircraftIsHelicopterRole).toBool()) {
aircraftType = Helicopter;
} else if (index.data(AircraftIsSeaplaneRole).toBool()) {
aircraftType = Seaplane;
}
m_locationWidget_FIXME->setAircraftType(aircraftType);
}
emit canFlyChanged();
}
bool LauncherController::canFly() const
{
QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
if (!index.isValid()) {
return false;
}
int status = index.data(AircraftPackageStatusRole).toInt();
bool canRun = (status == LocalAircraftCache::PackageInstalled);
return canRun;
}
void LauncherController::downloadDirChanged(QString path)
{
// this can get run if the UI is disabled, just bail out before doing
// anything permanent.
if (!m_config->enableDownloadDirUI()) {
return;
}
// if the default dir is passed in, map that back to the emptru string
if (path == m_config->defaultDownloadDir()) {
path.clear();;
}
auto options = flightgear::Options::sharedInstance();
if (options->valueForOption("download-dir") == path.toStdString()) {
// this works because we propogate the value from QSettings to
// the flightgear::Options object in runLauncherDialog()
// so the options object always contains our current idea of this
// value
return;
}
if (!path.isEmpty()) {
options->setOption("download-dir", path.toStdString());
} else {
options->clearOption("download-dir");
}
// replace existing package root
globals->get_subsystem<FGHTTPClient>()->shutdown();
globals->setPackageRoot(simgear::pkg::RootRef());
// create new root with updated download-dir value
fgInitPackageRoot();
globals->get_subsystem<FGHTTPClient>()->init();
QSettings settings;
// re-scan the aircraft list
m_aircraftModel->setPackageRoot(globals->packageRoot());
auto aircraftCache = LocalAircraftCache::instance();
aircraftCache->setPaths(settings.value("aircraft-paths").toStringList());
aircraftCache->scanDirs();
emit showNoOfficialHangarChanged();
// re-set scenery dirs
setSceneryPaths();
}
bool LauncherController::shouldShowOfficialCatalogMessage() const
{
QSettings settings;
bool showOfficialCatalogMesssage = !globals->get_subsystem<FGHTTPClient>()->isDefaultCatalogInstalled();
if (settings.value("hide-official-catalog-message").toBool()) {
showOfficialCatalogMesssage = false;
}
return showOfficialCatalogMesssage;
}
QmlAircraftInfo *LauncherController::selectedAircraftInfo() const
{
return m_selectedAircraftInfo;
}
void LauncherController::restoreLocation(QVariant var)
{
m_locationWidget_FIXME->restoreLocation(var.toMap());
}
QUrl LauncherController::selectedAircraft() const
{
return m_selectedAircraft;
}
bool LauncherController::matchesSearch(QString term, QStringList keywords) const
{
Q_FOREACH(QString s, keywords) {
if (s.contains(term, Qt::CaseInsensitive)) {
return true;
}
}
return false;
}
bool LauncherController::isSearchActive() const
{
return !m_settingsSearchTerm.isEmpty();
}
QStringList LauncherController::settingsSummary() const
{
return m_settingsSummary;
}
QStringList LauncherController::environmentSummary() const
{
return m_environmentSummary;
}
void LauncherController::setSelectedAircraft(QUrl selectedAircraft)
{
if (m_selectedAircraft == selectedAircraft)
return;
m_selectedAircraft = selectedAircraft;
updateSelectedAircraft();
emit selectedAircraftChanged(m_selectedAircraft);
}
void LauncherController::setSettingsSearchTerm(QString settingsSearchTerm)
{
if (m_settingsSearchTerm == settingsSearchTerm)
return;
m_settingsSearchTerm = settingsSearchTerm;
emit searchChanged();
}
void LauncherController::setSettingsSummary(QStringList settingsSummary)
{
if (m_settingsSummary == settingsSummary)
return;
m_settingsSummary = settingsSummary;
emit summaryChanged();
}
void LauncherController::setEnvironmentSummary(QStringList environmentSummary)
{
if (m_environmentSummary == environmentSummary)
return;
m_environmentSummary = environmentSummary;
emit summaryChanged();
}
QStringList LauncherController::combinedSummary() const
{
return m_settingsSummary + m_environmentSummary;
}
QString LauncherController::locationDescription() const
{
return m_locationWidget_FIXME->locationDescription();
}
simgear::pkg::PackageRef LauncherController::packageForAircraftURI(QUrl uri) const
{
if (uri.scheme() != "package") {
qWarning() << "invalid URL scheme:" << uri;
return simgear::pkg::PackageRef();
}
QString ident = uri.path();
return globals->packageRoot()->getPackageById(ident.toStdString());
}
void LauncherController::onAircraftPathsChanged()
{
QSettings settings;
auto aircraftCache = LocalAircraftCache::instance();
aircraftCache->setPaths(settings.value("aircraft-paths").toStringList());
aircraftCache->scanDirs();
}
bool LauncherController::validateMetarString(QString metar)
{
if (metar.isEmpty()) {
return true;
}
try {
std::string s = metar.toStdString();
SGMetar theMetar(s);
} catch (sg_io_exception&) {
return false;
}
return true;
}
void LauncherController::requestInstallUpdate(QUrl aircraftUri)
{
// also select, otherwise UI is confusing
m_selectedAircraft = aircraftUri;
updateSelectedAircraft();
simgear::pkg::PackageRef pref = packageForAircraftURI(aircraftUri);
if (pref) {
if (pref->isInstalled()) {
InstallRef install = pref->existingInstall();
if (install->hasUpdate()) {
globals->packageRoot()->scheduleToUpdate(install);
}
} else {
pref->install();
}
}
}
void LauncherController::requestUninstall(QUrl aircraftUri)
{
simgear::pkg::PackageRef pref = packageForAircraftURI(aircraftUri);
if (pref) {
simgear::pkg::InstallRef i = pref->existingInstall();
if (i) {
i->uninstall();
}
}
}
void LauncherController::requestInstallCancel(QUrl aircraftUri)
{
simgear::pkg::PackageRef pref = packageForAircraftURI(aircraftUri);
if (pref) {
simgear::pkg::InstallRef i = pref->existingInstall();
if (i) {
i->cancelDownload();
}
}
}
void LauncherController::requestUpdateAllAircraft()
{
const PackageList& toBeUpdated = globals->packageRoot()->packagesNeedingUpdate();
std::for_each(toBeUpdated.begin(), toBeUpdated.end(), [](PackageRef pkg) {
globals->packageRoot()->scheduleToUpdate(pkg->install());
});
}
void LauncherController::queryMPServers()
{
m_serversModel->refresh();
}
bool LauncherController::showNoOfficialHanger() const
{
return shouldShowOfficialCatalogMessage();
}
QString LauncherController::versionString() const
{
return FLIGHTGEAR_VERSION;
}
RecentAircraftModel *LauncherController::aircraftHistory()
{
return m_aircraftHistory;
}
RecentLocationsModel *LauncherController::locationHistory()
{
return m_locationHistory;
}
void LauncherController::launchUrl(QUrl url)
{
QDesktopServices::openUrl(url);
}
QVariantList LauncherController::defaultSplashUrls() const
{
QVariantList urls;
for (auto path : flightgear::defaultSplashScreenPaths()) {
QUrl url = QUrl::fromLocalFile(QString::fromStdString(path));
urls.append(url);
}
return urls;
}
void LauncherController::onAircraftInstalledCompleted(QModelIndex index)
{
maybeUpdateSelectedAircraft(index);
}
void LauncherController::onAircraftInstallFailed(QModelIndex index, QString errorMessage)
{
qWarning() << Q_FUNC_INFO << index.data(AircraftURIRole) << errorMessage;
QMessageBox msg;
msg.setWindowTitle(tr("Aircraft installation failed"));
msg.setText(tr("An error occurred installing the aircraft %1: %2").
arg(index.data(Qt::DisplayRole).toString()).arg(errorMessage));
msg.addButton(QMessageBox::Ok);
msg.exec();
maybeUpdateSelectedAircraft(index);
}
void LauncherController::setSceneryPaths()
{
flightgear::launcherSetSceneryPaths();
}
void LauncherController::officialCatalogAction(QString s)
{
if (s == "hide") {
QSettings settings;
settings.setValue("hide-official-catalog-message", true);
} else if (s == "add-official") {
// TEMPROARY, FIXME
AddOnsPage::addDefaultCatalog(nullptr, false /* not silent */);
}
emit showNoOfficialHangarChanged();
}
QPointF LauncherController::mapToGlobal(QQuickItem *item, const QPointF &pos) const
{
QPointF scenePos = item->mapToScene(pos);
QQuickWindow* win = item->window();
return win->mapToGlobal(scenePos.toPoint());
}

View file

@ -0,0 +1,224 @@
// LauncherController.hxx - GUI launcher dialog using Qt5
//
// Written by James Turner, started March 2018.
//
// Copyright (C) 2018 James Turner <james@flightgear.org>
//
// This program is free software; you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of the
// License, or (at your option) any later version.
//
// This program is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#ifndef LAUNCHERCONTROLLER_HXX
#define LAUNCHERCONTROLLER_HXX
#include <QObject>
#include <QUrl>
#include <QModelIndex>
#include <simgear/package/Package.hxx>
#include <simgear/package/Catalog.hxx>
// forward decls
class AircraftProxyModel;
class QmlAircraftInfo;
class RecentAircraftModel;
class RecentLocationsModel;
class MPServersModel;
class AircraftItemModel;
class LocationWidget;
class QQuickItem;
class LaunchConfig;
class LauncherController : public QObject
{
Q_OBJECT
Q_PROPERTY(bool showNoOfficialHanger READ showNoOfficialHanger NOTIFY showNoOfficialHangarChanged)
Q_PROPERTY(AircraftProxyModel* installedAircraftModel MEMBER m_installedAircraftModel CONSTANT)
Q_PROPERTY(AircraftProxyModel* browseAircraftModel MEMBER m_browseAircraftModel CONSTANT)
Q_PROPERTY(AircraftProxyModel* searchAircraftModel MEMBER m_aircraftSearchModel CONSTANT)
Q_PROPERTY(AircraftItemModel* baseAircraftModel MEMBER m_aircraftModel CONSTANT)
Q_PROPERTY(MPServersModel* mpServersModel MEMBER m_serversModel CONSTANT)
Q_PROPERTY(QUrl selectedAircraft READ selectedAircraft WRITE setSelectedAircraft NOTIFY selectedAircraftChanged)
Q_PROPERTY(QmlAircraftInfo* selectedAircraftInfo READ selectedAircraftInfo NOTIFY selectedAircraftChanged)
Q_PROPERTY(bool isSearchActive READ isSearchActive NOTIFY searchChanged)
Q_PROPERTY(QString settingsSearchTerm READ settingsSearchTerm WRITE setSettingsSearchTerm NOTIFY searchChanged)
Q_PROPERTY(QStringList settingsSummary READ settingsSummary WRITE setSettingsSummary NOTIFY summaryChanged)
Q_PROPERTY(QStringList environmentSummary READ environmentSummary WRITE setEnvironmentSummary NOTIFY summaryChanged)
Q_PROPERTY(QString locationDescription READ locationDescription NOTIFY summaryChanged)
Q_PROPERTY(QStringList combinedSummary READ combinedSummary NOTIFY summaryChanged)
Q_PROPERTY(QString versionString READ versionString CONSTANT)
Q_PROPERTY(RecentAircraftModel* aircraftHistory READ aircraftHistory CONSTANT)
Q_PROPERTY(RecentLocationsModel* locationHistory READ locationHistory CONSTANT)
Q_PROPERTY(bool canFly READ canFly NOTIFY canFlyChanged)
public:
explicit LauncherController(QObject *parent,
LocationWidget* loc);
void initQML();
Q_INVOKABLE bool validateMetarString(QString metar);
Q_INVOKABLE void requestInstallUpdate(QUrl aircraftUri);
Q_INVOKABLE void requestUninstall(QUrl aircraftUri);
Q_INVOKABLE void requestInstallCancel(QUrl aircraftUri);
Q_INVOKABLE void downloadDirChanged(QString path);
Q_INVOKABLE void requestUpdateAllAircraft();
Q_INVOKABLE void queryMPServers();
bool showNoOfficialHanger() const;
Q_INVOKABLE void officialCatalogAction(QString s);
QUrl selectedAircraft() const;
// work around the fact, that this is not available on QQuickItem until 5.7
Q_INVOKABLE QPointF mapToGlobal(QQuickItem* item, const QPointF& pos) const;
QmlAircraftInfo* selectedAircraftInfo() const;
Q_INVOKABLE void restoreLocation(QVariant var);
Q_INVOKABLE bool matchesSearch(QString term, QStringList keywords) const;
bool isSearchActive() const;
QString settingsSearchTerm() const
{
return m_settingsSearchTerm;
}
QStringList settingsSummary() const;
QStringList environmentSummary() const;
QStringList combinedSummary() const;
QString locationDescription() const;
QString versionString() const;
RecentAircraftModel* aircraftHistory();
RecentLocationsModel* locationHistory();
Q_INVOKABLE void launchUrl(QUrl url);
// list of QUrls containing the default splash images from FGData.
// used on the summary screen
Q_INVOKABLE QVariantList defaultSplashUrls() const;
Q_INVOKABLE QString selectAircraftStateAutomatically();
LaunchConfig* config() const
{ return m_config; }
void doRun();
void doApply();
bool canFly() const;
void setSceneryPaths();
AircraftItemModel* baseAircraftModel() const
{ return m_aircraftModel; }
void restoreSettings();
void saveSettings();
signals:
void showNoOfficialHangarChanged();
void selectedAircraftChanged(QUrl selectedAircraft);
void searchChanged();
void summaryChanged();
void canFlyChanged();
/**
* @brief requestSaveState - signal to request QML settings to save their
* state to persistent storage
*/
void requestSaveState();
public slots:
void setSelectedAircraft(QUrl selectedAircraft);
void setSettingsSearchTerm(QString settingsSearchTerm);
void setSettingsSummary(QStringList settingsSummary);
void setEnvironmentSummary(QStringList environmentSummary);
void onAircraftPathsChanged();
private slots:
void onAircraftInstalledCompleted(QModelIndex index);
void onAircraftInstallFailed(QModelIndex index, QString errorMessage);
private:
/**
* Check if the passed index is the selected aircraft, and if so, refresh
* the associated UI data
*/
void maybeUpdateSelectedAircraft(QModelIndex index);
void updateSelectedAircraft();
simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const;
// need to wait after a model reset before restoring selection and
// scrolling, to give the view time it seems.
void delayedAircraftModelReset();
bool shouldShowOfficialCatalogMessage() const;
void collectAircraftArgs();
private:
AircraftProxyModel* m_installedAircraftModel;
AircraftItemModel* m_aircraftModel;
AircraftProxyModel* m_aircraftSearchModel;
AircraftProxyModel* m_browseAircraftModel;
MPServersModel* m_serversModel = nullptr;
QUrl m_selectedAircraft;
int m_ratingFilters[4] = {3, 3, 3, 3};
LaunchConfig* m_config = nullptr;
QmlAircraftInfo* m_selectedAircraftInfo = nullptr;
QString m_settingsSearchTerm;
QStringList m_settingsSummary, m_environmentSummary;
RecentAircraftModel* m_aircraftHistory = nullptr;
RecentLocationsModel* m_locationHistory = nullptr;
LocationWidget* m_locationWidget_FIXME = nullptr;
};
#endif // LAUNCHERCONTROLLER_HXX

View file

@ -7,8 +7,7 @@
#include <QMenu>
#include <QMenuBar>
#include <QMenu>
#include <QNetworkAccessManager>
#include <QNetworkDiskCache>
#include <QPushButton>
#include <QStandardItemModel>
#include <QDesktopServices>
@ -35,27 +34,16 @@
// launcher headers
#include "QtLauncher.hxx"
#include "AircraftModel.hxx"
#include "PathsDialog.hxx"
#include "AircraftSearchFilterModel.hxx"
#include "DefaultAircraftLocator.hxx"
#include "LaunchConfig.hxx"
#include "ViewCommandLinePage.hxx"
#include "MPServersModel.h"
#include "ThumbnailImageItem.hxx"
#include "PreviewImageItem.hxx"
#include "FlickableExtentQuery.hxx"
#include "AircraftModel.hxx"
#include "LocalAircraftCache.hxx"
#include "QmlAircraftInfo.hxx"
#include "LauncherArgumentTokenizer.hxx"
#include "PathUrlHelper.hxx"
#include "PopupWindowTracker.hxx"
#include "RecentAircraftModel.hxx"
#include "RecentLocationsModel.hxx"
#include "LauncherController.hxx"
#include "DefaultAircraftLocator.hxx"
#include "ui_Launcher.h"
using namespace simgear::pkg;
extern void restartTheApp(QStringList fgArgs);
@ -78,6 +66,14 @@ LauncherMainWindow::LauncherMainWindow() :
#endif
m_controller = new LauncherController(this, m_ui->location);
m_controller->initQML();
connect(m_controller, &LauncherController::canFlyChanged,
this, &LauncherMainWindow::onCanFlyChanged);
m_ui->location->setLaunchConfig(m_controller->config());
QMenu* toolsMenu = mb->addMenu(tr("Tools"));
QAction* restoreDefaultsAction = toolsMenu->addAction(tr("Restore defaults..."));
connect(restoreDefaultsAction, &QAction::triggered,
@ -91,12 +87,6 @@ LauncherMainWindow::LauncherMainWindow() :
connect(viewCommandLineAction, &QAction::triggered,
this, &LauncherMainWindow::onViewCommandLine);
m_serversModel = new MPServersModel(this);
m_locationHistory = new RecentLocationsModel(this);
m_selectedAircraftInfo = new QmlAircraftInfo(this);
initQML();
m_subsystemIdleTimer = new QTimer(this);
m_subsystemIdleTimer->setInterval(0);
@ -112,64 +102,48 @@ LauncherMainWindow::LauncherMainWindow() :
connect(m_ui->settingsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
connect(m_ui->addOnsButton, &QAbstractButton::clicked, this, &LauncherMainWindow::onClickToolboxButton);
connect(m_ui->location, &LocationWidget::descriptionChanged,
this, &LauncherMainWindow::summaryChanged);
QAction* qa = new QAction(this);
qa->setShortcut(QKeySequence("Ctrl+Q"));
connect(qa, &QAction::triggered, this, &LauncherMainWindow::onQuit);
addAction(qa);
m_aircraftModel = new AircraftItemModel(this);
m_installedAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
m_installedAircraftModel->setInstalledFilterEnabled(true);
m_browseAircraftModel = new AircraftProxyModel(this, m_aircraftModel);
m_browseAircraftModel->setRatingFilterEnabled(true);
m_aircraftSearchModel = new AircraftProxyModel(this, m_aircraftModel);
m_aircraftHistory = new RecentAircraftModel(m_aircraftModel, this);
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
this, &LauncherMainWindow::onAircraftInstalledCompleted);
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallFailed,
this, &LauncherMainWindow::onAircraftInstallFailed);
connect(LocalAircraftCache::instance(),
&LocalAircraftCache::scanCompleted,
this, &LauncherMainWindow::updateSelectedAircraft);
AddOnsPage* addOnsPage = new AddOnsPage(NULL, globals->packageRoot());
connect(addOnsPage, &AddOnsPage::sceneryPathsChanged,
this, &LauncherMainWindow::setSceneryPaths);
m_controller, &LauncherController::setSceneryPaths);
connect(addOnsPage, &AddOnsPage::aircraftPathsChanged,
this, &LauncherMainWindow::onAircraftPathsChanged);
m_controller, &LauncherController::onAircraftPathsChanged);
m_ui->stack->addWidget(addOnsPage);
connect (m_aircraftModel, &AircraftItemModel::catalogsRefreshed,
connect(m_controller->baseAircraftModel(), &AircraftItemModel::catalogsRefreshed,
addOnsPage, &AddOnsPage::onCatalogsRefreshed);
QSettings settings;
LocalAircraftCache::instance()->setPaths(settings.value("aircraft-paths").toStringList());
LocalAircraftCache::instance()->scanDirs();
m_aircraftModel->setPackageRoot(globals->packageRoot());
m_viewCommandLinePage = new ViewCommandLinePage;
m_viewCommandLinePage->setLaunchConfig(m_config);
m_viewCommandLinePage->setLaunchConfig(m_controller->config());
m_ui->stack->addWidget(m_viewCommandLinePage);
QSettings settings;
restoreGeometry(settings.value("window-geometry").toByteArray());
m_controller->restoreSettings();
restoreSettings();
emit summaryChanged();
emit showNoOfficialHangarChanged();
////////////
#if defined(Q_OS_WIN)
const QString osName("win");
#elif defined(Q_OS_MAC)
const QString osName("mac");
#else
const QString osName("unix");
#endif
/////////////
// aircraft
m_ui->aircraftList->setResizeMode(QQuickWidget::SizeRootObjectToView);
m_ui->aircraftList->engine()->addImportPath("qrc:///");
m_ui->aircraftList->engine()->rootContext()->setContextProperty("_launcher", this);
m_ui->aircraftList->engine()->rootContext()->setContextProperty("_launcher", m_controller);
connect( m_ui->aircraftList, &QQuickWidget::statusChanged,
this, &LauncherMainWindow::onQuickStatusChanged);
@ -177,8 +151,11 @@ LauncherMainWindow::LauncherMainWindow() :
// settings
m_ui->settings->engine()->addImportPath("qrc:///");
m_ui->settings->engine()->rootContext()->setContextProperty("_launcher", this);
m_ui->settings->engine()->rootContext()->setContextProperty("_mpServers", m_serversModel);
QQmlContext* settingsContext = m_ui->settings->engine()->rootContext();
settingsContext->setContextProperty("_launcher", m_controller);
settingsContext->setContextProperty("_osName", osName);
settingsContext->setContextProperty("_config", m_controller->config());
m_ui->settings->setResizeMode(QQuickWidget::SizeRootObjectToView);
connect( m_ui->settings, &QQuickWidget::statusChanged,
this, &LauncherMainWindow::onQuickStatusChanged);
@ -186,10 +163,10 @@ LauncherMainWindow::LauncherMainWindow() :
// environemnt
m_ui->environmentPage->engine()->addImportPath("qrc:///");
m_ui->environmentPage->engine()->rootContext()->setContextProperty("_launcher", this);
m_ui->environmentPage->engine()->rootContext()->setContextProperty("_launcher", m_controller);
auto weatherScenariosModel = new flightgear::WeatherScenariosModel(this);
m_ui->environmentPage->engine()->rootContext()->setContextProperty("_weatherScenarios", weatherScenariosModel);
m_ui->environmentPage->engine()->rootContext()->setContextProperty("_config", m_config);
m_ui->environmentPage->engine()->rootContext()->setContextProperty("_config", m_controller->config());
m_ui->environmentPage->setResizeMode(QQuickWidget::SizeRootObjectToView);
@ -199,7 +176,9 @@ LauncherMainWindow::LauncherMainWindow() :
// summary
m_ui->summary->engine()->addImportPath("qrc:///");
m_ui->summary->engine()->rootContext()->setContextProperty("_launcher", this);
m_ui->summary->engine()->rootContext()->setContextProperty("_launcher", m_controller);
m_ui->summary->engine()->rootContext()->setContextProperty("_config", m_controller->config());
m_ui->summary->setResizeMode(QQuickWidget::SizeRootObjectToView);
connect( m_ui->summary, &QQuickWidget::statusChanged,
@ -209,52 +188,17 @@ LauncherMainWindow::LauncherMainWindow() :
}
void LauncherMainWindow::initQML()
void LauncherMainWindow::restoreSettings()
{
qmlRegisterType<LauncherArgumentTokenizer>("FlightGear.Launcher", 1, 0, "ArgumentTokenizer");
qmlRegisterUncreatableType<QAbstractItemModel>("FlightGear.Launcher", 1, 0, "QAIM", "no");
qmlRegisterUncreatableType<AircraftProxyModel>("FlightGear.Launcher", 1, 0, "AircraftProxyModel", "no");
qmlRegisterUncreatableType<RecentAircraftModel>("FlightGear.Launcher", 1, 0, "RecentAircraftModel", "no");
qmlRegisterUncreatableType<RecentLocationsModel>("FlightGear.Launcher", 1, 0, "RecentLocationsModel", "no");
qmlRegisterUncreatableType<LaunchConfig>("FlightGear.Launcher", 1, 0, "LaunchConfig", "Singleton API");
if (!m_inAppMode) {
m_controller->setSceneryPaths();
}
}
qmlRegisterType<FileDialogWrapper>("FlightGear.Launcher", 1, 0, "FileDialog");
qmlRegisterType<FlickableExtentQuery>("FlightGear.Launcher", 1, 0, "FlickableExtentQuery");
qmlRegisterType<QmlAircraftInfo>("FlightGear.Launcher", 1, 0, "AircraftInfo");
qmlRegisterType<PopupWindowTracker>("FlightGear.Launcher", 1, 0, "PopupWindowTracker");
qmlRegisterUncreatableType<LocalAircraftCache>("FlightGear.Launcher", 1, 0, "LocalAircraftCache", "Aircraft cache");
qmlRegisterUncreatableType<AircraftItemModel>("FlightGear.Launcher", 1, 0, "AircraftModel", "Built-in model");
qmlRegisterType<ThumbnailImageItem>("FlightGear.Launcher", 1, 0, "ThumbnailImage");
qmlRegisterType<PreviewImageItem>("FlightGear.Launcher", 1, 0, "PreviewImage");
m_config = new LaunchConfig(this);
connect(m_config, &LaunchConfig::collect, this, &LauncherMainWindow::collectAircraftArgs);
m_ui->location->setLaunchConfig(m_config);
#if defined(Q_OS_WIN)
const QString osName("win");
#elif defined(Q_OS_MAC)
const QString osName("mac");
#else
const QString osName("unix");
#endif
QQmlContext* settingsContext = m_ui->settings->engine()->rootContext();
settingsContext->setContextProperty("_mpServers", m_serversModel);
settingsContext->setContextProperty("_config", m_config);
settingsContext->setContextProperty("_osName", osName);
QQmlContext* summaryContext = m_ui->summary->engine()->rootContext();
summaryContext->setContextProperty("_config", m_config);
QNetworkDiskCache* diskCache = new QNetworkDiskCache(this);
SGPath cachePath = globals->get_fg_home() / "PreviewsCache";
diskCache->setCacheDirectory(QString::fromStdString(cachePath.utf8Str()));
QNetworkAccessManager* netAccess = new QNetworkAccessManager(this);
netAccess->setCache(diskCache);
PreviewImageItem::setGlobalNetworkAccess(netAccess);
void LauncherMainWindow::saveSettings()
{
QSettings settings;
settings.setValue("window-geometry", saveGeometry());
}
void LauncherMainWindow::onQuickStatusChanged(QQuickWidget::Status status)
@ -275,32 +219,13 @@ void LauncherMainWindow::onQuickStatusChanged(QQuickWidget::Status status)
}
}
void LauncherMainWindow::onCanFlyChanged()
{
m_ui->flyButton->setEnabled(m_controller->canFly());
}
LauncherMainWindow::~LauncherMainWindow()
{
std::vector<QQuickWidget*> quickWidgets = { m_ui->aircraftList,
m_ui->settings,
m_ui->summary,
m_ui->environmentPage};
// candidate work-around for crash on deleting the launcher window
for (auto qw : quickWidgets) {
qw->setSource(QUrl{});
}
#if 1
for (auto qw : quickWidgets) {
auto rootContext = qw->engine()->rootContext();
rootContext->setContextProperty("_launcher", nullptr);
}
#endif
// candidate work-around for crash on deleting the launcher window
// this un-parents the quick widgets so they won't be freed
#if 0
for (auto qw : quickWidgets) {
qw->setParent(nullptr);
}
#endif
}
bool LauncherMainWindow::execInApp()
@ -321,180 +246,24 @@ bool LauncherMainWindow::execInApp()
return m_accepted;
}
void LauncherMainWindow::restoreSettings()
{
QSettings settings;
restoreGeometry(settings.value("window-geometry").toByteArray());
m_selectedAircraft = m_aircraftHistory->mostRecent();
if (m_selectedAircraft.isEmpty()) {
// select the default aircraft specified in defaults.xml
flightgear::DefaultAircraftLocator da;
if (da.foundPath().exists()) {
m_selectedAircraft = QUrl::fromLocalFile(QString::fromStdString(da.foundPath().utf8Str()));
qDebug() << "Restored default aircraft:" << m_selectedAircraft;
} else {
qWarning() << "Completely failed to find the default aircraft";
}
}
if (!m_inAppMode) {
setSceneryPaths();
}
m_ui->location->restoreSettings();
QVariantMap currentLocation = m_locationHistory->mostRecent();
if (currentLocation.isEmpty()) {
// use the default
std::string defaultAirport = flightgear::defaultAirportICAO();
FGAirportRef apt = FGAirport::findByIdent(defaultAirport);
if (apt) {
currentLocation["location-id"] = static_cast<qlonglong>(apt->guid());
currentLocation["location-apt-runway"] = "active";
} // otherwise we failed to find the default airport in the nav-db :(
}
m_ui->location->restoreLocation(currentLocation);
updateSelectedAircraft();
m_serversModel->requestRestore();
}
void LauncherMainWindow::saveSettings()
{
emit requestSaveState();
m_aircraftHistory->saveToSettings();
m_locationHistory->saveToSettings();
QSettings settings;
settings.setValue("window-geometry", saveGeometry());
}
void LauncherMainWindow::closeEvent(QCloseEvent *event)
{
qApp->exit(-1);
}
void LauncherMainWindow::collectAircraftArgs()
{
// aircraft
if (!m_selectedAircraft.isEmpty()) {
if (m_selectedAircraft.isLocalFile()) {
QFileInfo setFileInfo(m_selectedAircraft.toLocalFile());
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
m_config->setArg("aircraft", setFile);
} else if (m_selectedAircraft.scheme() == "package") {
// no need to set aircraft-dir, handled by the corresponding code
// in fgInitAircraft
m_config->setArg("aircraft", m_selectedAircraft.path());
} else {
qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
}
}
// scenery paths
QSettings settings;
Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) {
m_config->setArg("fg-scenery", path);
}
// aircraft paths
Q_FOREACH(QString path, settings.value("aircraft-paths").toStringList()) {
m_config->setArg("fg-aircraft", path);
}
}
void LauncherMainWindow::onRun()
{
flightgear::Options* opt = flightgear::Options::sharedInstance();
m_config->reset();
m_config->collect();
m_aircraftHistory->insert(m_selectedAircraft);
QVariant locSet = m_ui->location->saveLocation();
m_locationHistory->insert(locSet);
// aircraft paths
QSettings settings;
QString downloadDir = settings.value("downloadSettings/downloadDir").toString();
if (!downloadDir.isEmpty()) {
QDir d(downloadDir);
if (!d.exists()) {
int result = QMessageBox::question(this, tr("Create download folder?"),
tr("The selected location for downloads does not exist. (%1) Create it?").arg(downloadDir),
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
if (result == QMessageBox::Cancel) {
return;
}
if (result == QMessageBox::Yes) {
d.mkpath(downloadDir);
}
}
}
if (settings.contains("restore-defaults-on-run")) {
settings.remove("restore-defaults-on-run");
opt->addOption("restore-defaults", "");
}
m_config->applyToOptions();
m_controller->doRun();
saveSettings();
// set a positive value here so we can detect this case in runLauncherDialog
qApp->exit(1);
}
QString LauncherMainWindow::selectAircraftStateAutomatically()
{
if (m_ui->location->isAirborneLocation()) {
return "approach";
}
if (m_ui->location->isParkedLocation()) {
return "parked";
} else {
return "take-off";
}
return {}; // failed to compute, give up
}
void LauncherMainWindow::onApply()
{
// aircraft
if (!m_selectedAircraft.isEmpty()) {
std::string aircraftPropValue,
aircraftDir;
if (m_selectedAircraft.isLocalFile()) {
QFileInfo setFileInfo(m_selectedAircraft.toLocalFile());
QString setFile = setFileInfo.fileName();
Q_ASSERT(setFile.endsWith("-set.xml"));
setFile.truncate(setFile.count() - 8); // drop the '-set.xml' portion
aircraftDir = setFileInfo.dir().absolutePath().toStdString();
aircraftPropValue = setFile.toStdString();
} else if (m_selectedAircraft.scheme() == "package") {
// no need to set aircraft-dir, handled by the corresponding code
// in fgInitAircraft
aircraftPropValue = m_selectedAircraft.path().toStdString();
} else {
qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
}
m_aircraftHistory->insert(m_selectedAircraft);
globals->get_props()->setStringValue("/sim/aircraft", aircraftPropValue);
globals->get_props()->setStringValue("/sim/aircraft-dir", aircraftDir);
}
// location
m_ui->location->setLocationProperties();
m_controller->doApply();
saveSettings();
m_accepted = true;
m_runInApp = false;
@ -509,25 +278,6 @@ void LauncherMainWindow::onQuit()
}
}
void LauncherMainWindow::onAircraftInstalledCompleted(QModelIndex index)
{
maybeUpdateSelectedAircraft(index);
}
void LauncherMainWindow::onAircraftInstallFailed(QModelIndex index, QString errorMessage)
{
qWarning() << Q_FUNC_INFO << index.data(AircraftURIRole) << errorMessage;
QMessageBox msg;
msg.setWindowTitle(tr("Aircraft installation failed"));
msg.setText(tr("An error occurred installing the aircraft %1: %2").
arg(index.data(Qt::DisplayRole).toString()).arg(errorMessage));
msg.addButton(QMessageBox::Ok);
msg.exec();
maybeUpdateSelectedAircraft(index);
}
void LauncherMainWindow::onRestoreDefaults()
{
QMessageBox mbox(this);
@ -561,36 +311,6 @@ void LauncherMainWindow::onViewCommandLine()
m_viewCommandLinePage->update();
}
void LauncherMainWindow::maybeUpdateSelectedAircraft(QModelIndex index)
{
QUrl u = index.data(AircraftURIRole).toUrl();
if (u == m_selectedAircraft) {
// potentially enable the run button now!
updateSelectedAircraft();
}
}
void LauncherMainWindow::updateSelectedAircraft()
{
m_selectedAircraftInfo->setUri(m_selectedAircraft);
QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
if (index.isValid()) {
int status = index.data(AircraftPackageStatusRole).toInt();
bool canRun = (status == LocalAircraftCache::PackageInstalled);
m_ui->flyButton->setEnabled(canRun);
LauncherAircraftType aircraftType = Airplane;
if (index.data(AircraftIsHelicopterRole).toBool()) {
aircraftType = Helicopter;
} else if (index.data(AircraftIsSeaplaneRole).toBool()) {
aircraftType = Seaplane;
}
m_ui->location->setAircraftType(aircraftType);
} else {
m_ui->flyButton->setEnabled(false);
}
}
void LauncherMainWindow::onClickToolboxButton()
{
@ -602,202 +322,11 @@ void LauncherMainWindow::onClickToolboxButton()
saveSettings();
}
void LauncherMainWindow::setSceneryPaths()
{
flightgear::launcherSetSceneryPaths();
}
void LauncherMainWindow::onSubsytemIdleTimeout()
{
globals->get_subsystem_mgr()->update(0.0);
}
void LauncherMainWindow::downloadDirChanged(QString path)
{
// this can get run if the UI is disabled, just bail out before doing
// anything permanent.
if (!m_config->enableDownloadDirUI()) {
return;
}
// if the default dir is passed in, map that back to the emptru string
if (path == m_config->defaultDownloadDir()) {
path.clear();;
}
auto options = flightgear::Options::sharedInstance();
if (options->valueForOption("download-dir") == path.toStdString()) {
// this works because we propogate the value from QSettings to
// the flightgear::Options object in runLauncherDialog()
// so the options object always contains our current idea of this
// value
return;
}
if (!path.isEmpty()) {
options->setOption("download-dir", path.toStdString());
} else {
options->clearOption("download-dir");
}
// replace existing package root
globals->get_subsystem<FGHTTPClient>()->shutdown();
globals->setPackageRoot(simgear::pkg::RootRef());
// create new root with updated download-dir value
fgInitPackageRoot();
globals->get_subsystem<FGHTTPClient>()->init();
QSettings settings;
// re-scan the aircraft list
m_aircraftModel->setPackageRoot(globals->packageRoot());
auto aircraftCache = LocalAircraftCache::instance();
aircraftCache->setPaths(settings.value("aircraft-paths").toStringList());
aircraftCache->scanDirs();
emit showNoOfficialHangarChanged();
// re-set scenery dirs
setSceneryPaths();
}
bool LauncherMainWindow::shouldShowOfficialCatalogMessage() const
{
QSettings settings;
bool showOfficialCatalogMesssage = !globals->get_subsystem<FGHTTPClient>()->isDefaultCatalogInstalled();
if (settings.value("hide-official-catalog-message").toBool()) {
showOfficialCatalogMesssage = false;
}
return showOfficialCatalogMesssage;
}
void LauncherMainWindow::officialCatalogAction(QString s)
{
if (s == "hide") {
QSettings settings;
settings.setValue("hide-official-catalog-message", true);
} else if (s == "add-official") {
AddOnsPage::addDefaultCatalog(this, false /* not silent */);
}
emit showNoOfficialHangarChanged();
}
QUrl LauncherMainWindow::selectedAircraft() const
{
return m_selectedAircraft;
}
QPointF LauncherMainWindow::mapToGlobal(QQuickItem *item, const QPointF &pos) const
{
QPointF scenePos = item->mapToScene(pos);
return m_ui->aircraftList->mapToGlobal(scenePos.toPoint());
}
QmlAircraftInfo *LauncherMainWindow::selectedAircraftInfo() const
{
return m_selectedAircraftInfo;
}
void LauncherMainWindow::restoreLocation(QVariant var)
{
m_ui->location->restoreLocation(var.toMap());
}
bool LauncherMainWindow::matchesSearch(QString term, QStringList keywords) const
{
Q_FOREACH(QString s, keywords) {
if (s.contains(term, Qt::CaseInsensitive)) {
return true;
}
}
return false;
}
bool LauncherMainWindow::isSearchActive() const
{
return !m_settingsSearchTerm.isEmpty();
}
QStringList LauncherMainWindow::settingsSummary() const
{
return m_settingsSummary;
}
QStringList LauncherMainWindow::environmentSummary() const
{
return m_environmentSummary;
}
void LauncherMainWindow::setSelectedAircraft(QUrl selectedAircraft)
{
if (m_selectedAircraft == selectedAircraft)
return;
m_selectedAircraft = selectedAircraft;
updateSelectedAircraft();
emit selectedAircraftChanged(m_selectedAircraft);
}
void LauncherMainWindow::setSettingsSearchTerm(QString settingsSearchTerm)
{
if (m_settingsSearchTerm == settingsSearchTerm)
return;
m_settingsSearchTerm = settingsSearchTerm;
emit searchChanged();
}
void LauncherMainWindow::setSettingsSummary(QStringList settingsSummary)
{
if (m_settingsSummary == settingsSummary)
return;
m_settingsSummary = settingsSummary;
emit summaryChanged();
}
void LauncherMainWindow::setEnvironmentSummary(QStringList environmentSummary)
{
if (m_environmentSummary == environmentSummary)
return;
m_environmentSummary = environmentSummary;
emit summaryChanged();
}
QStringList LauncherMainWindow::combinedSummary() const
{
return m_settingsSummary + m_environmentSummary;
}
QString LauncherMainWindow::locationDescription() const
{
return m_ui->location->locationDescription();
}
simgear::pkg::PackageRef LauncherMainWindow::packageForAircraftURI(QUrl uri) const
{
if (uri.scheme() != "package") {
qWarning() << "invalid URL scheme:" << uri;
return simgear::pkg::PackageRef();
}
QString ident = uri.path();
return globals->packageRoot()->getPackageById(ident.toStdString());
}
void LauncherMainWindow::onAircraftPathsChanged()
{
QSettings settings;
auto aircraftCache = LocalAircraftCache::instance();
aircraftCache->setPaths(settings.value("aircraft-paths").toStringList());
aircraftCache->scanDirs();
}
void LauncherMainWindow::onChangeDataDir()
{
QString currentLocText;
@ -835,109 +364,4 @@ void LauncherMainWindow::onChangeDataDir()
}
bool LauncherMainWindow::validateMetarString(QString metar)
{
if (metar.isEmpty()) {
return true;
}
try {
std::string s = metar.toStdString();
SGMetar theMetar(s);
} catch (sg_io_exception&) {
return false;
}
return true;
}
void LauncherMainWindow::requestInstallUpdate(QUrl aircraftUri)
{
// also select, otherwise UI is confusing
m_selectedAircraft = aircraftUri;
updateSelectedAircraft();
simgear::pkg::PackageRef pref = packageForAircraftURI(aircraftUri);
if (pref) {
if (pref->isInstalled()) {
InstallRef install = pref->existingInstall();
if (install->hasUpdate()) {
globals->packageRoot()->scheduleToUpdate(install);
}
} else {
pref->install();
}
}
}
void LauncherMainWindow::requestUninstall(QUrl aircraftUri)
{
simgear::pkg::PackageRef pref = packageForAircraftURI(aircraftUri);
if (pref) {
simgear::pkg::InstallRef i = pref->existingInstall();
if (i) {
i->uninstall();
}
}
}
void LauncherMainWindow::requestInstallCancel(QUrl aircraftUri)
{
simgear::pkg::PackageRef pref = packageForAircraftURI(aircraftUri);
if (pref) {
simgear::pkg::InstallRef i = pref->existingInstall();
if (i) {
i->cancelDownload();
}
}
}
void LauncherMainWindow::requestUpdateAllAircraft()
{
const PackageList& toBeUpdated = globals->packageRoot()->packagesNeedingUpdate();
std::for_each(toBeUpdated.begin(), toBeUpdated.end(), [](PackageRef pkg) {
globals->packageRoot()->scheduleToUpdate(pkg->install());
});
}
void LauncherMainWindow::queryMPServers()
{
m_serversModel->refresh();
}
bool LauncherMainWindow::showNoOfficialHanger() const
{
return shouldShowOfficialCatalogMessage();
}
QString LauncherMainWindow::versionString() const
{
return FLIGHTGEAR_VERSION;
}
RecentAircraftModel *LauncherMainWindow::aircraftHistory()
{
return m_aircraftHistory;
}
RecentLocationsModel *LauncherMainWindow::locationHistory()
{
return m_locationHistory;
}
void LauncherMainWindow::launchUrl(QUrl url)
{
QDesktopServices::openUrl(url);
}
QVariantList LauncherMainWindow::defaultSplashUrls() const
{
QVariantList urls;
for (auto path : flightgear::defaultSplashScreenPaths()) {
QUrl url = QUrl::fromLocalFile(QString::fromStdString(path));
urls.append(url);
}
return urls;
}

View file

@ -29,61 +29,21 @@
#include <QUrl>
#include <QQuickWidget>
#include <simgear/package/Package.hxx>
#include <simgear/package/Catalog.hxx>
namespace Ui
{
class Launcher;
}
class QModelIndex;
class AircraftProxyModel;
class AircraftItemModel;
class QCheckBox;
class CatalogListModel;
class QQmlEngine;
class LaunchConfig;
class ExtraSettingsSection;
class ViewCommandLinePage;
class MPServersModel;
class QQuickItem;
class QmlAircraftInfo;
class QStandardItemModel;
class RecentAircraftModel;
class RecentLocationsModel;
class LauncherController;
class LauncherMainWindow : public QMainWindow
{
Q_OBJECT
Q_PROPERTY(bool showNoOfficialHanger READ showNoOfficialHanger NOTIFY showNoOfficialHangarChanged)
Q_PROPERTY(AircraftProxyModel* installedAircraftModel MEMBER m_installedAircraftModel CONSTANT)
Q_PROPERTY(AircraftProxyModel* browseAircraftModel MEMBER m_browseAircraftModel CONSTANT)
Q_PROPERTY(AircraftProxyModel* searchAircraftModel MEMBER m_aircraftSearchModel CONSTANT)
Q_PROPERTY(AircraftItemModel* baseAircraftModel MEMBER m_aircraftModel CONSTANT)
Q_PROPERTY(QUrl selectedAircraft READ selectedAircraft WRITE setSelectedAircraft NOTIFY selectedAircraftChanged)
Q_PROPERTY(QmlAircraftInfo* selectedAircraftInfo READ selectedAircraftInfo NOTIFY selectedAircraftChanged)
Q_PROPERTY(bool isSearchActive READ isSearchActive NOTIFY searchChanged)
Q_PROPERTY(QString settingsSearchTerm READ settingsSearchTerm WRITE setSettingsSearchTerm NOTIFY searchChanged)
Q_PROPERTY(QStringList settingsSummary READ settingsSummary WRITE setSettingsSummary NOTIFY summaryChanged)
Q_PROPERTY(QStringList environmentSummary READ environmentSummary WRITE setEnvironmentSummary NOTIFY summaryChanged)
Q_PROPERTY(QString locationDescription READ locationDescription NOTIFY summaryChanged)
Q_PROPERTY(QStringList combinedSummary READ combinedSummary NOTIFY summaryChanged)
Q_PROPERTY(QString versionString READ versionString CONSTANT)
Q_PROPERTY(RecentAircraftModel* aircraftHistory READ aircraftHistory CONSTANT)
Q_PROPERTY(RecentLocationsModel* locationHistory READ locationHistory CONSTANT)
public:
LauncherMainWindow();
virtual ~LauncherMainWindow();
@ -92,88 +52,7 @@ public:
bool wasRejected();
Q_INVOKABLE bool validateMetarString(QString metar);
Q_INVOKABLE void requestInstallUpdate(QUrl aircraftUri);
Q_INVOKABLE void requestUninstall(QUrl aircraftUri);
Q_INVOKABLE void requestInstallCancel(QUrl aircraftUri);
Q_INVOKABLE void downloadDirChanged(QString path);
Q_INVOKABLE void requestUpdateAllAircraft();
Q_INVOKABLE void queryMPServers();
bool showNoOfficialHanger() const;
Q_INVOKABLE void officialCatalogAction(QString s);
QUrl selectedAircraft() const;
// work around the fact, that this is not available on QQuickItem until 5.7
Q_INVOKABLE QPointF mapToGlobal(QQuickItem* item, const QPointF& pos) const;
QmlAircraftInfo* selectedAircraftInfo() const;
Q_INVOKABLE void restoreLocation(QVariant var);
Q_INVOKABLE bool matchesSearch(QString term, QStringList keywords) const;
bool isSearchActive() const;
QString settingsSearchTerm() const
{
return m_settingsSearchTerm;
}
QStringList settingsSummary() const;
QStringList environmentSummary() const;
QStringList combinedSummary() const;
QString locationDescription() const;
QString versionString() const;
RecentAircraftModel* aircraftHistory();
RecentLocationsModel* locationHistory();
Q_INVOKABLE void launchUrl(QUrl url);
// list of QUrls containing the default splash images from FGData.
// used on the summary screen
Q_INVOKABLE QVariantList defaultSplashUrls() const;
Q_INVOKABLE QString selectAircraftStateAutomatically();
public slots:
void setSelectedAircraft(QUrl selectedAircraft);
void setSettingsSearchTerm(QString settingsSearchTerm);
void setSettingsSummary(QStringList settingsSummary);
void setEnvironmentSummary(QStringList environmentSummary);
signals:
void showNoOfficialHangarChanged();
void selectedAircraftChanged(QUrl selectedAircraft);
void searchChanged();
void summaryChanged();
/**
* @brief requestSaveState - signal to request QML settings to save their
* state to persistent storage
*/
void requestSaveState();
protected:
virtual void closeEvent(QCloseEvent *event) override;
@ -190,64 +69,32 @@ private slots:
void onSubsytemIdleTimeout();
void onAircraftInstalledCompleted(QModelIndex index);
void onAircraftInstallFailed(QModelIndex index, QString errorMessage);
void onRestoreDefaults();
void onViewCommandLine();
void onClickToolboxButton();
void setSceneryPaths();
void onAircraftPathsChanged();
void onChangeDataDir();
void onQuickStatusChanged(QQuickWidget::Status status);
void onCanFlyChanged();
void onChangeDataDir();
private:
/**
* Check if the passed index is the selected aircraft, and if so, refresh
* the associated UI data
*/
void maybeUpdateSelectedAircraft(QModelIndex index);
void updateSelectedAircraft();
void restoreSettings();
void saveSettings();
simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const;
// need to wait after a model reset before restoring selection and
// scrolling, to give the view time it seems.
void delayedAircraftModelReset();
bool shouldShowOfficialCatalogMessage() const;
void collectAircraftArgs();
void initQML();
LauncherController* m_controller;
QScopedPointer<Ui::Launcher> m_ui;
AircraftProxyModel* m_installedAircraftModel;
AircraftItemModel* m_aircraftModel;
AircraftProxyModel* m_aircraftSearchModel;
AircraftProxyModel* m_browseAircraftModel;
MPServersModel* m_serversModel = nullptr;
QUrl m_selectedAircraft;
QTimer* m_subsystemIdleTimer;
bool m_inAppMode = false;
bool m_runInApp = false;
bool m_accepted = false;
int m_ratingFilters[4] = {3, 3, 3, 3};
QQmlEngine* m_qmlEngine = nullptr;
LaunchConfig* m_config = nullptr;
ViewCommandLinePage* m_viewCommandLinePage = nullptr;
QmlAircraftInfo* m_selectedAircraftInfo = nullptr;
QString m_settingsSearchTerm;
QStringList m_settingsSummary,
m_environmentSummary;
RecentAircraftModel* m_aircraftHistory = nullptr;
RecentLocationsModel* m_locationHistory = nullptr;
void restoreSettings();
void saveSettings();
};
#endif // of LAUNCHER_MAIN_WINDOW_HXX

View file

@ -186,9 +186,9 @@ Item {
label: qsTr("Server")
enabled: enableMP.checked
description: qsTr("Select a server close to you for better responsiveness and reduced lag when flying online.")
choices: _mpServers
choices: _launcher.mpServersModel
readonly property bool currentIsCustom: (_mpServers.serverForIndex(selectedIndex) == "__custom__")
readonly property bool currentIsCustom: (_launcher.mpServersModel.serverForIndex(selectedIndex) == "__custom__")
property string __savedServer;
keywords: ["server", "hostname"]
@ -202,7 +202,7 @@ Item {
{
// these values match the code in MPServersModel.cpp, sorry for that
// nastyness
_config.setValueForKey("mpSettings", "mp-server", _mpServers.serverForIndex(selectedIndex) );
_config.setValueForKey("mpSettings", "mp-server", _launcher.mpServersModel.serverForIndex(selectedIndex) );
}
function restoreState()
@ -212,7 +212,7 @@ Item {
Connections
{
target: _mpServers
target: _launcher.mpServersModel
onRestoreIndex: { mpServer.selectedIndex = index }
}
},