2015-03-07 23:52:03 +00:00
|
|
|
// QtLauncher.cxx - GUI launcher dialog using Qt5
|
|
|
|
//
|
|
|
|
// Written by James Turner, started December 2014.
|
|
|
|
//
|
|
|
|
// Copyright (C) 2014 James Turner <zakalawe@mac.com>
|
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License as
|
|
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful, but
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program; if not, write to the Free Software
|
|
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
#include "QtLauncher.hxx"
|
2015-10-16 01:05:17 +00:00
|
|
|
#include "QtLauncher_private.hxx"
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2016-02-02 20:44:46 +00:00
|
|
|
#include <locale.h>
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
// Qt
|
|
|
|
#include <QProgressDialog>
|
|
|
|
#include <QCoreApplication>
|
|
|
|
#include <QAbstractListModel>
|
|
|
|
#include <QDir>
|
|
|
|
#include <QFileInfo>
|
|
|
|
#include <QPixmap>
|
|
|
|
#include <QTimer>
|
|
|
|
#include <QDebug>
|
|
|
|
#include <QCompleter>
|
|
|
|
#include <QListView>
|
|
|
|
#include <QSettings>
|
|
|
|
#include <QSortFilterProxyModel>
|
|
|
|
#include <QMenu>
|
|
|
|
#include <QDesktopServices>
|
|
|
|
#include <QUrl>
|
|
|
|
#include <QAction>
|
|
|
|
#include <QFileDialog>
|
2015-01-10 14:18:44 +00:00
|
|
|
#include <QMessageBox>
|
2015-03-05 17:39:37 +00:00
|
|
|
#include <QDateTime>
|
2015-03-06 18:52:06 +00:00
|
|
|
#include <QApplication>
|
2015-10-16 01:05:17 +00:00
|
|
|
#include <QSpinBox>
|
|
|
|
#include <QDoubleSpinBox>
|
2016-01-17 19:10:22 +00:00
|
|
|
#include <QProcess>
|
2016-08-08 22:13:57 +00:00
|
|
|
#include <QThread>
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
// Simgear
|
|
|
|
#include <simgear/timing/timestamp.hxx>
|
|
|
|
#include <simgear/props/props_io.hxx>
|
|
|
|
#include <simgear/structure/exception.hxx>
|
2015-03-10 08:30:55 +00:00
|
|
|
#include <simgear/structure/subsystem_mgr.hxx>
|
2014-12-26 12:20:51 +00:00
|
|
|
#include <simgear/misc/sg_path.hxx>
|
2015-08-03 20:53:56 +00:00
|
|
|
#include <simgear/package/Catalog.hxx>
|
|
|
|
#include <simgear/package/Package.hxx>
|
|
|
|
#include <simgear/package/Install.hxx>
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
#include "ui_Launcher.h"
|
2016-04-15 16:06:53 +00:00
|
|
|
#include "ui_NoOfficialHangar.h"
|
2016-11-07 19:49:33 +00:00
|
|
|
#include "ui_UpdateAllAircraft.h"
|
2016-04-15 16:06:53 +00:00
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
#include "EditRatingsFilterDialog.hxx"
|
2015-03-10 00:13:55 +00:00
|
|
|
#include "AircraftItemDelegate.hxx"
|
|
|
|
#include "AircraftModel.hxx"
|
2015-03-25 14:16:09 +00:00
|
|
|
#include "PathsDialog.hxx"
|
2016-07-08 08:36:42 +00:00
|
|
|
#include "EditCustomMPServerDialog.hxx"
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
#include <Main/globals.hxx>
|
2016-07-08 08:36:42 +00:00
|
|
|
#include <Main/fg_props.hxx>
|
2014-12-26 12:20:51 +00:00
|
|
|
#include <Navaids/NavDataCache.hxx>
|
2015-10-16 01:05:17 +00:00
|
|
|
#include <Navaids/navrecord.hxx>
|
2015-11-26 16:48:23 +00:00
|
|
|
#include <Navaids/SHPParser.hxx>
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
#include <Main/options.hxx>
|
2015-03-10 08:30:55 +00:00
|
|
|
#include <Main/fg_init.hxx>
|
2015-03-06 18:52:06 +00:00
|
|
|
#include <Viewer/WindowBuilder.hxx>
|
2015-03-10 08:30:55 +00:00
|
|
|
#include <Network/HTTPClient.hxx>
|
2016-07-08 08:36:42 +00:00
|
|
|
#include <Network/RemoteXMLRequest.hxx>
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
using namespace flightgear;
|
2015-08-03 20:53:56 +00:00
|
|
|
using namespace simgear::pkg;
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
const int MAX_RECENT_AIRCRAFT = 20;
|
2016-11-06 20:51:15 +00:00
|
|
|
const int DEFAULT_MP_PORT = 5000;
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
namespace { // anonymous namespace
|
|
|
|
|
|
|
|
void initNavCache()
|
|
|
|
{
|
2015-03-19 17:01:38 +00:00
|
|
|
QString baseLabel = QT_TR_NOOP("Initialising navigation data, this may take several minutes");
|
2015-03-08 00:40:22 +00:00
|
|
|
NavDataCache* cache = NavDataCache::createInstance();
|
2014-12-26 12:20:51 +00:00
|
|
|
if (cache->isRebuildRequired()) {
|
2015-03-19 17:01:38 +00:00
|
|
|
QProgressDialog rebuildProgress(baseLabel,
|
2014-12-26 12:20:51 +00:00
|
|
|
QString() /* cancel text */,
|
2016-04-13 14:15:27 +00:00
|
|
|
0, 100, Q_NULLPTR,
|
|
|
|
Qt::Dialog
|
|
|
|
| Qt::CustomizeWindowHint
|
|
|
|
| Qt::WindowTitleHint
|
|
|
|
| Qt::WindowSystemMenuHint
|
|
|
|
| Qt::MSWindowsFixedSizeDialogHint);
|
2014-12-26 12:20:51 +00:00
|
|
|
rebuildProgress.setWindowModality(Qt::WindowModal);
|
|
|
|
rebuildProgress.show();
|
|
|
|
|
2015-03-19 17:01:38 +00:00
|
|
|
NavDataCache::RebuildPhase phase = cache->rebuild();
|
|
|
|
|
|
|
|
while (phase != NavDataCache::REBUILD_DONE) {
|
2014-12-26 12:20:51 +00:00
|
|
|
// sleep to give the rebuild thread more time
|
|
|
|
SGTimeStamp::sleepForMSec(50);
|
2015-03-19 17:01:38 +00:00
|
|
|
phase = cache->rebuild();
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2015-03-19 17:01:38 +00:00
|
|
|
switch (phase) {
|
2016-10-19 11:01:35 +00:00
|
|
|
case NavDataCache::REBUILD_READING_APT_DAT_FILES:
|
|
|
|
rebuildProgress.setLabelText(QT_TR_NOOP("Reading airport data"));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NavDataCache::REBUILD_LOADING_AIRPORTS:
|
|
|
|
rebuildProgress.setLabelText(QT_TR_NOOP("Loading airports"));
|
2015-03-19 17:01:38 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
case NavDataCache::REBUILD_FIXES:
|
|
|
|
rebuildProgress.setLabelText(QT_TR_NOOP("Loading waypoint data"));
|
|
|
|
break;
|
|
|
|
|
|
|
|
case NavDataCache::REBUILD_NAVAIDS:
|
|
|
|
rebuildProgress.setLabelText(QT_TR_NOOP("Loading navigation data"));
|
|
|
|
break;
|
|
|
|
|
|
|
|
|
|
|
|
case NavDataCache::REBUILD_POIS:
|
|
|
|
rebuildProgress.setLabelText(QT_TR_NOOP("Loading point-of-interest data"));
|
|
|
|
break;
|
|
|
|
|
|
|
|
default:
|
|
|
|
rebuildProgress.setLabelText(baseLabel);
|
|
|
|
}
|
|
|
|
|
|
|
|
if (phase == NavDataCache::REBUILD_UNKNOWN) {
|
|
|
|
rebuildProgress.setValue(0);
|
|
|
|
rebuildProgress.setMaximum(0);
|
|
|
|
} else {
|
|
|
|
rebuildProgress.setValue(cache->rebuildPhaseCompletionPercentage());
|
|
|
|
rebuildProgress.setMaximum(100);
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
QCoreApplication::processEvents();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-23 13:41:30 +00:00
|
|
|
class ArgumentsTokenizer
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
class Arg
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
explicit Arg(QString k, QString v = QString()) : arg(k), value(v) {}
|
|
|
|
|
|
|
|
QString arg;
|
|
|
|
QString value;
|
|
|
|
};
|
|
|
|
|
|
|
|
QList<Arg> tokenize(QString in) const
|
|
|
|
{
|
|
|
|
int index = 0;
|
|
|
|
const int len = in.count();
|
|
|
|
QChar c, nc;
|
|
|
|
State state = Start;
|
|
|
|
QString key, value;
|
|
|
|
QList<Arg> result;
|
|
|
|
|
|
|
|
for (; index < len; ++index) {
|
|
|
|
c = in.at(index);
|
|
|
|
nc = index < (len - 1) ? in.at(index + 1) : QChar();
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case Start:
|
|
|
|
if (c == QChar('-')) {
|
|
|
|
if (nc == QChar('-')) {
|
|
|
|
state = Key;
|
|
|
|
key.clear();
|
|
|
|
++index;
|
|
|
|
} else {
|
|
|
|
// should we pemit single hyphen arguments?
|
|
|
|
// choosing to fail for now
|
|
|
|
return QList<Arg>();
|
|
|
|
}
|
2015-11-03 21:28:36 +00:00
|
|
|
} else if (c == QChar('#')) {
|
|
|
|
state = Comment;
|
|
|
|
break;
|
2015-02-23 13:41:30 +00:00
|
|
|
} else if (c.isSpace()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Key:
|
|
|
|
if (c == QChar('=')) {
|
|
|
|
state = Value;
|
|
|
|
value.clear();
|
|
|
|
} else if (c.isSpace()) {
|
|
|
|
state = Start;
|
|
|
|
result.append(Arg(key));
|
|
|
|
} else {
|
|
|
|
// could check for illegal charatcers here
|
|
|
|
key.append(c);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Value:
|
|
|
|
if (c == QChar('"')) {
|
|
|
|
state = Quoted;
|
|
|
|
} else if (c.isSpace()) {
|
|
|
|
state = Start;
|
|
|
|
result.append(Arg(key, value));
|
|
|
|
} else {
|
|
|
|
value.append(c);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
|
|
|
|
case Quoted:
|
|
|
|
if (c == QChar('\\')) {
|
|
|
|
// check for escaped double-quote inside quoted value
|
|
|
|
if (nc == QChar('"')) {
|
|
|
|
++index;
|
|
|
|
}
|
|
|
|
} else if (c == QChar('"')) {
|
|
|
|
state = Value;
|
|
|
|
} else {
|
|
|
|
value.append(c);
|
|
|
|
}
|
|
|
|
break;
|
2015-11-03 21:28:36 +00:00
|
|
|
|
|
|
|
case Comment:
|
|
|
|
if ((c == QChar('\n')) || (c == QChar('\r'))) {
|
|
|
|
state = Start;
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
// nothing to do, eat comment chars
|
|
|
|
}
|
|
|
|
break;
|
2015-02-23 13:41:30 +00:00
|
|
|
} // of state switch
|
|
|
|
} // of character loop
|
|
|
|
|
|
|
|
// ensure last argument isn't lost
|
|
|
|
if (state == Key) {
|
|
|
|
result.append(Arg(key));
|
|
|
|
} else if (state == Value) {
|
|
|
|
result.append(Arg(key, value));
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
|
|
|
enum State {
|
|
|
|
Start = 0,
|
|
|
|
Key,
|
|
|
|
Value,
|
2015-11-03 21:28:36 +00:00
|
|
|
Quoted,
|
|
|
|
Comment
|
2015-02-23 13:41:30 +00:00
|
|
|
};
|
|
|
|
};
|
|
|
|
|
2016-08-08 22:13:57 +00:00
|
|
|
class NaturalEarthDataLoaderThread : public QThread
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
|
2016-08-09 08:38:54 +00:00
|
|
|
NaturalEarthDataLoaderThread() :
|
|
|
|
m_lineInsertCount(0)
|
2016-08-08 22:13:57 +00:00
|
|
|
{
|
|
|
|
connect(this, &QThread::finished, this, &NaturalEarthDataLoaderThread::onFinished);
|
|
|
|
}
|
|
|
|
|
|
|
|
protected:
|
|
|
|
virtual void run() Q_DECL_OVERRIDE
|
|
|
|
{
|
|
|
|
SGTimeStamp st;
|
|
|
|
st.stamp();
|
|
|
|
|
|
|
|
loadNaturalEarthFile("ne_10m_coastline.shp", flightgear::PolyLine::COASTLINE, false);
|
|
|
|
loadNaturalEarthFile("ne_10m_rivers_lake_centerlines.shp", flightgear::PolyLine::RIVER, false);
|
|
|
|
loadNaturalEarthFile("ne_10m_lakes.shp", flightgear::PolyLine::LAKE, true);
|
|
|
|
|
|
|
|
qDebug() << "load basic data took" << st.elapsedMSec();
|
|
|
|
|
|
|
|
st.stamp();
|
|
|
|
loadNaturalEarthFile("ne_10m_urban_areas.shp", flightgear::PolyLine::URBAN, true);
|
|
|
|
|
|
|
|
qDebug() << "loading urban areas took:" << st.elapsedMSec();
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2016-08-15 22:10:06 +00:00
|
|
|
Q_SLOT void onFinished()
|
2016-08-08 22:13:57 +00:00
|
|
|
{
|
2016-08-09 08:38:54 +00:00
|
|
|
flightgear::PolyLineList::const_iterator begin = m_parsedLines.begin() + m_lineInsertCount;
|
2016-08-12 09:48:34 +00:00
|
|
|
unsigned int numToAdd = std::min<unsigned long>(1000UL, m_parsedLines.size() - m_lineInsertCount);
|
2016-08-09 08:38:54 +00:00
|
|
|
flightgear::PolyLineList::const_iterator end = begin + numToAdd;
|
|
|
|
flightgear::PolyLine::bulkAddToSpatialIndex(begin, end);
|
|
|
|
|
|
|
|
if (end == m_parsedLines.end()) {
|
|
|
|
deleteLater(); // commit suicide
|
|
|
|
} else {
|
|
|
|
m_lineInsertCount += 1000;
|
2016-08-11 21:32:29 +00:00
|
|
|
QTimer::singleShot(50, this, SLOT(onFinished()));
|
2016-08-09 08:38:54 +00:00
|
|
|
}
|
2016-08-08 22:13:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void loadNaturalEarthFile(const std::string& aFileName,
|
|
|
|
flightgear::PolyLine::Type aType,
|
|
|
|
bool areClosed)
|
|
|
|
{
|
|
|
|
SGPath path(globals->get_fg_root());
|
|
|
|
path.append( "Geodata" );
|
|
|
|
path.append(aFileName);
|
|
|
|
if (!path.exists())
|
|
|
|
return; // silently fail for now
|
|
|
|
|
|
|
|
flightgear::PolyLineList lines;
|
|
|
|
flightgear::SHPParser::parsePolyLines(path, aType, m_parsedLines, areClosed);
|
|
|
|
}
|
|
|
|
|
|
|
|
flightgear::PolyLineList m_parsedLines;
|
2016-08-09 08:38:54 +00:00
|
|
|
unsigned int m_lineInsertCount;
|
2016-08-08 22:13:57 +00:00
|
|
|
};
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
} // of anonymous namespace
|
|
|
|
|
|
|
|
class AircraftProxyModel : public QSortFilterProxyModel
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
AircraftProxyModel(QObject* pr) :
|
|
|
|
QSortFilterProxyModel(pr),
|
2016-01-10 18:56:46 +00:00
|
|
|
m_ratingsFilter(true),
|
|
|
|
m_onlyShowInstalled(false)
|
2014-12-26 12:20:51 +00:00
|
|
|
{
|
|
|
|
for (int i=0; i<4; ++i) {
|
|
|
|
m_ratings[i] = 3;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void setRatings(int* ratings)
|
|
|
|
{
|
|
|
|
::memcpy(m_ratings, ratings, sizeof(int) * 4);
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2016-11-13 21:51:35 +00:00
|
|
|
void setAircraftFilterString(QString s)
|
|
|
|
{
|
|
|
|
m_filterString = s;
|
2016-11-30 21:33:16 +00:00
|
|
|
|
|
|
|
m_filterProps = new SGPropertyNode;
|
|
|
|
int index = 0;
|
|
|
|
Q_FOREACH(QString term, s.split(' ')) {
|
|
|
|
m_filterProps->getNode("all-of/text", index++, true)->setStringValue(term.toStdString());
|
|
|
|
}
|
|
|
|
|
2016-11-13 21:51:35 +00:00
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
public slots:
|
|
|
|
void setRatingFilterEnabled(bool e)
|
|
|
|
{
|
|
|
|
if (e == m_ratingsFilter) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ratingsFilter = e;
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2016-01-10 18:56:46 +00:00
|
|
|
void setInstalledFilterEnabled(bool e)
|
|
|
|
{
|
|
|
|
if (e == m_onlyShowInstalled) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_onlyShowInstalled = e;
|
|
|
|
invalidate();
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
protected:
|
|
|
|
bool filterAcceptsRow(int sourceRow, const QModelIndex &sourceParent) const
|
|
|
|
{
|
2016-04-15 16:06:53 +00:00
|
|
|
QModelIndex index = sourceModel()->index(sourceRow, 0, sourceParent);
|
|
|
|
QVariant v = index.data(AircraftPackageStatusRole);
|
|
|
|
AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
|
2016-11-07 19:49:33 +00:00
|
|
|
if (status == MessageWidget) {
|
2016-04-15 16:06:53 +00:00
|
|
|
return true;
|
|
|
|
}
|
2016-07-08 08:36:42 +00:00
|
|
|
|
2016-11-13 21:51:35 +00:00
|
|
|
if (!filterAircraft(index)) {
|
2014-12-26 12:20:51 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2016-01-10 18:56:46 +00:00
|
|
|
if (m_onlyShowInstalled) {
|
|
|
|
QVariant v = index.data(AircraftPackageStatusRole);
|
|
|
|
AircraftItemStatus status = static_cast<AircraftItemStatus>(v.toInt());
|
|
|
|
if (status == PackageNotInstalled) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-01-10 19:47:57 +00:00
|
|
|
if (!m_onlyShowInstalled && m_ratingsFilter) {
|
2014-12-26 12:20:51 +00:00
|
|
|
for (int i=0; i<4; ++i) {
|
|
|
|
if (m_ratings[i] > index.data(AircraftRatingRole + i).toInt()) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private:
|
2016-11-13 21:51:35 +00:00
|
|
|
bool filterAircraft(const QModelIndex& sourceIndex) const
|
|
|
|
{
|
|
|
|
if (m_filterString.isEmpty()) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2016-11-30 21:33:16 +00:00
|
|
|
simgear::pkg::PackageRef pkg = sourceIndex.data(AircraftPackageRefRole).value<simgear::pkg::PackageRef>();
|
|
|
|
if (pkg) {
|
|
|
|
return pkg->matches(m_filterProps.ptr());
|
|
|
|
}
|
|
|
|
|
2016-11-13 21:51:35 +00:00
|
|
|
QString baseName = sourceIndex.data(Qt::DisplayRole).toString();
|
|
|
|
if (baseName.contains(m_filterString, Qt::CaseInsensitive)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString longDesc = sourceIndex.data(AircraftLongDescriptionRole).toString();
|
|
|
|
if (longDesc.contains(m_filterString, Qt::CaseInsensitive)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
const int variantCount = sourceIndex.data(AircraftVariantCountRole).toInt();
|
|
|
|
for (int variant = 0; variant < variantCount; ++variant) {
|
|
|
|
QString desc = sourceIndex.data(AircraftVariantDescriptionRole + variant).toString();
|
|
|
|
if (desc.contains(m_filterString, Qt::CaseInsensitive)) {
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
bool m_ratingsFilter;
|
2016-01-10 18:56:46 +00:00
|
|
|
bool m_onlyShowInstalled;
|
2014-12-26 12:20:51 +00:00
|
|
|
int m_ratings[4];
|
2016-11-13 21:51:35 +00:00
|
|
|
QString m_filterString;
|
2016-11-30 21:33:16 +00:00
|
|
|
SGPropertyNode_ptr m_filterProps;
|
2014-12-26 12:20:51 +00:00
|
|
|
};
|
|
|
|
|
2016-04-15 16:06:53 +00:00
|
|
|
class NoOfficialHangarMessage : public QWidget
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
NoOfficialHangarMessage() :
|
|
|
|
m_ui(new Ui::NoOfficialHangarMessage)
|
|
|
|
{
|
|
|
|
m_ui->setupUi(this);
|
|
|
|
// proxy this signal upwards
|
|
|
|
connect(m_ui->label, &QLabel::linkActivated,
|
|
|
|
this, &NoOfficialHangarMessage::linkActivated);
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_SIGNALS:
|
|
|
|
void linkActivated(QUrl link);
|
|
|
|
|
|
|
|
private:
|
|
|
|
Ui::NoOfficialHangarMessage* m_ui;
|
|
|
|
};
|
|
|
|
|
2016-11-07 19:49:33 +00:00
|
|
|
class UpdateAllAircraftMessage : public QWidget
|
|
|
|
{
|
|
|
|
Q_OBJECT
|
|
|
|
public:
|
|
|
|
UpdateAllAircraftMessage() :
|
|
|
|
m_ui(new Ui::UpdateAllAircraftMessage)
|
|
|
|
{
|
|
|
|
m_ui->setupUi(this);
|
|
|
|
// proxy this signal upwards
|
|
|
|
connect(m_ui->label, &QLabel::linkActivated, this, &UpdateAllAircraftMessage::linkActivated);
|
|
|
|
connect(m_ui->updateAllButton, &QPushButton::clicked, this, &UpdateAllAircraftMessage::updateAll);
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_SIGNALS:
|
|
|
|
void linkActivated(QUrl link);
|
|
|
|
void updateAll();
|
|
|
|
private:
|
|
|
|
Ui::UpdateAllAircraftMessage* m_ui;
|
|
|
|
};
|
|
|
|
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
static void initQtResources()
|
|
|
|
{
|
|
|
|
Q_INIT_RESOURCE(resources);
|
|
|
|
}
|
|
|
|
|
|
|
|
namespace flightgear
|
|
|
|
{
|
|
|
|
|
|
|
|
void initApp(int& argc, char** argv)
|
|
|
|
{
|
|
|
|
static bool qtInitDone = false;
|
2015-11-26 16:49:55 +00:00
|
|
|
static int s_argc;
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
if (!qtInitDone) {
|
|
|
|
qtInitDone = true;
|
2016-02-02 20:44:46 +00:00
|
|
|
|
|
|
|
sglog().setLogLevels( SG_ALL, SG_INFO );
|
|
|
|
initQtResources(); // can't be called from a namespace
|
|
|
|
|
2015-11-26 16:49:55 +00:00
|
|
|
s_argc = argc; // QApplication only stores a reference to argc,
|
|
|
|
// and may crash if it is freed
|
|
|
|
// http://doc.qt.io/qt-5/qguiapplication.html#QGuiApplication
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2015-11-26 16:49:55 +00:00
|
|
|
QApplication* app = new QApplication(s_argc, argv);
|
2015-10-16 01:05:17 +00:00
|
|
|
app->setOrganizationName("FlightGear");
|
|
|
|
app->setApplicationName("FlightGear");
|
|
|
|
app->setOrganizationDomain("flightgear.org");
|
|
|
|
|
2016-05-21 13:00:29 +00:00
|
|
|
QSettings::setDefaultFormat(QSettings::IniFormat);
|
|
|
|
QSettings::setPath(QSettings::IniFormat, QSettings::UserScope,
|
2016-06-21 11:29:04 +00:00
|
|
|
QString::fromStdString(globals->get_fg_home().utf8Str()));
|
2016-05-21 13:00:29 +00:00
|
|
|
|
2016-02-02 20:44:46 +00:00
|
|
|
// reset numeric / collation locales as described at:
|
|
|
|
// http://doc.qt.io/qt-5/qcoreapplication.html#details
|
|
|
|
::setlocale(LC_NUMERIC, "C");
|
|
|
|
::setlocale(LC_COLLATE, "C");
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
Qt::KeyboardModifiers mods = app->queryKeyboardModifiers();
|
2016-01-17 19:10:22 +00:00
|
|
|
if (mods & (Qt::AltModifier | Qt::ShiftModifier)) {
|
|
|
|
qWarning() << "Alt/shift pressed during launch";
|
2015-10-16 01:05:17 +00:00
|
|
|
QSettings settings;
|
2016-01-17 19:10:22 +00:00
|
|
|
settings.setValue("fg-root", "!ask");
|
2015-10-16 01:05:17 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-26 16:48:23 +00:00
|
|
|
bool runLauncherDialog()
|
|
|
|
{
|
Initial support for NavData/<type>/*.dat[.gz] files in scenery paths
Load every file matching the pattern NavData/apt/*.dat[.gz] inside each
scenery path. These files are loaded in the same order as the components
of globals->get_unmangled_fg_scenery() they reside in. Inside a given
component, the order is determined by pathSortPredicate() in
simgear/misc/sg_dir.cxx (lexicographic order at the time of this
writing). For compatibility with existing scenery,
$FG_ROOT/Airports/apt.dat.gz is also loaded last.
The idea is that such files will have the same precedence order as the
globals->get_unmangled_fg_scenery() scenery components they come from.
This commit doesn't handle this fully yet, though: it blindly loads all
these files. A future commit will ensure that no airport is loaded twice
due to overlapping apt.dat files. This commit however handles all the
logic of navdata cache rebuilding when the list, the order of apt.dat
files, or any of their timestamps changes.
Although only apt.dat files receive a new treatment in this commit, the
changes to NavDataCache.[ch]xx are already generic so that extension of
this method to fix.dat, nav.dat, etc. will require almost no change to
NavDataCache.[ch]xx (however, changes will probably be needed in the
various loaders: in fixlist.[ch]xx, navdb.[ch]xx, etc.).
src/Navaids/CacheSchema.h:
- increment the SCHEMA_VERSION by 1. This ensures among others that if
someone uses a FlightGear version posterior to this change with
new-style scenery (having NavData/apt/*.dat[.gz] files inside
scenery paths), then goes back to a FlightGear version anterior to
this change, his NavCache is rebuilt ignoring the in-scenery-paths
NavData/apt/*.dat[.gz] files, as expected with the old FlightGear
version.
src/Navaids/NavDataCache.cxx:
- NavDataCachePrivate: replace aptDatPath (SGPath) with aptDatPaths
(PathList).
- NavDataCachePrivate::getDatFilesPaths(): new method that returns the
list of $scenery_path/NavData/<type>/*.dat[.gz] files found inside
scenery paths (where <type> is one of 'apt', 'fix', etc.), plus the
historical file (e.g., $FG_ROOT/Airports/apt.dat.gz for the 'apt'
type).
- NavDataCachePrivate::areDatFilesModified(): new method that tells
whether any of these files (for a given type) has changed since the
last NavCache rebuild, or if their ordered list has changed.
- NavDataCachePrivate::isCachedFileModified(): minor changes.
- NavDataCache::updateListsOfDatFiles(): new method that updates the
lists of dat files used for NavCache freshness checking and
rebuilding, i.e. currently sets/updates d->aptDatPaths using the new
method d->getDatFilesPaths(), and d->metarDatPath, d->navDatPath,
d->fixDatPath, d->poiDatPath, etc. as usual. This method will be
useful for instance in the built-in launcher after updating scenery
paths and before calling NavDataCache::isRebuildRequired().
- NavDataCache::NavDataCache(): use
NavDataCache::updateListsOfDatFiles() to initialize d->aptDatPaths,
d->metarDatPath, d->navDatPath, d->fixDatPath, d->poiDatPath, etc.
- NavDataCache::isRebuildRequired(): use
NavDataCachePrivate::areDatFilesModified() instead of just checking
$FG_ROOT/Airports/apt.dat.gz.
- NavDataCache::doRebuild(): load all apt.dat files listed in
d->aptDatPaths, instead of only $FG_ROOT/Airports/apt.dat.gz. Write
their ordered list and timestamps in the NavCache.
src/Navaids/NavDataCache.hxx:
- declare the new method NavDataCache::updateListsOfDatFiles().
- NavDataCache::DatFileType: new enum with values DATFILETYPE_APT,
DATFILETYPE_METAR, DATFILETYPE_AWY, DATFILETYPE_NAV,
DATFILETYPE_FIX, DATFILETYPE_POI, DATFILETYPE_CARRIER and
DATFILETYPE_TACAN_FREQ. Maybe some of the corresponding files won't
have to be moved to scenery paths, but simply listing them in the
enum doesn't change how they are dealt with. Those for which
per-scenery-path locations doesn't make sense can just be removed
from the enum.
- NavDataCache::datTypeStr: new static string_list giving an
std::string such as 'apt' for each value of the
NavDataCache::DatFileType enum.
- NavDataCache::defaultDatFile: new static string_list giving a path
(relative to $FG_ROOT) to the historical/default file for each value
of the NavDataCache::DatFileType enum.
src/Airports/apt_loader.cxx and src/Airports/apt_loader.hxx:
- always include a path to the apt.dat file being processed in log
messages, since they can now apply to many files;
- be clearer about code 99: it should normally be at the end of
apt.dat files, but technically, it is not an EOF;
- use the expression "row code" consistently with the apt.dat format
spec (for now: only in places where there is another change to do).
src/GUI/QtLauncher.cxx and src/GUI/QtLauncher_private.hxx:
- turn QtLauncher::setSceneryPaths() into a static method and call it
in runLauncherDialog() before instantiating NavDataCache, so that
NavDataCache::updateListsOfDatFiles() (called from NavDataCache's
constructor) can see all configured scenery paths.
2016-10-11 14:31:13 +00:00
|
|
|
// Used for NavDataCache initialization: needed to find the apt.dat files
|
|
|
|
QtLauncher::setSceneryPaths();
|
2015-10-16 01:05:17 +00:00
|
|
|
// startup the nav-cache now. This pre-empts normal startup of
|
|
|
|
// the cache, but no harm done. (Providing scenery paths are consistent)
|
|
|
|
|
|
|
|
initNavCache();
|
|
|
|
|
2016-03-24 15:10:06 +00:00
|
|
|
QSettings settings;
|
|
|
|
QString downloadDir = settings.value("download-dir").toString();
|
|
|
|
if (!downloadDir.isEmpty()) {
|
|
|
|
flightgear::Options::sharedInstance()->setOption("download-dir", downloadDir.toStdString());
|
|
|
|
}
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
fgInitPackageRoot();
|
|
|
|
|
|
|
|
// startup the HTTP system now since packages needs it
|
2016-03-24 15:08:38 +00:00
|
|
|
FGHTTPClient* http = globals->add_new_subsystem<FGHTTPClient>();
|
2016-07-08 08:36:42 +00:00
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
// we guard against re-init in the global phase; bind and postinit
|
|
|
|
// will happen as normal
|
|
|
|
http->init();
|
|
|
|
|
2016-08-08 22:13:57 +00:00
|
|
|
NaturalEarthDataLoaderThread* naturalEarthLoader = new NaturalEarthDataLoaderThread;
|
|
|
|
naturalEarthLoader->start();
|
2015-11-26 16:48:23 +00:00
|
|
|
|
2016-03-19 14:46:11 +00:00
|
|
|
// avoid double Apple menu and other weirdness if both Qt and OSG
|
|
|
|
// try to initialise various Cocoa structures.
|
|
|
|
flightgear::WindowBuilder::setPoseAsStandaloneApp(false);
|
2016-04-14 09:07:41 +00:00
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
QtLauncher dlg;
|
2016-01-17 19:10:22 +00:00
|
|
|
dlg.show();
|
2016-01-10 22:38:01 +00:00
|
|
|
|
2016-01-17 19:10:22 +00:00
|
|
|
int appResult = qApp->exec();
|
2016-09-08 10:41:21 +00:00
|
|
|
if (appResult <= 0) {
|
2016-01-17 19:10:22 +00:00
|
|
|
return false; // quit
|
2015-10-16 01:05:17 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 22:38:01 +00:00
|
|
|
// don't set scenery paths twice
|
|
|
|
globals->clear_fg_scenery();
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool runInAppLauncherDialog()
|
|
|
|
{
|
|
|
|
QtLauncher dlg;
|
|
|
|
dlg.setInAppMode();
|
|
|
|
dlg.exec();
|
|
|
|
if (dlg.result() != QDialog::Accepted) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
} // of namespace flightgear
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
QtLauncher::QtLauncher() :
|
|
|
|
QDialog(),
|
2015-10-16 01:05:17 +00:00
|
|
|
m_ui(NULL),
|
|
|
|
m_subsystemIdleTimer(NULL),
|
2016-07-08 08:36:42 +00:00
|
|
|
m_inAppMode(false),
|
|
|
|
m_doRestoreMPServer(false)
|
2014-12-26 12:20:51 +00:00
|
|
|
{
|
|
|
|
m_ui.reset(new Ui::Launcher);
|
|
|
|
m_ui->setupUi(this);
|
|
|
|
|
2015-02-25 23:14:02 +00:00
|
|
|
#if QT_VERSION >= 0x050300
|
|
|
|
// don't require Qt 5.3
|
|
|
|
m_ui->commandLineArgs->setPlaceholderText("--option=value --prop:/sim/name=value");
|
|
|
|
#endif
|
2015-02-26 23:29:35 +00:00
|
|
|
|
|
|
|
#if QT_VERSION >= 0x050200
|
|
|
|
m_ui->aircraftFilter->setClearButtonEnabled(true);
|
|
|
|
#endif
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
for (int i=0; i<4; ++i) {
|
|
|
|
m_ratingFilters[i] = 3;
|
|
|
|
}
|
|
|
|
|
2015-03-10 08:30:55 +00:00
|
|
|
m_subsystemIdleTimer = new QTimer(this);
|
|
|
|
m_subsystemIdleTimer->setInterval(0);
|
|
|
|
connect(m_subsystemIdleTimer, &QTimer::timeout,
|
|
|
|
this, &QtLauncher::onSubsytemIdleTimeout);
|
|
|
|
m_subsystemIdleTimer->start();
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
// create and configure the proxy model
|
|
|
|
m_aircraftProxy = new AircraftProxyModel(this);
|
2015-03-17 22:10:49 +00:00
|
|
|
connect(m_ui->ratingsFilterCheck, &QAbstractButton::toggled,
|
|
|
|
m_aircraftProxy, &AircraftProxyModel::setRatingFilterEnabled);
|
2016-01-10 19:47:57 +00:00
|
|
|
connect(m_ui->ratingsFilterCheck, &QAbstractButton::toggled,
|
|
|
|
this, &QtLauncher::maybeRestoreAircraftSelection);
|
|
|
|
|
2016-01-10 18:56:46 +00:00
|
|
|
connect(m_ui->onlyShowInstalledCheck, &QAbstractButton::toggled,
|
|
|
|
m_aircraftProxy, &AircraftProxyModel::setInstalledFilterEnabled);
|
2015-03-17 22:10:49 +00:00
|
|
|
connect(m_ui->aircraftFilter, &QLineEdit::textChanged,
|
2016-11-13 21:51:35 +00:00
|
|
|
m_aircraftProxy, &AircraftProxyModel::setAircraftFilterString);
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
connect(m_ui->runButton, SIGNAL(clicked()), this, SLOT(onRun()));
|
|
|
|
connect(m_ui->quitButton, SIGNAL(clicked()), this, SLOT(onQuit()));
|
|
|
|
|
|
|
|
connect(m_ui->aircraftHistory, &QPushButton::clicked,
|
|
|
|
this, &QtLauncher::onPopupAircraftHistory);
|
2016-11-07 11:54:38 +00:00
|
|
|
connect(m_ui->locationHistory, &QPushButton::clicked,
|
|
|
|
this, &QtLauncher::onPopupLocationHistory);
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
connect(m_ui->location, &LocationWidget::descriptionChanged,
|
|
|
|
m_ui->locationDescription, &QLabel::setText);
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
QAction* qa = new QAction(this);
|
|
|
|
qa->setShortcut(QKeySequence("Ctrl+Q"));
|
|
|
|
connect(qa, &QAction::triggered, this, &QtLauncher::onQuit);
|
|
|
|
addAction(qa);
|
|
|
|
|
|
|
|
connect(m_ui->editRatingFilter, &QPushButton::clicked,
|
|
|
|
this, &QtLauncher::onEditRatingsFilter);
|
2016-01-10 18:56:46 +00:00
|
|
|
connect(m_ui->onlyShowInstalledCheck, &QCheckBox::toggled,
|
|
|
|
this, &QtLauncher::onShowInstalledAircraftToggled);
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
QIcon historyIcon(":/history-icon");
|
|
|
|
m_ui->aircraftHistory->setIcon(historyIcon);
|
2016-11-07 11:54:38 +00:00
|
|
|
m_ui->locationHistory->setIcon(historyIcon);
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
connect(m_ui->timeOfDayCombo, SIGNAL(currentIndexChanged(int)),
|
|
|
|
this, SLOT(updateSettingsSummary()));
|
2015-01-10 14:57:59 +00:00
|
|
|
connect(m_ui->seasonCombo, SIGNAL(currentIndexChanged(int)),
|
|
|
|
this, SLOT(updateSettingsSummary()));
|
2014-12-26 12:20:51 +00:00
|
|
|
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()));
|
2015-01-07 11:30:44 +00:00
|
|
|
connect(m_ui->msaaCheckbox, SIGNAL(toggled(bool)),
|
|
|
|
this, SLOT(updateSettingsSummary()));
|
|
|
|
|
2016-11-07 12:00:57 +00:00
|
|
|
connect(m_ui->mpBox, SIGNAL(toggled(bool)),
|
|
|
|
this, SLOT(updateSettingsSummary()));
|
|
|
|
connect(m_ui->mpCallsign, SIGNAL(textChanged(QString)),
|
|
|
|
this, SLOT(updateSettingsSummary()));
|
|
|
|
|
2015-01-07 11:30:44 +00:00
|
|
|
connect(m_ui->rembrandtCheckbox, SIGNAL(toggled(bool)),
|
|
|
|
this, SLOT(onRembrandtToggled(bool)));
|
2015-03-17 22:10:49 +00:00
|
|
|
connect(m_ui->terrasyncCheck, &QCheckBox::toggled,
|
|
|
|
this, &QtLauncher::onToggleTerrasync);
|
2014-12-26 12:20:51 +00:00
|
|
|
updateSettingsSummary();
|
|
|
|
|
2016-07-08 08:36:42 +00:00
|
|
|
connect(m_ui->mpServerCombo, SIGNAL(activated(int)),
|
|
|
|
this, SLOT(onMPServerActivated(int)));
|
|
|
|
|
2016-03-24 15:10:06 +00:00
|
|
|
m_aircraftModel = new AircraftItemModel(this);
|
2015-03-17 22:10:49 +00:00
|
|
|
m_aircraftProxy->setSourceModel(m_aircraftModel);
|
|
|
|
|
|
|
|
m_aircraftProxy->setFilterCaseSensitivity(Qt::CaseInsensitive);
|
|
|
|
m_aircraftProxy->setSortCaseSensitivity(Qt::CaseInsensitive);
|
|
|
|
m_aircraftProxy->setSortRole(Qt::DisplayRole);
|
|
|
|
m_aircraftProxy->setDynamicSortFilter(true);
|
|
|
|
|
|
|
|
m_ui->aircraftList->setModel(m_aircraftProxy);
|
|
|
|
m_ui->aircraftList->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
|
|
|
AircraftItemDelegate* delegate = new AircraftItemDelegate(m_ui->aircraftList);
|
|
|
|
m_ui->aircraftList->setItemDelegate(delegate);
|
|
|
|
m_ui->aircraftList->setSelectionMode(QAbstractItemView::SingleSelection);
|
|
|
|
connect(m_ui->aircraftList, &QListView::clicked,
|
|
|
|
this, &QtLauncher::onAircraftSelected);
|
|
|
|
connect(delegate, &AircraftItemDelegate::variantChanged,
|
|
|
|
this, &QtLauncher::onAircraftSelected);
|
2015-08-03 20:53:56 +00:00
|
|
|
connect(delegate, &AircraftItemDelegate::requestInstall,
|
|
|
|
this, &QtLauncher::onRequestPackageInstall);
|
2016-11-06 20:50:34 +00:00
|
|
|
connect(delegate, &AircraftItemDelegate::requestUninstall,
|
|
|
|
this, &QtLauncher::onRequestPackageUninstall);
|
2015-08-03 20:53:56 +00:00
|
|
|
connect(delegate, &AircraftItemDelegate::cancelDownload,
|
|
|
|
this, &QtLauncher::onCancelDownload);
|
|
|
|
|
|
|
|
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallCompleted,
|
|
|
|
this, &QtLauncher::onAircraftInstalledCompleted);
|
|
|
|
connect(m_aircraftModel, &AircraftItemModel::aircraftInstallFailed,
|
|
|
|
this, &QtLauncher::onAircraftInstallFailed);
|
2015-11-12 00:11:12 +00:00
|
|
|
connect(m_aircraftModel, &AircraftItemModel::scanCompleted,
|
|
|
|
this, &QtLauncher::updateSelectedAircraft);
|
2016-04-08 09:26:19 +00:00
|
|
|
connect(m_ui->restoreDefaultsButton, &QPushButton::clicked,
|
|
|
|
this, &QtLauncher::onRestoreDefaults);
|
|
|
|
|
2015-03-12 22:43:58 +00:00
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
AddOnsPage* addOnsPage = new AddOnsPage(NULL, globals->packageRoot());
|
|
|
|
connect(addOnsPage, &AddOnsPage::downloadDirChanged,
|
|
|
|
this, &QtLauncher::onDownloadDirChanged);
|
|
|
|
connect(addOnsPage, &AddOnsPage::sceneryPathsChanged,
|
|
|
|
this, &QtLauncher::setSceneryPaths);
|
|
|
|
|
|
|
|
m_ui->tabWidget->addTab(addOnsPage, tr("Add-ons"));
|
2016-01-10 19:47:57 +00:00
|
|
|
// 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,
|
|
|
|
// unfortunately.
|
|
|
|
connect(m_aircraftProxy, &AircraftProxyModel::modelReset,
|
|
|
|
this, &QtLauncher::delayedAircraftModelReset);
|
|
|
|
|
2015-03-11 23:20:18 +00:00
|
|
|
QSettings settings;
|
|
|
|
m_aircraftModel->setPaths(settings.value("aircraft-paths").toStringList());
|
2016-03-24 15:10:06 +00:00
|
|
|
m_aircraftModel->setPackageRoot(globals->packageRoot());
|
2015-03-11 23:20:18 +00:00
|
|
|
m_aircraftModel->scanDirs();
|
2016-03-24 15:10:06 +00:00
|
|
|
|
2016-04-15 16:06:53 +00:00
|
|
|
checkOfficialCatalogMessage();
|
2016-03-24 15:10:06 +00:00
|
|
|
restoreSettings();
|
2016-07-08 08:36:42 +00:00
|
|
|
|
|
|
|
onRefreshMPServers();
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QtLauncher::~QtLauncher()
|
|
|
|
{
|
2016-09-14 11:59:20 +00:00
|
|
|
// 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<FGHTTPClient>()->client()->cancelRequest(m_mpServerRequest);
|
2015-10-16 01:05:17 +00:00
|
|
|
}
|
|
|
|
|
Initial support for NavData/<type>/*.dat[.gz] files in scenery paths
Load every file matching the pattern NavData/apt/*.dat[.gz] inside each
scenery path. These files are loaded in the same order as the components
of globals->get_unmangled_fg_scenery() they reside in. Inside a given
component, the order is determined by pathSortPredicate() in
simgear/misc/sg_dir.cxx (lexicographic order at the time of this
writing). For compatibility with existing scenery,
$FG_ROOT/Airports/apt.dat.gz is also loaded last.
The idea is that such files will have the same precedence order as the
globals->get_unmangled_fg_scenery() scenery components they come from.
This commit doesn't handle this fully yet, though: it blindly loads all
these files. A future commit will ensure that no airport is loaded twice
due to overlapping apt.dat files. This commit however handles all the
logic of navdata cache rebuilding when the list, the order of apt.dat
files, or any of their timestamps changes.
Although only apt.dat files receive a new treatment in this commit, the
changes to NavDataCache.[ch]xx are already generic so that extension of
this method to fix.dat, nav.dat, etc. will require almost no change to
NavDataCache.[ch]xx (however, changes will probably be needed in the
various loaders: in fixlist.[ch]xx, navdb.[ch]xx, etc.).
src/Navaids/CacheSchema.h:
- increment the SCHEMA_VERSION by 1. This ensures among others that if
someone uses a FlightGear version posterior to this change with
new-style scenery (having NavData/apt/*.dat[.gz] files inside
scenery paths), then goes back to a FlightGear version anterior to
this change, his NavCache is rebuilt ignoring the in-scenery-paths
NavData/apt/*.dat[.gz] files, as expected with the old FlightGear
version.
src/Navaids/NavDataCache.cxx:
- NavDataCachePrivate: replace aptDatPath (SGPath) with aptDatPaths
(PathList).
- NavDataCachePrivate::getDatFilesPaths(): new method that returns the
list of $scenery_path/NavData/<type>/*.dat[.gz] files found inside
scenery paths (where <type> is one of 'apt', 'fix', etc.), plus the
historical file (e.g., $FG_ROOT/Airports/apt.dat.gz for the 'apt'
type).
- NavDataCachePrivate::areDatFilesModified(): new method that tells
whether any of these files (for a given type) has changed since the
last NavCache rebuild, or if their ordered list has changed.
- NavDataCachePrivate::isCachedFileModified(): minor changes.
- NavDataCache::updateListsOfDatFiles(): new method that updates the
lists of dat files used for NavCache freshness checking and
rebuilding, i.e. currently sets/updates d->aptDatPaths using the new
method d->getDatFilesPaths(), and d->metarDatPath, d->navDatPath,
d->fixDatPath, d->poiDatPath, etc. as usual. This method will be
useful for instance in the built-in launcher after updating scenery
paths and before calling NavDataCache::isRebuildRequired().
- NavDataCache::NavDataCache(): use
NavDataCache::updateListsOfDatFiles() to initialize d->aptDatPaths,
d->metarDatPath, d->navDatPath, d->fixDatPath, d->poiDatPath, etc.
- NavDataCache::isRebuildRequired(): use
NavDataCachePrivate::areDatFilesModified() instead of just checking
$FG_ROOT/Airports/apt.dat.gz.
- NavDataCache::doRebuild(): load all apt.dat files listed in
d->aptDatPaths, instead of only $FG_ROOT/Airports/apt.dat.gz. Write
their ordered list and timestamps in the NavCache.
src/Navaids/NavDataCache.hxx:
- declare the new method NavDataCache::updateListsOfDatFiles().
- NavDataCache::DatFileType: new enum with values DATFILETYPE_APT,
DATFILETYPE_METAR, DATFILETYPE_AWY, DATFILETYPE_NAV,
DATFILETYPE_FIX, DATFILETYPE_POI, DATFILETYPE_CARRIER and
DATFILETYPE_TACAN_FREQ. Maybe some of the corresponding files won't
have to be moved to scenery paths, but simply listing them in the
enum doesn't change how they are dealt with. Those for which
per-scenery-path locations doesn't make sense can just be removed
from the enum.
- NavDataCache::datTypeStr: new static string_list giving an
std::string such as 'apt' for each value of the
NavDataCache::DatFileType enum.
- NavDataCache::defaultDatFile: new static string_list giving a path
(relative to $FG_ROOT) to the historical/default file for each value
of the NavDataCache::DatFileType enum.
src/Airports/apt_loader.cxx and src/Airports/apt_loader.hxx:
- always include a path to the apt.dat file being processed in log
messages, since they can now apply to many files;
- be clearer about code 99: it should normally be at the end of
apt.dat files, but technically, it is not an EOF;
- use the expression "row code" consistently with the apt.dat format
spec (for now: only in places where there is another change to do).
src/GUI/QtLauncher.cxx and src/GUI/QtLauncher_private.hxx:
- turn QtLauncher::setSceneryPaths() into a static method and call it
in runLauncherDialog() before instantiating NavDataCache, so that
NavDataCache::updateListsOfDatFiles() (called from NavDataCache's
constructor) can see all configured scenery paths.
2016-10-11 14:31:13 +00:00
|
|
|
void QtLauncher::setSceneryPaths() // static method
|
2016-01-10 22:38:01 +00:00
|
|
|
{
|
|
|
|
globals->clear_fg_scenery();
|
|
|
|
|
|
|
|
// mimic what optionss.cxx does, so we can find airport data for parking
|
|
|
|
// positions
|
|
|
|
QSettings settings;
|
|
|
|
// append explicit scenery paths
|
|
|
|
Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) {
|
|
|
|
globals->append_fg_scenery(path.toStdString());
|
|
|
|
}
|
|
|
|
|
|
|
|
// append the TerraSync path
|
|
|
|
QString downloadDir = settings.value("download-dir").toString();
|
|
|
|
if (downloadDir.isEmpty()) {
|
2016-06-23 13:26:34 +00:00
|
|
|
downloadDir = QString::fromStdString(flightgear::defaultDownloadDir().utf8Str());
|
2016-01-10 22:38:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
SGPath terraSyncDir(downloadDir.toStdString());
|
|
|
|
terraSyncDir.append("TerraSync");
|
|
|
|
if (terraSyncDir.exists()) {
|
2016-06-21 11:29:04 +00:00
|
|
|
globals->append_fg_scenery(terraSyncDir);
|
2016-01-10 22:38:01 +00:00
|
|
|
}
|
|
|
|
|
2016-08-19 11:12:02 +00:00
|
|
|
// add the installation path since it contains default airport data,
|
|
|
|
// if terrasync is disabled or on first-launch
|
|
|
|
globals->append_fg_scenery(globals->get_fg_root() / "Scenery");
|
|
|
|
|
2016-01-10 22:38:01 +00:00
|
|
|
}
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
void QtLauncher::setInAppMode()
|
|
|
|
{
|
|
|
|
m_inAppMode = true;
|
2016-08-19 15:09:52 +00:00
|
|
|
m_ui->tabWidget->removeTab(3);
|
|
|
|
m_ui->tabWidget->removeTab(3);
|
2016-06-08 11:00:35 +00:00
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
m_ui->runButton->setText(tr("Apply"));
|
|
|
|
m_ui->quitButton->setText(tr("Cancel"));
|
|
|
|
|
|
|
|
disconnect(m_ui->runButton, SIGNAL(clicked()), this, SLOT(onRun()));
|
|
|
|
connect(m_ui->runButton, SIGNAL(clicked()), this, SLOT(onApply()));
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::restoreSettings()
|
|
|
|
{
|
|
|
|
QSettings settings;
|
2016-10-24 11:27:49 +00:00
|
|
|
|
|
|
|
restoreGeometry(settings.value("window-geometry").toByteArray());
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
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());
|
2015-01-10 14:57:59 +00:00
|
|
|
m_ui->seasonCombo->setCurrentIndex(settings.value("season", 0).toInt());
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
// full paths to -set.xml files
|
2015-08-03 20:53:56 +00:00
|
|
|
m_recentAircraft = QUrl::fromStringList(settings.value("recent-aircraft").toStringList());
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
if (!m_recentAircraft.empty()) {
|
|
|
|
m_selectedAircraft = m_recentAircraft.front();
|
|
|
|
} else {
|
|
|
|
// select the default C172p
|
|
|
|
}
|
|
|
|
|
2016-01-10 22:38:01 +00:00
|
|
|
if (!m_inAppMode) {
|
|
|
|
setSceneryPaths();
|
|
|
|
}
|
|
|
|
|
2015-11-03 21:28:36 +00:00
|
|
|
m_ui->location->restoreSettings();
|
2016-11-07 11:54:38 +00:00
|
|
|
m_recentLocations = settings.value("recent-location-sets").toList();
|
|
|
|
if (!m_recentLocations.isEmpty()) {
|
|
|
|
m_ui->location->restoreLocation(m_recentLocations.front().toMap());
|
|
|
|
}
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
// rating filters
|
2016-01-10 18:56:46 +00:00
|
|
|
m_ui->onlyShowInstalledCheck->setChecked(settings.value("only-show-installed", false).toBool());
|
|
|
|
if (m_ui->onlyShowInstalledCheck->isChecked()) {
|
|
|
|
m_ui->ratingsFilterCheck->setEnabled(false);
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
m_ui->ratingsFilterCheck->setChecked(settings.value("ratings-filter", true).toBool());
|
|
|
|
int index = 0;
|
|
|
|
Q_FOREACH(QVariant v, settings.value("min-ratings").toList()) {
|
|
|
|
m_ratingFilters[index++] = v.toInt();
|
|
|
|
}
|
|
|
|
|
|
|
|
m_aircraftProxy->setRatingFilterEnabled(m_ui->ratingsFilterCheck->isChecked());
|
|
|
|
m_aircraftProxy->setRatings(m_ratingFilters);
|
|
|
|
|
2016-01-10 19:47:57 +00:00
|
|
|
updateSelectedAircraft();
|
|
|
|
maybeRestoreAircraftSelection();
|
|
|
|
|
2015-02-23 13:41:30 +00:00
|
|
|
m_ui->commandLineArgs->setPlainText(settings.value("additional-args").toString());
|
2016-07-08 08:36:42 +00:00
|
|
|
|
2016-07-15 21:48:37 +00:00
|
|
|
m_ui->mpBox->setChecked(settings.value("mp-enabled").toBool());
|
2016-07-08 08:36:42 +00:00
|
|
|
m_ui->mpCallsign->setText(settings.value("mp-callsign").toString());
|
|
|
|
// don't restore MP server here, we do it after a refresh
|
|
|
|
m_doRestoreMPServer = true;
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 19:47:57 +00:00
|
|
|
void QtLauncher::delayedAircraftModelReset()
|
|
|
|
{
|
2016-01-12 23:25:54 +00:00
|
|
|
QTimer::singleShot(1, this, SLOT(maybeRestoreAircraftSelection()));
|
2016-01-10 19:47:57 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::maybeRestoreAircraftSelection()
|
|
|
|
{
|
|
|
|
QModelIndex aircraftIndex = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
|
|
|
|
QModelIndex proxyIndex = m_aircraftProxy->mapFromSource(aircraftIndex);
|
|
|
|
if (proxyIndex.isValid()) {
|
|
|
|
m_ui->aircraftList->selectionModel()->setCurrentIndex(proxyIndex,
|
|
|
|
QItemSelectionModel::ClearAndSelect);
|
|
|
|
m_ui->aircraftList->selectionModel()->select(proxyIndex,
|
|
|
|
QItemSelectionModel::ClearAndSelect);
|
|
|
|
m_ui->aircraftList->scrollTo(proxyIndex);
|
2016-11-18 16:10:02 +00:00
|
|
|
|
|
|
|
// and also select the correct variant on the model
|
|
|
|
m_aircraftModel->selectVariantForAircraftURI(m_selectedAircraft);
|
2016-01-10 19:47:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
void QtLauncher::saveSettings()
|
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
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());
|
|
|
|
settings.setValue("ratings-filter", m_ui->ratingsFilterCheck->isChecked());
|
2016-01-10 18:56:46 +00:00
|
|
|
settings.setValue("only-show-installed", m_ui->onlyShowInstalledCheck->isChecked());
|
2015-08-03 20:53:56 +00:00
|
|
|
settings.setValue("recent-aircraft", QUrl::toStringList(m_recentAircraft));
|
2016-11-07 11:54:38 +00:00
|
|
|
settings.setValue("recent-location-sets", m_recentLocations);
|
2015-10-25 00:40:41 +00:00
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
settings.setValue("timeofday", m_ui->timeOfDayCombo->currentIndex());
|
2015-01-10 14:57:59 +00:00
|
|
|
settings.setValue("season", m_ui->seasonCombo->currentIndex());
|
2015-02-23 13:41:30 +00:00
|
|
|
settings.setValue("additional-args", m_ui->commandLineArgs->toPlainText());
|
2015-11-03 21:28:36 +00:00
|
|
|
|
2016-07-08 08:36:42 +00:00
|
|
|
settings.setValue("mp-callsign", m_ui->mpCallsign->text());
|
|
|
|
settings.setValue("mp-server", m_ui->mpServerCombo->currentData());
|
2016-07-15 21:48:37 +00:00
|
|
|
settings.setValue("mp-enabled", m_ui->mpBox->isChecked());
|
2016-10-24 11:27:49 +00:00
|
|
|
|
|
|
|
settings.setValue("window-geometry", saveGeometry());
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::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, "");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-24 19:50:37 +00:00
|
|
|
void QtLauncher::closeEvent(QCloseEvent *event)
|
|
|
|
{
|
|
|
|
qApp->exit(-1);
|
|
|
|
}
|
|
|
|
|
2016-04-14 09:07:41 +00:00
|
|
|
void QtLauncher::reject()
|
|
|
|
{
|
|
|
|
qApp->exit(-1);
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
void QtLauncher::onRun()
|
|
|
|
{
|
|
|
|
flightgear::Options* opt = flightgear::Options::sharedInstance();
|
|
|
|
setEnableDisableOptionFromCheckbox(m_ui->terrasyncCheck, "terrasync");
|
|
|
|
setEnableDisableOptionFromCheckbox(m_ui->fetchRealWxrCheckbox, "real-weather-fetch");
|
|
|
|
setEnableDisableOptionFromCheckbox(m_ui->rembrandtCheckbox, "rembrandt");
|
|
|
|
setEnableDisableOptionFromCheckbox(m_ui->fullScreenCheckbox, "fullscreen");
|
2015-11-03 22:05:20 +00:00
|
|
|
// setEnableDisableOptionFromCheckbox(m_ui->startPausedCheck, "freeze");
|
|
|
|
|
|
|
|
bool startPaused = m_ui->startPausedCheck->isChecked() ||
|
|
|
|
m_ui->location->shouldStartPaused();
|
|
|
|
if (startPaused) {
|
|
|
|
opt->addOption("enable-freeze", "");
|
|
|
|
}
|
2014-12-26 12:20:51 +00:00
|
|
|
|
2015-01-07 11:30:44 +00:00
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
// aircraft
|
|
|
|
if (!m_selectedAircraft.isEmpty()) {
|
2015-08-03 20:53:56 +00:00
|
|
|
if (m_selectedAircraft.isLocalFile()) {
|
|
|
|
QFileInfo setFileInfo(m_selectedAircraft.toLocalFile());
|
|
|
|
opt->addOption("aircraft-dir", setFileInfo.dir().absolutePath().toStdString());
|
|
|
|
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());
|
|
|
|
} else if (m_selectedAircraft.scheme() == "package") {
|
2016-01-04 02:58:03 +00:00
|
|
|
QString qualifiedId = m_selectedAircraft.path();
|
2015-08-03 20:53:56 +00:00
|
|
|
// no need to set aircraft-dir, handled by the corresponding code
|
|
|
|
// in fgInitAircraft
|
2016-01-04 02:58:03 +00:00
|
|
|
opt->addOption("aircraft", qualifiedId.toStdString());
|
2015-08-03 20:53:56 +00:00
|
|
|
} else {
|
|
|
|
qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
|
|
|
|
}
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
// manage aircraft history
|
|
|
|
if (m_recentAircraft.contains(m_selectedAircraft))
|
|
|
|
m_recentAircraft.removeOne(m_selectedAircraft);
|
|
|
|
m_recentAircraft.prepend(m_selectedAircraft);
|
|
|
|
if (m_recentAircraft.size() > MAX_RECENT_AIRCRAFT)
|
|
|
|
m_recentAircraft.pop_back();
|
|
|
|
}
|
|
|
|
|
2016-07-08 08:36:42 +00:00
|
|
|
if (m_ui->mpBox->isChecked()) {
|
2016-08-31 22:07:21 +00:00
|
|
|
std::string callSign = m_ui->mpCallsign->text().toStdString();
|
|
|
|
if (!callSign.empty()) {
|
|
|
|
opt->addOption("callsign", callSign);
|
|
|
|
}
|
|
|
|
|
2016-07-08 08:36:42 +00:00
|
|
|
QString host = m_ui->mpServerCombo->currentData().toString();
|
2016-11-06 20:51:15 +00:00
|
|
|
int port = DEFAULT_MP_PORT;
|
2016-07-08 08:36:42 +00:00
|
|
|
if (host == "custom") {
|
|
|
|
QSettings settings;
|
|
|
|
host = settings.value("mp-custom-host").toString();
|
|
|
|
} else {
|
|
|
|
port = findMPServerPort(host.toStdString());
|
|
|
|
}
|
2016-11-06 20:51:15 +00:00
|
|
|
|
|
|
|
if (port == 0) {
|
|
|
|
port = DEFAULT_MP_PORT;
|
|
|
|
}
|
2016-07-08 08:36:42 +00:00
|
|
|
globals->get_props()->setStringValue("/sim/multiplay/txhost", host.toStdString());
|
|
|
|
globals->get_props()->setIntValue("/sim/multiplay/txport", port);
|
|
|
|
}
|
|
|
|
|
2016-08-19 15:09:52 +00:00
|
|
|
m_ui->location->setLocationProperties();
|
2016-11-07 11:54:38 +00:00
|
|
|
updateLocationHistory();
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
// time of day
|
|
|
|
if (m_ui->timeOfDayCombo->currentIndex() != 0) {
|
|
|
|
QString dayval = m_ui->timeOfDayCombo->currentText().toLower();
|
|
|
|
opt->addOption("timeofday", dayval.toStdString());
|
|
|
|
}
|
|
|
|
|
2015-01-10 14:57:59 +00:00
|
|
|
if (m_ui->seasonCombo->currentIndex() != 0) {
|
2016-03-22 10:56:05 +00:00
|
|
|
QString seasonName = m_ui->seasonCombo->currentText().toLower();
|
|
|
|
opt->addOption("season", seasonName.toStdString());
|
2015-01-10 14:57:59 +00:00
|
|
|
}
|
|
|
|
|
2015-03-25 14:16:09 +00:00
|
|
|
QSettings settings;
|
|
|
|
QString downloadDir = settings.value("download-dir").toString();
|
|
|
|
if (!downloadDir.isEmpty()) {
|
|
|
|
QDir d(downloadDir);
|
2015-03-17 22:10:49 +00:00
|
|
|
if (!d.exists()) {
|
|
|
|
int result = QMessageBox::question(this, tr("Create download folder?"),
|
|
|
|
tr("The selected location for downloads does not exist. Create it?"),
|
|
|
|
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
|
|
|
if (result == QMessageBox::Cancel) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (result == QMessageBox::Yes) {
|
2015-03-25 14:16:09 +00:00
|
|
|
d.mkpath(downloadDir);
|
2015-03-17 22:10:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
opt->addOption("download-dir", downloadDir.toStdString());
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
// scenery paths
|
2015-03-25 14:16:09 +00:00
|
|
|
Q_FOREACH(QString path, settings.value("scenery-paths").toStringList()) {
|
2014-12-26 12:20:51 +00:00
|
|
|
opt->addOption("fg-scenery", path.toStdString());
|
|
|
|
}
|
|
|
|
|
2015-03-11 23:20:18 +00:00
|
|
|
// aircraft paths
|
2015-03-25 14:16:09 +00:00
|
|
|
Q_FOREACH(QString path, settings.value("aircraft-paths").toStringList()) {
|
2015-04-03 13:13:13 +00:00
|
|
|
// can't use fg-aircraft for this, as it is processed before the launcher is run
|
|
|
|
globals->append_aircraft_path(path.toStdString());
|
2015-03-11 23:20:18 +00:00
|
|
|
}
|
|
|
|
|
2015-02-23 13:41:30 +00:00
|
|
|
// additional arguments
|
|
|
|
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());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
if (settings.contains("restore-defaults-on-run")) {
|
|
|
|
settings.remove("restore-defaults-on-run");
|
|
|
|
opt->addOption("restore-defaults", "");
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
saveSettings();
|
2016-01-17 19:10:22 +00:00
|
|
|
|
2016-09-08 10:41:21 +00:00
|
|
|
// set a positive value here so we can detect this case in runLauncherDialog
|
|
|
|
qApp->exit(1);
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
2015-10-25 00:40:41 +00:00
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
void QtLauncher::onApply()
|
|
|
|
{
|
|
|
|
accept();
|
|
|
|
|
|
|
|
// 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
|
2016-01-04 02:58:03 +00:00
|
|
|
aircraftPropValue = m_selectedAircraft.path().toStdString();
|
2015-10-16 01:05:17 +00:00
|
|
|
} else {
|
|
|
|
qWarning() << "unsupported aircraft launch URL" << m_selectedAircraft;
|
|
|
|
}
|
|
|
|
|
|
|
|
// manage aircraft history
|
|
|
|
if (m_recentAircraft.contains(m_selectedAircraft))
|
|
|
|
m_recentAircraft.removeOne(m_selectedAircraft);
|
|
|
|
m_recentAircraft.prepend(m_selectedAircraft);
|
|
|
|
if (m_recentAircraft.size() > MAX_RECENT_AIRCRAFT)
|
|
|
|
m_recentAircraft.pop_back();
|
|
|
|
|
|
|
|
globals->get_props()->setStringValue("/sim/aircraft", aircraftPropValue);
|
|
|
|
globals->get_props()->setStringValue("/sim/aircraft-dir", aircraftDir);
|
|
|
|
}
|
|
|
|
|
2016-08-19 15:09:52 +00:00
|
|
|
// location
|
|
|
|
m_ui->location->setLocationProperties();
|
2015-10-16 01:05:17 +00:00
|
|
|
|
|
|
|
saveSettings();
|
|
|
|
}
|
|
|
|
|
2016-11-07 11:54:38 +00:00
|
|
|
void QtLauncher::updateLocationHistory()
|
|
|
|
{
|
|
|
|
QVariant locSet = m_ui->location->saveLocation();
|
|
|
|
|
|
|
|
// check for existing; let's use description to imply uniqueness. This means
|
|
|
|
// 'A1111' parkings get merged but I prefer that to keep the menu usable
|
|
|
|
QVariant locDesc = locSet.toMap().value("text");
|
|
|
|
auto it = std::remove_if(m_recentLocations.begin(), m_recentLocations.end(),
|
|
|
|
[locDesc](QVariant v) { return v.toMap().value("text") == locDesc; });
|
|
|
|
m_recentLocations.erase(it, m_recentLocations.end());
|
|
|
|
|
|
|
|
// now we can always prepend
|
|
|
|
m_recentLocations.prepend(locSet);
|
|
|
|
if (m_recentLocations.size() > MAX_RECENT_AIRCRAFT)
|
|
|
|
m_recentLocations.pop_back();
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
void QtLauncher::onQuit()
|
|
|
|
{
|
2016-01-17 19:10:22 +00:00
|
|
|
qApp->exit(-1);
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
2015-03-17 22:10:49 +00:00
|
|
|
void QtLauncher::onToggleTerrasync(bool enabled)
|
|
|
|
{
|
|
|
|
if (enabled) {
|
2015-03-25 14:16:09 +00:00
|
|
|
QSettings settings;
|
|
|
|
QString downloadDir = settings.value("download-dir").toString();
|
|
|
|
if (downloadDir.isEmpty()) {
|
2016-06-23 13:26:34 +00:00
|
|
|
downloadDir = QString::fromStdString(flightgear::defaultDownloadDir().utf8Str());
|
2015-03-25 14:16:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QFileInfo info(downloadDir);
|
2015-03-17 22:10:49 +00:00
|
|
|
if (!info.exists()) {
|
|
|
|
QMessageBox msg;
|
|
|
|
msg.setWindowTitle(tr("Create download folder?"));
|
2016-04-08 09:26:19 +00:00
|
|
|
msg.setText(tr("The download folder '%1' does not exist, create it now?").arg(downloadDir));
|
2015-03-17 22:10:49 +00:00
|
|
|
msg.addButton(QMessageBox::Yes);
|
|
|
|
msg.addButton(QMessageBox::Cancel);
|
|
|
|
int result = msg.exec();
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2015-03-17 22:10:49 +00:00
|
|
|
if (result == QMessageBox::Cancel) {
|
|
|
|
m_ui->terrasyncCheck->setChecked(false);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-03-25 14:16:09 +00:00
|
|
|
QDir d(downloadDir);
|
|
|
|
d.mkpath(downloadDir);
|
2015-03-17 22:10:49 +00:00
|
|
|
}
|
2015-03-25 14:16:09 +00:00
|
|
|
} // of is enabled
|
2015-03-17 22:10:49 +00:00
|
|
|
}
|
|
|
|
|
2015-08-03 20:53:56 +00:00
|
|
|
void QtLauncher::onAircraftInstalledCompleted(QModelIndex index)
|
|
|
|
{
|
2015-09-28 00:44:29 +00:00
|
|
|
maybeUpdateSelectedAircraft(index);
|
2015-08-03 20:53:56 +00:00
|
|
|
}
|
|
|
|
|
2016-01-10 19:47:57 +00:00
|
|
|
void QtLauncher::onRatingsFilterToggled()
|
|
|
|
{
|
|
|
|
QModelIndex aircraftIndex = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
|
|
|
|
QModelIndex proxyIndex = m_aircraftProxy->mapFromSource(aircraftIndex);
|
|
|
|
if (proxyIndex.isValid()) {
|
|
|
|
m_ui->aircraftList->scrollTo(proxyIndex);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-03 20:53:56 +00:00
|
|
|
void QtLauncher::onAircraftInstallFailed(QModelIndex index, QString errorMessage)
|
|
|
|
{
|
|
|
|
qWarning() << Q_FUNC_INFO << index.data(AircraftURIRole) << errorMessage;
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2015-08-03 20:53:56 +00:00
|
|
|
QMessageBox msg;
|
2015-09-28 00:44:29 +00:00
|
|
|
msg.setWindowTitle(tr("Aircraft installation failed"));
|
2015-08-03 20:53:56 +00:00
|
|
|
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();
|
2015-09-28 00:44:29 +00:00
|
|
|
|
|
|
|
maybeUpdateSelectedAircraft(index);
|
2015-08-03 20:53:56 +00:00
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
void QtLauncher::onAircraftSelected(const QModelIndex& index)
|
|
|
|
{
|
2015-08-03 20:53:56 +00:00
|
|
|
m_selectedAircraft = index.data(AircraftURIRole).toUrl();
|
2014-12-26 12:20:51 +00:00
|
|
|
updateSelectedAircraft();
|
|
|
|
}
|
|
|
|
|
2015-08-03 20:53:56 +00:00
|
|
|
void QtLauncher::onRequestPackageInstall(const QModelIndex& index)
|
|
|
|
{
|
2016-01-10 19:47:57 +00:00
|
|
|
// also select, otherwise UI is confusing
|
|
|
|
m_selectedAircraft = index.data(AircraftURIRole).toUrl();
|
|
|
|
updateSelectedAircraft();
|
|
|
|
|
2015-08-03 20:53:56 +00:00
|
|
|
QString pkg = index.data(AircraftPackageIdRole).toString();
|
|
|
|
simgear::pkg::PackageRef pref = globals->packageRoot()->getPackageById(pkg.toStdString());
|
2015-09-28 00:44:29 +00:00
|
|
|
if (pref->isInstalled()) {
|
|
|
|
InstallRef install = pref->existingInstall();
|
|
|
|
if (install && install->hasUpdate()) {
|
|
|
|
globals->packageRoot()->scheduleToUpdate(install);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
pref->install();
|
|
|
|
}
|
2015-08-03 20:53:56 +00:00
|
|
|
}
|
|
|
|
|
2016-11-06 20:50:34 +00:00
|
|
|
void QtLauncher::onRequestPackageUninstall(const QModelIndex& index)
|
|
|
|
{
|
|
|
|
|
|
|
|
m_selectedAircraft = index.data(AircraftURIRole).toUrl();
|
|
|
|
updateSelectedAircraft();
|
|
|
|
|
|
|
|
QString pkg = index.data(AircraftPackageIdRole).toString();
|
|
|
|
if (pkg.isEmpty()) {
|
|
|
|
return; // can't be uninstalled, not from a package
|
|
|
|
}
|
|
|
|
|
|
|
|
simgear::pkg::PackageRef pref = globals->packageRoot()->getPackageById(pkg.toStdString());
|
|
|
|
if (pref && pref->isInstalled()) {
|
|
|
|
qDebug() << "uninstalling" << pkg;
|
|
|
|
pref->existingInstall()->uninstall();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-08-03 20:53:56 +00:00
|
|
|
void QtLauncher::onCancelDownload(const QModelIndex& index)
|
|
|
|
{
|
|
|
|
QString pkg = index.data(AircraftPackageIdRole).toString();
|
|
|
|
simgear::pkg::PackageRef pref = globals->packageRoot()->getPackageById(pkg.toStdString());
|
|
|
|
simgear::pkg::InstallRef i = pref->existingInstall();
|
|
|
|
i->cancelDownload();
|
|
|
|
}
|
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
void QtLauncher::onRestoreDefaults()
|
|
|
|
{
|
|
|
|
QMessageBox mbox(this);
|
|
|
|
mbox.setText(tr("Restore all settings to defaults?"));
|
|
|
|
mbox.setInformativeText(tr("Restoring settings to their defaults may affect available add-ons such as scenery or aircraft."));
|
|
|
|
QPushButton* quitButton = mbox.addButton(tr("Restore and restart 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;
|
|
|
|
settings.clear();
|
|
|
|
settings.setValue("restore-defaults-on-run", true);
|
|
|
|
}
|
|
|
|
|
|
|
|
restartTheApp(QStringList());
|
|
|
|
}
|
|
|
|
|
2015-09-28 00:44:29 +00:00
|
|
|
void QtLauncher::maybeUpdateSelectedAircraft(QModelIndex index)
|
|
|
|
{
|
|
|
|
QUrl u = index.data(AircraftURIRole).toUrl();
|
|
|
|
if (u == m_selectedAircraft) {
|
|
|
|
// potentially enable the run button now!
|
|
|
|
updateSelectedAircraft();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
void QtLauncher::updateSelectedAircraft()
|
|
|
|
{
|
2015-08-03 20:53:56 +00:00
|
|
|
QModelIndex index = m_aircraftModel->indexOfAircraftURI(m_selectedAircraft);
|
|
|
|
if (index.isValid()) {
|
|
|
|
QPixmap pm = index.data(Qt::DecorationRole).value<QPixmap>();
|
|
|
|
m_ui->thumbnail->setPixmap(pm);
|
2016-11-07 14:14:41 +00:00
|
|
|
m_ui->aircraftName->setText(index.data(Qt::DisplayRole).toString());
|
|
|
|
|
|
|
|
QVariant longDesc = index.data(AircraftLongDescriptionRole);
|
|
|
|
m_ui->aircraftDescription->setVisible(!longDesc.isNull());
|
|
|
|
m_ui->aircraftDescription->setText(longDesc.toString());
|
2015-10-16 01:05:17 +00:00
|
|
|
|
2015-08-03 20:53:56 +00:00
|
|
|
int status = index.data(AircraftPackageStatusRole).toInt();
|
|
|
|
bool canRun = (status == PackageInstalled);
|
|
|
|
m_ui->runButton->setEnabled(canRun);
|
2015-11-17 07:36:54 +00:00
|
|
|
|
|
|
|
LauncherAircraftType aircraftType = Airplane;
|
|
|
|
if (index.data(AircraftIsHelicopterRole).toBool()) {
|
|
|
|
aircraftType = Helicopter;
|
|
|
|
} else if (index.data(AircraftIsSeaplaneRole).toBool()) {
|
|
|
|
aircraftType = Seaplane;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_ui->location->setAircraftType(aircraftType);
|
2015-08-03 20:53:56 +00:00
|
|
|
} else {
|
2014-12-26 12:20:51 +00:00
|
|
|
m_ui->thumbnail->setPixmap(QPixmap());
|
2016-11-07 14:14:41 +00:00
|
|
|
m_ui->aircraftName->setText("");
|
|
|
|
m_ui->aircraftDescription->hide();
|
2015-08-03 20:53:56 +00:00
|
|
|
m_ui->runButton->setEnabled(false);
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-07 19:49:33 +00:00
|
|
|
void QtLauncher::onUpdateAllAircraft()
|
|
|
|
{
|
|
|
|
const PackageList& toBeUpdated = globals->packageRoot()->packagesNeedingUpdate();
|
|
|
|
std::for_each(toBeUpdated.begin(), toBeUpdated.end(), [](PackageRef pkg) {
|
|
|
|
globals->packageRoot()->scheduleToUpdate(pkg->install());
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::onPackagesNeedUpdate(bool yes)
|
|
|
|
{
|
|
|
|
Q_UNUSED(yes);
|
|
|
|
checkUpdateAircraft();
|
|
|
|
}
|
|
|
|
|
2015-08-03 20:53:56 +00:00
|
|
|
QModelIndex QtLauncher::proxyIndexForAircraftURI(QUrl uri) const
|
2014-12-26 12:20:51 +00:00
|
|
|
{
|
2015-08-03 20:53:56 +00:00
|
|
|
return m_aircraftProxy->mapFromSource(sourceIndexForAircraftURI(uri));
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
2015-08-03 20:53:56 +00:00
|
|
|
QModelIndex QtLauncher::sourceIndexForAircraftURI(QUrl uri) const
|
2014-12-26 12:20:51 +00:00
|
|
|
{
|
|
|
|
AircraftItemModel* sourceModel = qobject_cast<AircraftItemModel*>(m_aircraftProxy->sourceModel());
|
|
|
|
Q_ASSERT(sourceModel);
|
2015-08-03 20:53:56 +00:00
|
|
|
return sourceModel->indexOfAircraftURI(uri);
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::onPopupAircraftHistory()
|
|
|
|
{
|
|
|
|
if (m_recentAircraft.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QMenu m;
|
2015-08-03 20:53:56 +00:00
|
|
|
Q_FOREACH(QUrl uri, m_recentAircraft) {
|
2016-12-18 21:25:05 +00:00
|
|
|
QString nm = m_aircraftModel->nameForAircraftURI(uri);
|
|
|
|
if (nm.isEmpty()) {
|
2014-12-26 12:20:51 +00:00
|
|
|
continue;
|
|
|
|
}
|
2016-12-18 21:25:05 +00:00
|
|
|
|
|
|
|
QAction* act = m.addAction(nm);
|
2015-08-03 20:53:56 +00:00
|
|
|
act->setData(uri);
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QPoint popupPos = m_ui->aircraftHistory->mapToGlobal(m_ui->aircraftHistory->rect().bottomLeft());
|
|
|
|
QAction* triggered = m.exec(popupPos);
|
|
|
|
if (triggered) {
|
2016-11-18 16:10:02 +00:00
|
|
|
const QUrl uri = triggered->data().toUrl();
|
|
|
|
m_selectedAircraft = uri;
|
2015-08-03 20:53:56 +00:00
|
|
|
QModelIndex index = proxyIndexForAircraftURI(m_selectedAircraft);
|
2014-12-26 12:20:51 +00:00
|
|
|
m_ui->aircraftList->selectionModel()->setCurrentIndex(index,
|
|
|
|
QItemSelectionModel::ClearAndSelect);
|
|
|
|
m_ui->aircraftFilter->clear();
|
2016-11-18 16:10:02 +00:00
|
|
|
m_aircraftModel->selectVariantForAircraftURI(uri);
|
2014-12-26 12:20:51 +00:00
|
|
|
updateSelectedAircraft();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-11-07 11:54:38 +00:00
|
|
|
void QtLauncher::onPopupLocationHistory()
|
|
|
|
{
|
|
|
|
if (m_recentLocations.isEmpty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
QMenu m;
|
|
|
|
Q_FOREACH(QVariant loc, m_recentLocations) {
|
|
|
|
QString summary = loc.toMap().value("text").toString();
|
|
|
|
QAction* act = m.addAction(summary);
|
|
|
|
act->setData(loc);
|
|
|
|
}
|
|
|
|
|
|
|
|
QPoint popupPos = m_ui->locationHistory->mapToGlobal(m_ui->locationHistory->rect().bottomLeft());
|
|
|
|
QAction* triggered = m.exec(popupPos);
|
|
|
|
if (triggered) {
|
|
|
|
m_ui->location->restoreLocation(triggered->data().toMap());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
void QtLauncher::onEditRatingsFilter()
|
|
|
|
{
|
|
|
|
EditRatingsFilterDialog dialog(this);
|
|
|
|
dialog.setRatings(m_ratingFilters);
|
|
|
|
|
|
|
|
dialog.exec();
|
|
|
|
if (dialog.result() == QDialog::Accepted) {
|
|
|
|
QVariantList vl;
|
|
|
|
for (int i=0; i<4; ++i) {
|
|
|
|
m_ratingFilters[i] = dialog.getRating(i);
|
|
|
|
vl.append(m_ratingFilters[i]);
|
|
|
|
}
|
|
|
|
m_aircraftProxy->setRatings(m_ratingFilters);
|
|
|
|
|
|
|
|
QSettings settings;
|
|
|
|
settings.setValue("min-ratings", vl);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::updateSettingsSummary()
|
|
|
|
{
|
|
|
|
QStringList summary;
|
|
|
|
if (m_ui->timeOfDayCombo->currentIndex() > 0) {
|
|
|
|
summary.append(QString(m_ui->timeOfDayCombo->currentText().toLower()));
|
|
|
|
}
|
|
|
|
|
2015-01-10 14:57:59 +00:00
|
|
|
if (m_ui->seasonCombo->currentIndex() > 0) {
|
|
|
|
summary.append(QString(m_ui->seasonCombo->currentText().toLower()));
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
if (m_ui->rembrandtCheckbox->isChecked()) {
|
|
|
|
summary.append("Rembrandt enabled");
|
2015-01-07 11:30:44 +00:00
|
|
|
} else if (m_ui->msaaCheckbox->isChecked()) {
|
|
|
|
summary.append("anti-aliasing");
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
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");
|
|
|
|
}
|
|
|
|
|
2016-11-07 12:00:57 +00:00
|
|
|
if (m_ui->mpBox->isChecked()) {
|
|
|
|
summary.append(tr("multiplayer: %1").arg(m_ui->mpCallsign->text()));
|
|
|
|
}
|
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
QString s = summary.join(", ");
|
|
|
|
s[0] = s[0].toUpper();
|
|
|
|
m_ui->settingsDescription->setText(s);
|
|
|
|
}
|
|
|
|
|
2015-01-07 11:30:44 +00:00
|
|
|
void QtLauncher::onRembrandtToggled(bool b)
|
|
|
|
{
|
|
|
|
// Rembrandt and multi-sample are exclusive
|
|
|
|
m_ui->msaaCheckbox->setEnabled(!b);
|
|
|
|
}
|
|
|
|
|
2016-01-10 18:56:46 +00:00
|
|
|
void QtLauncher::onShowInstalledAircraftToggled(bool b)
|
|
|
|
{
|
|
|
|
m_ui->ratingsFilterCheck->setEnabled(!b);
|
2016-01-10 19:47:57 +00:00
|
|
|
maybeRestoreAircraftSelection();
|
2016-01-10 18:56:46 +00:00
|
|
|
}
|
|
|
|
|
2015-03-10 08:30:55 +00:00
|
|
|
void QtLauncher::onSubsytemIdleTimeout()
|
|
|
|
{
|
|
|
|
globals->get_subsystem_mgr()->update(0.0);
|
|
|
|
}
|
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
void QtLauncher::onDownloadDirChanged()
|
2015-03-12 13:29:06 +00:00
|
|
|
{
|
2016-04-15 16:06:53 +00:00
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
// replace existing package root
|
|
|
|
globals->get_subsystem<FGHTTPClient>()->shutdown();
|
|
|
|
globals->setPackageRoot(simgear::pkg::RootRef());
|
2016-03-24 15:10:06 +00:00
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
// create new root with updated download-dir value
|
|
|
|
fgInitPackageRoot();
|
2016-03-24 15:10:06 +00:00
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
globals->get_subsystem<FGHTTPClient>()->init();
|
2016-03-24 15:10:06 +00:00
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
QSettings settings;
|
2016-04-15 16:06:53 +00:00
|
|
|
// re-scan the aircraft list
|
2016-04-08 09:26:19 +00:00
|
|
|
m_aircraftModel->setPackageRoot(globals->packageRoot());
|
|
|
|
m_aircraftModel->setPaths(settings.value("aircraft-paths").toStringList());
|
|
|
|
m_aircraftModel->scanDirs();
|
2016-01-10 22:38:01 +00:00
|
|
|
|
2016-04-15 16:06:53 +00:00
|
|
|
checkOfficialCatalogMessage();
|
2016-07-08 08:36:42 +00:00
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
// re-set scenery dirs
|
|
|
|
setSceneryPaths();
|
2015-03-12 13:29:06 +00:00
|
|
|
}
|
|
|
|
|
2016-11-07 19:49:33 +00:00
|
|
|
bool QtLauncher::shouldShowOfficialCatalogMessage() const
|
2016-04-15 16:06:53 +00:00
|
|
|
{
|
|
|
|
QSettings settings;
|
|
|
|
bool showOfficialCatalogMesssage = !globals->get_subsystem<FGHTTPClient>()->isDefaultCatalogInstalled();
|
|
|
|
if (settings.value("hide-official-catalog-message").toBool()) {
|
|
|
|
showOfficialCatalogMesssage = false;
|
|
|
|
}
|
2016-11-07 19:49:33 +00:00
|
|
|
return showOfficialCatalogMesssage;
|
|
|
|
}
|
|
|
|
void QtLauncher::checkOfficialCatalogMessage()
|
|
|
|
{
|
|
|
|
const bool show = shouldShowOfficialCatalogMessage();
|
|
|
|
m_aircraftModel->setMessageWidgetVisible(show);
|
|
|
|
if (show) {
|
2016-04-15 16:06:53 +00:00
|
|
|
NoOfficialHangarMessage* messageWidget = new NoOfficialHangarMessage;
|
|
|
|
connect(messageWidget, &NoOfficialHangarMessage::linkActivated,
|
|
|
|
this, &QtLauncher::onOfficialCatalogMessageLink);
|
|
|
|
|
2016-11-07 19:49:33 +00:00
|
|
|
QModelIndex index = m_aircraftProxy->mapFromSource(m_aircraftModel->messageWidgetIndex());
|
2016-04-15 16:06:53 +00:00
|
|
|
m_ui->aircraftList->setIndexWidget(index, messageWidget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::onOfficialCatalogMessageLink(QUrl link)
|
|
|
|
{
|
|
|
|
QString s = link.toString();
|
|
|
|
if (s == "action:hide") {
|
|
|
|
QSettings settings;
|
|
|
|
settings.setValue("hide-official-catalog-message", true);
|
|
|
|
} else if (s == "action:add-official") {
|
|
|
|
AddOnsPage::addDefaultCatalog(this);
|
|
|
|
}
|
|
|
|
|
|
|
|
checkOfficialCatalogMessage();
|
|
|
|
}
|
|
|
|
|
2016-11-07 19:49:33 +00:00
|
|
|
void QtLauncher::checkUpdateAircraft()
|
|
|
|
{
|
|
|
|
if (shouldShowOfficialCatalogMessage()) {
|
|
|
|
return; // don't interfere
|
|
|
|
}
|
|
|
|
|
|
|
|
const bool showUpdateMessage = !globals->packageRoot()->packagesNeedingUpdate().empty();
|
|
|
|
m_aircraftModel->setMessageWidgetVisible(showUpdateMessage);
|
|
|
|
if (showUpdateMessage) {
|
|
|
|
UpdateAllAircraftMessage* messageWidget = new UpdateAllAircraftMessage;
|
|
|
|
// connect(messageWidget, &UpdateAllAircraftMessage::linkActivated,
|
|
|
|
// this, &QtLauncher::onMessageLink);
|
|
|
|
connect(messageWidget, &UpdateAllAircraftMessage::updateAll, this, &QtLauncher::onUpdateAllAircraft);
|
|
|
|
QModelIndex index = m_aircraftProxy->mapFromSource(m_aircraftModel->messageWidgetIndex());
|
|
|
|
m_ui->aircraftList->setIndexWidget(index, messageWidget);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-07-08 08:36:42 +00:00
|
|
|
void QtLauncher::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, &QtLauncher::onRefreshMPServersDone);
|
|
|
|
m_mpServerRequest->fail(this, &QtLauncher::onRefreshMPServersFailed);
|
|
|
|
globals->get_subsystem<FGHTTPClient>()->makeRequest(m_mpServerRequest);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::onRefreshMPServersDone(simgear::HTTP::Request*)
|
|
|
|
{
|
|
|
|
// parse the properties
|
|
|
|
SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true);
|
|
|
|
m_ui->mpServerCombo->clear();
|
|
|
|
|
|
|
|
for (int i=0; i<targetnode->nChildren(); ++i) {
|
|
|
|
SGPropertyNode* c = targetnode->getChild(i);
|
|
|
|
if (c->getName() != std::string("server")) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-11-21 22:01:06 +00:00
|
|
|
if (c->getBoolValue("online") != true) {
|
|
|
|
// only list online servers
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2016-07-08 08:36:42 +00:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-07-08 08:36:42 +00:00
|
|
|
EditCustomMPServerDialog::addCustomItem(m_ui->mpServerCombo);
|
|
|
|
restoreMPServerSelection();
|
2016-07-08 08:36:42 +00:00
|
|
|
|
|
|
|
m_mpServerRequest.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::onRefreshMPServersFailed(simgear::HTTP::Request*)
|
|
|
|
{
|
|
|
|
qWarning() << "refreshing MP servers failed:" << QString::fromStdString(m_mpServerRequest->responseReason());
|
|
|
|
m_mpServerRequest.clear();
|
2016-07-08 08:36:42 +00:00
|
|
|
EditCustomMPServerDialog::addCustomItem(m_ui->mpServerCombo);
|
|
|
|
restoreMPServerSelection();
|
|
|
|
}
|
|
|
|
|
|
|
|
void QtLauncher::restoreMPServerSelection()
|
|
|
|
{
|
|
|
|
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;
|
|
|
|
}
|
2016-07-08 08:36:42 +00:00
|
|
|
}
|
|
|
|
|
2016-07-08 08:36:42 +00:00
|
|
|
void QtLauncher::onMPServerActivated(int index)
|
2016-07-08 08:36:42 +00:00
|
|
|
{
|
2016-07-08 08:36:42 +00:00
|
|
|
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()));
|
|
|
|
}
|
|
|
|
}
|
2016-07-08 08:36:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
int QtLauncher::findMPServerPort(const std::string& host)
|
|
|
|
{
|
|
|
|
SGPropertyNode *targetnode = fgGetNode("/sim/multiplay/server-list", true);
|
|
|
|
for (int i=0; i<targetnode->nChildren(); ++i) {
|
|
|
|
SGPropertyNode* c = targetnode->getChild(i);
|
|
|
|
if (c->getName() != std::string("server")) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (c->getStringValue("hostname") == host) {
|
|
|
|
return c->getIntValue("port");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
simgear::pkg::PackageRef QtLauncher::packageForAircraftURI(QUrl uri) const
|
2016-01-17 19:10:22 +00:00
|
|
|
{
|
2016-04-08 09:26:19 +00:00
|
|
|
if (uri.scheme() != "package") {
|
|
|
|
qWarning() << "invalid URL scheme:" << uri;
|
|
|
|
return simgear::pkg::PackageRef();
|
2016-01-17 19:10:22 +00:00
|
|
|
}
|
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
QString ident = uri.path();
|
|
|
|
return globals->packageRoot()->getPackageById(ident.toStdString());
|
|
|
|
}
|
2016-01-17 19:10:22 +00:00
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
void QtLauncher::restartTheApp(QStringList fgArgs)
|
|
|
|
{
|
2016-01-17 19:10:22 +00:00
|
|
|
// Spawn a new instance of myApplication:
|
|
|
|
QProcess proc;
|
|
|
|
QStringList args;
|
|
|
|
|
2016-04-08 09:26:19 +00:00
|
|
|
#if defined(Q_OS_MAC)
|
2016-01-17 19:10:22 +00:00
|
|
|
QDir dir(qApp->applicationDirPath()); // returns the 'MacOS' dir
|
|
|
|
dir.cdUp(); // up to 'contents' dir
|
|
|
|
dir.cdUp(); // up to .app dir
|
2016-04-08 09:26:19 +00:00
|
|
|
// see 'man open' for details, but '-n' ensures we launch a new instance,
|
|
|
|
// and we want to pass remaining arguments to us, not open.
|
|
|
|
args << "-n" << dir.absolutePath() << "--args" << "--launcher" << fgArgs;
|
|
|
|
qDebug() << "args" << args;
|
2016-01-17 19:10:22 +00:00
|
|
|
proc.startDetached("open", args);
|
|
|
|
#else
|
2016-04-08 09:26:19 +00:00
|
|
|
args << "--launcher" << fgArgs;
|
|
|
|
proc.startDetached(qApp->applicationFilePath(), args);
|
2016-01-17 19:10:22 +00:00
|
|
|
#endif
|
|
|
|
qApp->exit(-1);
|
|
|
|
}
|
|
|
|
|
2015-10-16 01:05:17 +00:00
|
|
|
#include "QtLauncher.moc"
|