Working on airport entry
This commit is contained in:
parent
9098219032
commit
86786496a5
15 changed files with 640 additions and 311 deletions
|
@ -154,6 +154,8 @@ if (HAVE_QT)
|
||||||
QmlRadioButtonHelper.hxx
|
QmlRadioButtonHelper.hxx
|
||||||
UnitsModel.cxx
|
UnitsModel.cxx
|
||||||
UnitsModel.hxx
|
UnitsModel.hxx
|
||||||
|
NavaidSearchModel.hxx
|
||||||
|
NavaidSearchModel.cxx
|
||||||
)
|
)
|
||||||
|
|
||||||
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
set_property(TARGET fgqmlui PROPERTY AUTOMOC ON)
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#include "NavaidDiagram.hxx"
|
#include "NavaidDiagram.hxx"
|
||||||
#include "QmlRadioButtonHelper.hxx"
|
#include "QmlRadioButtonHelper.hxx"
|
||||||
#include "UnitsModel.hxx"
|
#include "UnitsModel.hxx"
|
||||||
|
#include "NavaidSearchModel.hxx"
|
||||||
|
|
||||||
using namespace simgear::pkg;
|
using namespace simgear::pkg;
|
||||||
|
|
||||||
|
@ -55,9 +56,6 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) :
|
||||||
QObject(parent),
|
QObject(parent),
|
||||||
m_window(window)
|
m_window(window)
|
||||||
{
|
{
|
||||||
qRegisterMetaType<QuantityValue>();
|
|
||||||
qRegisterMetaTypeStreamOperators<QuantityValue>("Quantity");
|
|
||||||
|
|
||||||
m_serversModel = new MPServersModel(this);
|
m_serversModel = new MPServersModel(this);
|
||||||
m_location = new LocationController(this);
|
m_location = new LocationController(this);
|
||||||
m_locationHistory = new RecentLocationsModel(this);
|
m_locationHistory = new RecentLocationsModel(this);
|
||||||
|
@ -137,6 +135,8 @@ void LauncherController::initQML()
|
||||||
qmlRegisterUncreatableType<LaunchConfig>("FlightGear.Launcher", 1, 0, "LaunchConfig", "Singleton API");
|
qmlRegisterUncreatableType<LaunchConfig>("FlightGear.Launcher", 1, 0, "LaunchConfig", "Singleton API");
|
||||||
qmlRegisterUncreatableType<MPServersModel>("FlightGear.Launcher", 1, 0, "MPServers", "Singleton API");
|
qmlRegisterUncreatableType<MPServersModel>("FlightGear.Launcher", 1, 0, "MPServers", "Singleton API");
|
||||||
|
|
||||||
|
qmlRegisterType<NavaidSearchModel>("FlightGear", 1, 0, "NavaidSearch");
|
||||||
|
|
||||||
qmlRegisterUncreatableType<Units>("FlightGear", 1, 0, "Units", "Only for enum");
|
qmlRegisterUncreatableType<Units>("FlightGear", 1, 0, "Units", "Only for enum");
|
||||||
qmlRegisterType<UnitsModel>("FlightGear", 1, 0, "UnitsModel");
|
qmlRegisterType<UnitsModel>("FlightGear", 1, 0, "UnitsModel");
|
||||||
|
|
||||||
|
@ -158,6 +158,8 @@ void LauncherController::initQML()
|
||||||
qmlRegisterType<NavaidDiagram>("FlightGear", 1, 0, "NavaidDiagram");
|
qmlRegisterType<NavaidDiagram>("FlightGear", 1, 0, "NavaidDiagram");
|
||||||
qmlRegisterType<QmlRadioButtonGroup>("FlightGear", 1, 0, "RadioButtonGroup");
|
qmlRegisterType<QmlRadioButtonGroup>("FlightGear", 1, 0, "RadioButtonGroup");
|
||||||
|
|
||||||
|
qmlRegisterSingletonType(QUrl("qrc:///qml/OverlayShared.qml"), "FlightGear", 1, 0, "OverlayShared");
|
||||||
|
|
||||||
QNetworkDiskCache* diskCache = new QNetworkDiskCache(this);
|
QNetworkDiskCache* diskCache = new QNetworkDiskCache(this);
|
||||||
SGPath cachePath = globals->get_fg_home() / "PreviewsCache";
|
SGPath cachePath = globals->get_fg_home() / "PreviewsCache";
|
||||||
diskCache->setCacheDirectory(QString::fromStdString(cachePath.utf8Str()));
|
diskCache->setCacheDirectory(QString::fromStdString(cachePath.utf8Str()));
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
#include "NavaidDiagram.hxx"
|
#include "NavaidDiagram.hxx"
|
||||||
#include "LaunchConfig.hxx"
|
#include "LaunchConfig.hxx"
|
||||||
#include "DefaultAircraftLocator.hxx"
|
#include "DefaultAircraftLocator.hxx"
|
||||||
|
#include "NavaidSearchModel.hxx"
|
||||||
|
|
||||||
#include <Airports/airport.hxx>
|
#include <Airports/airport.hxx>
|
||||||
#include <Airports/groundnetwork.hxx>
|
#include <Airports/groundnetwork.hxx>
|
||||||
|
@ -49,81 +50,6 @@ using namespace flightgear;
|
||||||
|
|
||||||
const unsigned int MAX_RECENT_LOCATIONS = 64;
|
const unsigned int MAX_RECENT_LOCATIONS = 64;
|
||||||
|
|
||||||
QString fixNavaidName(QString s)
|
|
||||||
{
|
|
||||||
// split into words
|
|
||||||
QStringList words = s.split(QChar(' '));
|
|
||||||
QStringList changedWords;
|
|
||||||
Q_FOREACH(QString w, words) {
|
|
||||||
QString up = w.toUpper();
|
|
||||||
|
|
||||||
// expand common abbreviations
|
|
||||||
// note these are not translated, since they are abbreivations
|
|
||||||
// for English-langauge airports, mostly in the US/Canada
|
|
||||||
if (up == "FLD") {
|
|
||||||
changedWords.append("Field");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (up == "CO") {
|
|
||||||
changedWords.append("County");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((up == "MUNI") || (up == "MUN")) {
|
|
||||||
changedWords.append("Municipal");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (up == "MEM") {
|
|
||||||
changedWords.append("Memorial");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (up == "RGNL") {
|
|
||||||
changedWords.append("Regional");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (up == "CTR") {
|
|
||||||
changedWords.append("Center");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (up == "INTL") {
|
|
||||||
changedWords.append("International");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// occurs in many Australian airport names in our DB
|
|
||||||
if (up == "(NSW)") {
|
|
||||||
changedWords.append("(New South Wales)");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((up == "VOR") || (up == "NDB")
|
|
||||||
|| (up == "VOR-DME") || (up == "VORTAC")
|
|
||||||
|| (up == "NDB-DME")
|
|
||||||
|| (up == "AFB") || (up == "RAF"))
|
|
||||||
{
|
|
||||||
changedWords.append(w);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if ((up =="[X]") || (up == "[H]") || (up == "[S]")) {
|
|
||||||
continue; // consume
|
|
||||||
}
|
|
||||||
|
|
||||||
QChar firstChar = w.at(0).toUpper();
|
|
||||||
w = w.mid(1).toLower();
|
|
||||||
w.prepend(firstChar);
|
|
||||||
|
|
||||||
changedWords.append(w);
|
|
||||||
}
|
|
||||||
|
|
||||||
return changedWords.join(QChar(' '));
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant savePositionList(const FGPositionedList& posList)
|
QVariant savePositionList(const FGPositionedList& posList)
|
||||||
{
|
{
|
||||||
QVariantList vl;
|
QVariantList vl;
|
||||||
|
@ -164,214 +90,10 @@ FGPositionedList loadPositionedList(QVariant v)
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
class IdentSearchFilter : public FGPositioned::TypeFilter
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
IdentSearchFilter(LauncherController::AircraftType aircraft)
|
|
||||||
{
|
|
||||||
addType(FGPositioned::VOR);
|
|
||||||
addType(FGPositioned::FIX);
|
|
||||||
addType(FGPositioned::NDB);
|
|
||||||
|
|
||||||
if (aircraft == LauncherController::Helicopter) {
|
|
||||||
addType(FGPositioned::HELIPAD);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aircraft == LauncherController::Seaplane) {
|
|
||||||
addType(FGPositioned::SEAPORT);
|
|
||||||
} else {
|
|
||||||
addType(FGPositioned::AIRPORT);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
class NavSearchModel : public QAbstractListModel
|
|
||||||
{
|
|
||||||
Q_OBJECT
|
|
||||||
|
|
||||||
Q_PROPERTY(bool isSearchActive READ isSearchActive NOTIFY searchActiveChanged)
|
|
||||||
Q_PROPERTY(bool haveExistingSearch READ haveExistingSearch NOTIFY haveExistingSearchChanged)
|
|
||||||
|
|
||||||
enum Roles {
|
|
||||||
GeodRole = Qt::UserRole + 1,
|
|
||||||
GuidRole = Qt::UserRole + 2,
|
|
||||||
IdentRole = Qt::UserRole + 3,
|
|
||||||
NameRole = Qt::UserRole + 4,
|
|
||||||
IconRole = Qt::UserRole + 5,
|
|
||||||
TypeRole = Qt::UserRole + 6,
|
|
||||||
NavFrequencyRole = Qt::UserRole + 7
|
|
||||||
};
|
|
||||||
|
|
||||||
public:
|
|
||||||
NavSearchModel() { }
|
|
||||||
|
|
||||||
enum AircraftType
|
|
||||||
{
|
|
||||||
Airplane = LauncherController::Airplane,
|
|
||||||
Seaplane = LauncherController::Seaplane,
|
|
||||||
Helicopter = LauncherController::Helicopter,
|
|
||||||
Airship = LauncherController::Airship
|
|
||||||
};
|
|
||||||
|
|
||||||
Q_ENUMS(AircraftType)
|
|
||||||
|
|
||||||
Q_INVOKABLE void setSearch(QString t, AircraftType aircraft)
|
|
||||||
{
|
|
||||||
beginResetModel();
|
|
||||||
|
|
||||||
m_items.clear();
|
|
||||||
m_ids.clear();
|
|
||||||
|
|
||||||
std::string term(t.toUpper().toStdString());
|
|
||||||
|
|
||||||
IdentSearchFilter filter(static_cast<LauncherController::AircraftType>(aircraft));
|
|
||||||
FGPositionedList exactMatches = NavDataCache::instance()->findAllWithIdent(term, &filter, true);
|
|
||||||
m_ids.reserve(exactMatches.size());
|
|
||||||
m_items.reserve(exactMatches.size());
|
|
||||||
for (auto match : exactMatches) {
|
|
||||||
m_ids.push_back(match->guid());
|
|
||||||
m_items.push_back(match);
|
|
||||||
}
|
|
||||||
endResetModel();
|
|
||||||
|
|
||||||
m_search.reset(new NavDataCache::ThreadedGUISearch(term));
|
|
||||||
QTimer::singleShot(100, this, SLOT(onSearchResultsPoll()));
|
|
||||||
m_searchActive = true;
|
|
||||||
emit searchActiveChanged();
|
|
||||||
emit haveExistingSearchChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool isSearchActive() const
|
|
||||||
{
|
|
||||||
return m_searchActive;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool haveExistingSearch() const
|
|
||||||
{
|
|
||||||
return m_searchActive || (!m_items.empty());
|
|
||||||
}
|
|
||||||
|
|
||||||
int rowCount(const QModelIndex&) const override
|
|
||||||
{
|
|
||||||
return m_ids.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
QVariant data(const QModelIndex& index, int role) const override
|
|
||||||
{
|
|
||||||
if (!index.isValid())
|
|
||||||
return QVariant();
|
|
||||||
|
|
||||||
FGPositionedRef pos = itemAtRow(index.row());
|
|
||||||
switch (role) {
|
|
||||||
case GuidRole: return static_cast<qlonglong>(pos->guid());
|
|
||||||
case IdentRole: return QString::fromStdString(pos->ident());
|
|
||||||
case NameRole:
|
|
||||||
return fixNavaidName(QString::fromStdString(pos->name()));
|
|
||||||
|
|
||||||
case NavFrequencyRole: {
|
|
||||||
FGNavRecord* nav = fgpositioned_cast<FGNavRecord>(pos);
|
|
||||||
return nav ? nav->get_freq() : 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
case TypeRole: return static_cast<QmlPositioned::Type>(pos->type());
|
|
||||||
case IconRole:
|
|
||||||
return AirportDiagram::iconForPositioned(pos,
|
|
||||||
AirportDiagram::SmallIcons | AirportDiagram::LargeAirportPlans);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {};
|
|
||||||
}
|
|
||||||
|
|
||||||
FGPositionedRef itemAtRow(unsigned int row) const
|
|
||||||
{
|
|
||||||
FGPositionedRef pos = m_items[row];
|
|
||||||
if (!pos.valid()) {
|
|
||||||
pos = NavDataCache::instance()->loadById(m_ids[row]);
|
|
||||||
m_items[row] = pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
return pos;
|
|
||||||
}
|
|
||||||
|
|
||||||
void setItems(const FGPositionedList& items)
|
|
||||||
{
|
|
||||||
beginResetModel();
|
|
||||||
m_searchActive = false;
|
|
||||||
m_items = items;
|
|
||||||
|
|
||||||
m_ids.clear();
|
|
||||||
for (unsigned int i=0; i < items.size(); ++i) {
|
|
||||||
m_ids.push_back(m_items[i]->guid());
|
|
||||||
}
|
|
||||||
|
|
||||||
endResetModel();
|
|
||||||
emit searchActiveChanged();
|
|
||||||
}
|
|
||||||
|
|
||||||
QHash<int, QByteArray> roleNames() const override
|
|
||||||
{
|
|
||||||
QHash<int, QByteArray> result = QAbstractListModel::roleNames();
|
|
||||||
|
|
||||||
result[GeodRole] = "geod";
|
|
||||||
result[GuidRole] = "guid";
|
|
||||||
result[IdentRole] = "ident";
|
|
||||||
result[NameRole] = "name";
|
|
||||||
result[IconRole] = "icon";
|
|
||||||
result[TypeRole] = "type";
|
|
||||||
result[NavFrequencyRole] = "frequency";
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Q_SIGNALS:
|
|
||||||
void searchComplete();
|
|
||||||
void searchActiveChanged();
|
|
||||||
void haveExistingSearchChanged();
|
|
||||||
|
|
||||||
private slots:
|
|
||||||
|
|
||||||
void onSearchResultsPoll()
|
|
||||||
{
|
|
||||||
if (m_search.isNull()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
PositionedIDVec newIds = m_search->results();
|
|
||||||
if (!newIds.empty()) {
|
|
||||||
m_ids.reserve(newIds.size());
|
|
||||||
beginInsertRows(QModelIndex(), m_ids.size(), newIds.size() - 1);
|
|
||||||
for (auto id : newIds) {
|
|
||||||
m_ids.push_back(id);
|
|
||||||
m_items.push_back({}); // null ref
|
|
||||||
}
|
|
||||||
endInsertRows();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (m_search->isComplete()) {
|
|
||||||
m_searchActive = false;
|
|
||||||
m_search.reset();
|
|
||||||
emit searchComplete();
|
|
||||||
emit searchActiveChanged();
|
|
||||||
emit haveExistingSearchChanged();
|
|
||||||
} else {
|
|
||||||
QTimer::singleShot(100, this, SLOT(onSearchResultsPoll()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
PositionedIDVec m_ids;
|
|
||||||
mutable FGPositionedList m_items;
|
|
||||||
bool m_searchActive = false;
|
|
||||||
QScopedPointer<NavDataCache::ThreadedGUISearch> m_search;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
LocationController::LocationController(QObject *parent) :
|
LocationController::LocationController(QObject *parent) :
|
||||||
QObject(parent)
|
QObject(parent)
|
||||||
{
|
{
|
||||||
qmlRegisterUncreatableType<NavSearchModel>("FlightGear.Launcher", 1, 0, "NavSearchModel", "no");
|
m_searchModel = new NavaidSearchModel;
|
||||||
m_searchModel = new NavSearchModel;
|
|
||||||
|
|
||||||
m_detailQml = new QmlPositioned(this);
|
m_detailQml = new QmlPositioned(this);
|
||||||
m_baseQml = new QmlPositioned(this);
|
m_baseQml = new QmlPositioned(this);
|
||||||
|
|
||||||
|
@ -1014,6 +736,22 @@ void LocationController::applyAltitude()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void LocationController::applyOnFinal()
|
||||||
|
{
|
||||||
|
if (m_onFinal) {
|
||||||
|
if (!m_altitudeEnabled) {
|
||||||
|
m_config->setArg("glideslope", std::string("3.0"));
|
||||||
|
}
|
||||||
|
|
||||||
|
const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value;
|
||||||
|
m_config->setArg("offset-distance", QString::number(offsetNm));
|
||||||
|
m_config->setArg("on-ground", std::string("false"));
|
||||||
|
|
||||||
|
applyAirspeed();
|
||||||
|
applyAltitude();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void LocationController::onCollectConfig()
|
void LocationController::onCollectConfig()
|
||||||
{
|
{
|
||||||
if (m_skipFromArgs) {
|
if (m_skipFromArgs) {
|
||||||
|
@ -1041,6 +779,7 @@ void LocationController::onCollectConfig()
|
||||||
|
|
||||||
if (m_useActiveRunway) {
|
if (m_useActiveRunway) {
|
||||||
// pick by default
|
// pick by default
|
||||||
|
applyOnFinal();
|
||||||
} else if (onRunway) {
|
} else if (onRunway) {
|
||||||
if (m_airportLocation->type() == FGPositioned::AIRPORT) {
|
if (m_airportLocation->type() == FGPositioned::AIRPORT) {
|
||||||
m_config->setArg("runway", QString::fromStdString(m_detailLocation->ident()));
|
m_config->setArg("runway", QString::fromStdString(m_detailLocation->ident()));
|
||||||
|
@ -1052,15 +791,7 @@ void LocationController::onCollectConfig()
|
||||||
m_config->setArg("nav1", QString("%1:%2").arg(runway->headingDeg()).arg(mhz));
|
m_config->setArg("nav1", QString("%1:%2").arg(runway->headingDeg()).arg(mhz));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_onFinal) {
|
applyOnFinal();
|
||||||
m_config->setArg("glideslope", std::string("3.0"));
|
|
||||||
const double offsetNm = m_offsetDistance.convertToUnit(Units::NauticalMiles).value;
|
|
||||||
m_config->setArg("offset-distance", QString::number(offsetNm));
|
|
||||||
m_config->setArg("on-ground", std::string("false"));
|
|
||||||
|
|
||||||
applyAirspeed();
|
|
||||||
applyAltitude();
|
|
||||||
}
|
|
||||||
} else if (m_airportLocation->type() == FGPositioned::HELIPORT) {
|
} else if (m_airportLocation->type() == FGPositioned::HELIPORT) {
|
||||||
m_config->setArg("runway", QString::fromStdString(m_detailLocation->ident()));
|
m_config->setArg("runway", QString::fromStdString(m_detailLocation->ident()));
|
||||||
}
|
}
|
||||||
|
@ -1228,5 +959,3 @@ void LocationController::addToRecent(FGPositionedRef pos)
|
||||||
QSettings settings;
|
QSettings settings;
|
||||||
settings.setValue("recent-locations", savePositionList(m_recentLocations));
|
settings.setValue("recent-locations", savePositionList(m_recentLocations));
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "LocationController.moc"
|
|
||||||
|
|
|
@ -31,7 +31,7 @@
|
||||||
#include "QmlPositioned.hxx"
|
#include "QmlPositioned.hxx"
|
||||||
#include "UnitsModel.hxx"
|
#include "UnitsModel.hxx"
|
||||||
|
|
||||||
class NavSearchModel;
|
class NavaidSearchModel;
|
||||||
|
|
||||||
class LocationController : public QObject
|
class LocationController : public QObject
|
||||||
{
|
{
|
||||||
|
@ -39,7 +39,7 @@ class LocationController : public QObject
|
||||||
|
|
||||||
Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
|
Q_PROPERTY(QString description READ description NOTIFY descriptionChanged)
|
||||||
|
|
||||||
Q_PROPERTY(NavSearchModel* searchModel MEMBER m_searchModel CONSTANT)
|
Q_PROPERTY(NavaidSearchModel* searchModel MEMBER m_searchModel CONSTANT)
|
||||||
|
|
||||||
Q_PROPERTY(QList<QObject*> airportRunways READ airportRunways NOTIFY baseLocationChanged)
|
Q_PROPERTY(QList<QObject*> airportRunways READ airportRunways NOTIFY baseLocationChanged)
|
||||||
Q_PROPERTY(QList<QObject*> airportParkings READ airportParkings NOTIFY baseLocationChanged)
|
Q_PROPERTY(QList<QObject*> airportParkings READ airportParkings NOTIFY baseLocationChanged)
|
||||||
|
@ -194,8 +194,9 @@ private:
|
||||||
void applyPositionOffset();
|
void applyPositionOffset();
|
||||||
void applyAltitude();
|
void applyAltitude();
|
||||||
void applyAirspeed();
|
void applyAirspeed();
|
||||||
|
void applyOnFinal();
|
||||||
|
|
||||||
NavSearchModel* m_searchModel = nullptr;
|
NavaidSearchModel* m_searchModel = nullptr;
|
||||||
|
|
||||||
FGPositionedRef m_location;
|
FGPositionedRef m_location;
|
||||||
FGAirportRef m_airportLocation; // valid if m_location is an FGAirport
|
FGAirportRef m_airportLocation; // valid if m_location is an FGAirport
|
||||||
|
|
293
src/GUI/NavaidSearchModel.cxx
Normal file
293
src/GUI/NavaidSearchModel.cxx
Normal file
|
@ -0,0 +1,293 @@
|
||||||
|
// NavaidSearchModel.cxx - expose navaids via a QabstractListModel
|
||||||
|
//
|
||||||
|
// Written by James Turner, started July 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.
|
||||||
|
|
||||||
|
#include "NavaidSearchModel.hxx"
|
||||||
|
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
#include "AirportDiagram.hxx"
|
||||||
|
#include <Navaids/navrecord.hxx>
|
||||||
|
#include "QmlPositioned.hxx"
|
||||||
|
|
||||||
|
using namespace flightgear;
|
||||||
|
|
||||||
|
QString fixNavaidName(QString s)
|
||||||
|
{
|
||||||
|
// split into words
|
||||||
|
QStringList words = s.split(QChar(' '));
|
||||||
|
QStringList changedWords;
|
||||||
|
Q_FOREACH(QString w, words) {
|
||||||
|
QString up = w.toUpper();
|
||||||
|
|
||||||
|
// expand common abbreviations
|
||||||
|
// note these are not translated, since they are abbreivations
|
||||||
|
// for English-langauge airports, mostly in the US/Canada
|
||||||
|
if (up == "FLD") {
|
||||||
|
changedWords.append("Field");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (up == "CO") {
|
||||||
|
changedWords.append("County");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((up == "MUNI") || (up == "MUN")) {
|
||||||
|
changedWords.append("Municipal");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (up == "MEM") {
|
||||||
|
changedWords.append("Memorial");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (up == "RGNL") {
|
||||||
|
changedWords.append("Regional");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (up == "CTR") {
|
||||||
|
changedWords.append("Center");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (up == "INTL") {
|
||||||
|
changedWords.append("International");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// occurs in many Australian airport names in our DB
|
||||||
|
if (up == "(NSW)") {
|
||||||
|
changedWords.append("(New South Wales)");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((up == "VOR") || (up == "NDB")
|
||||||
|
|| (up == "VOR-DME") || (up == "VORTAC")
|
||||||
|
|| (up == "NDB-DME")
|
||||||
|
|| (up == "AFB") || (up == "RAF"))
|
||||||
|
{
|
||||||
|
changedWords.append(w);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((up =="[X]") || (up == "[H]") || (up == "[S]")) {
|
||||||
|
continue; // consume
|
||||||
|
}
|
||||||
|
|
||||||
|
QChar firstChar = w.at(0).toUpper();
|
||||||
|
w = w.mid(1).toLower();
|
||||||
|
w.prepend(firstChar);
|
||||||
|
|
||||||
|
changedWords.append(w);
|
||||||
|
}
|
||||||
|
|
||||||
|
return changedWords.join(QChar(' '));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
class IdentSearchFilter : public FGPositioned::TypeFilter
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
IdentSearchFilter(LauncherController::AircraftType aircraft, bool airportsOnly)
|
||||||
|
{
|
||||||
|
if (!airportsOnly) {
|
||||||
|
addType(FGPositioned::VOR);
|
||||||
|
addType(FGPositioned::FIX);
|
||||||
|
addType(FGPositioned::NDB);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aircraft == LauncherController::Helicopter) {
|
||||||
|
addType(FGPositioned::HELIPAD);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (aircraft == LauncherController::Seaplane) {
|
||||||
|
addType(FGPositioned::SEAPORT);
|
||||||
|
} else {
|
||||||
|
addType(FGPositioned::AIRPORT);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
void NavaidSearchModel::clear()
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_items.clear();
|
||||||
|
m_ids.clear();
|
||||||
|
m_searchActive = false;
|
||||||
|
m_search.reset();
|
||||||
|
endResetModel();
|
||||||
|
emit searchActiveChanged();
|
||||||
|
emit haveExistingSearchChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavaidSearchModel::setSearch(QString t, NavaidSearchModel::AircraftType aircraft)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
|
||||||
|
m_items.clear();
|
||||||
|
m_ids.clear();
|
||||||
|
|
||||||
|
std::string term(t.toUpper().toStdString());
|
||||||
|
|
||||||
|
IdentSearchFilter filter(static_cast<LauncherController::AircraftType>(aircraft), m_airportsOnly);
|
||||||
|
FGPositionedList exactMatches = NavDataCache::instance()->findAllWithIdent(term, &filter, true);
|
||||||
|
|
||||||
|
// truncate based on max results
|
||||||
|
if ((m_maxResults > 0) && (exactMatches.size() > m_maxResults)) {
|
||||||
|
auto it = exactMatches.begin() + m_maxResults;
|
||||||
|
exactMatches.erase(it, exactMatches.end());
|
||||||
|
}
|
||||||
|
|
||||||
|
m_ids.reserve(exactMatches.size());
|
||||||
|
m_items.reserve(exactMatches.size());
|
||||||
|
for (auto match : exactMatches) {
|
||||||
|
m_ids.push_back(match->guid());
|
||||||
|
m_items.push_back(match);
|
||||||
|
}
|
||||||
|
endResetModel();
|
||||||
|
|
||||||
|
m_search.reset(new NavDataCache::ThreadedGUISearch(term, m_airportsOnly));
|
||||||
|
QTimer::singleShot(100, this, SLOT(onSearchResultsPoll()));
|
||||||
|
m_searchActive = true;
|
||||||
|
emit searchActiveChanged();
|
||||||
|
emit haveExistingSearchChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool NavaidSearchModel::haveExistingSearch() const
|
||||||
|
{
|
||||||
|
return m_searchActive || (!m_items.empty());
|
||||||
|
}
|
||||||
|
|
||||||
|
int NavaidSearchModel::rowCount(const QModelIndex &) const
|
||||||
|
{
|
||||||
|
return m_ids.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
QVariant NavaidSearchModel::data(const QModelIndex &index, int role) const
|
||||||
|
{
|
||||||
|
if (!index.isValid())
|
||||||
|
return QVariant();
|
||||||
|
|
||||||
|
FGPositionedRef pos = itemAtRow(index.row());
|
||||||
|
switch (role) {
|
||||||
|
case GuidRole: return static_cast<qlonglong>(pos->guid());
|
||||||
|
case IdentRole: return QString::fromStdString(pos->ident());
|
||||||
|
case NameRole:
|
||||||
|
return fixNavaidName(QString::fromStdString(pos->name()));
|
||||||
|
|
||||||
|
case NavFrequencyRole: {
|
||||||
|
FGNavRecord* nav = fgpositioned_cast<FGNavRecord>(pos);
|
||||||
|
return nav ? nav->get_freq() : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
case TypeRole: return static_cast<QmlPositioned::Type>(pos->type());
|
||||||
|
case IconRole:
|
||||||
|
return AirportDiagram::iconForPositioned(pos,
|
||||||
|
AirportDiagram::SmallIcons | AirportDiagram::LargeAirportPlans);
|
||||||
|
}
|
||||||
|
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
FGPositionedRef NavaidSearchModel::itemAtRow(unsigned int row) const
|
||||||
|
{
|
||||||
|
FGPositionedRef pos = m_items[row];
|
||||||
|
if (!pos.valid()) {
|
||||||
|
pos = NavDataCache::instance()->loadById(m_ids[row]);
|
||||||
|
m_items[row] = pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavaidSearchModel::setItems(const FGPositionedList &items)
|
||||||
|
{
|
||||||
|
beginResetModel();
|
||||||
|
m_searchActive = false;
|
||||||
|
m_items = items;
|
||||||
|
|
||||||
|
m_ids.clear();
|
||||||
|
for (unsigned int i=0; i < items.size(); ++i) {
|
||||||
|
m_ids.push_back(m_items[i]->guid());
|
||||||
|
}
|
||||||
|
|
||||||
|
endResetModel();
|
||||||
|
emit searchActiveChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
QHash<int, QByteArray> NavaidSearchModel::roleNames() const
|
||||||
|
{
|
||||||
|
QHash<int, QByteArray> result = QAbstractListModel::roleNames();
|
||||||
|
|
||||||
|
result[GeodRole] = "geod";
|
||||||
|
result[GuidRole] = "guid";
|
||||||
|
result[IdentRole] = "ident";
|
||||||
|
result[NameRole] = "name";
|
||||||
|
result[IconRole] = "icon";
|
||||||
|
result[TypeRole] = "type";
|
||||||
|
result[NavFrequencyRole] = "frequency";
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
qlonglong NavaidSearchModel::exactMatch() const
|
||||||
|
{
|
||||||
|
if (m_searchActive || (m_ids.size() != 1))
|
||||||
|
return 0; // no exact match
|
||||||
|
|
||||||
|
return m_ids.back(); // which is also the front
|
||||||
|
}
|
||||||
|
|
||||||
|
void NavaidSearchModel::onSearchResultsPoll()
|
||||||
|
{
|
||||||
|
if (m_search.isNull()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
PositionedIDVec newIds = m_search->results();
|
||||||
|
int newTotalSize = m_ids.size() + newIds.size();
|
||||||
|
if ((m_maxResults > 0) && (newTotalSize > m_maxResults)) {
|
||||||
|
// truncate new results as necessary
|
||||||
|
int numNewAllowed = m_maxResults - m_ids.size();
|
||||||
|
auto it = newIds.begin() + numNewAllowed;
|
||||||
|
newIds.erase(it, newIds.end());
|
||||||
|
// possible that newIDs is empty now
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!newIds.empty()) {
|
||||||
|
beginInsertRows(QModelIndex(), m_ids.size(), newIds.size() - 1);
|
||||||
|
for (auto id : newIds) {
|
||||||
|
m_ids.push_back(id);
|
||||||
|
m_items.push_back({}); // null ref
|
||||||
|
}
|
||||||
|
endInsertRows();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_search->isComplete()) {
|
||||||
|
m_searchActive = false;
|
||||||
|
m_search.reset();
|
||||||
|
emit searchComplete();
|
||||||
|
emit searchActiveChanged();
|
||||||
|
emit haveExistingSearchChanged();
|
||||||
|
} else {
|
||||||
|
QTimer::singleShot(100, this, SLOT(onSearchResultsPoll()));
|
||||||
|
}
|
||||||
|
}
|
108
src/GUI/NavaidSearchModel.hxx
Normal file
108
src/GUI/NavaidSearchModel.hxx
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
// NavaidSearchModel.hxx - expose navaids via a QabstractListModel
|
||||||
|
//
|
||||||
|
// Written by James Turner, started July 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 NAVAIDSEARCHMODEL_HXX
|
||||||
|
#define NAVAIDSEARCHMODEL_HXX
|
||||||
|
|
||||||
|
#include <QAbstractListModel>
|
||||||
|
|
||||||
|
#include "LauncherController.hxx"
|
||||||
|
|
||||||
|
#include <Navaids/positioned.hxx>
|
||||||
|
#include <Navaids/NavDataCache.hxx>
|
||||||
|
|
||||||
|
class NavaidSearchModel : public QAbstractListModel
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
Q_PROPERTY(bool isSearchActive READ isSearchActive NOTIFY searchActiveChanged)
|
||||||
|
Q_PROPERTY(bool haveExistingSearch READ haveExistingSearch NOTIFY haveExistingSearchChanged)
|
||||||
|
Q_PROPERTY(bool airportsOnly MEMBER m_airportsOnly NOTIFY airportsOnlyChanged)
|
||||||
|
Q_PROPERTY(int maxResults MEMBER m_maxResults NOTIFY maxResultsChanged)
|
||||||
|
|
||||||
|
Q_PROPERTY(qlonglong exactMatch READ exactMatch NOTIFY searchActiveChanged)
|
||||||
|
|
||||||
|
enum Roles {
|
||||||
|
GeodRole = Qt::UserRole + 1,
|
||||||
|
GuidRole = Qt::UserRole + 2,
|
||||||
|
IdentRole = Qt::UserRole + 3,
|
||||||
|
NameRole = Qt::UserRole + 4,
|
||||||
|
IconRole = Qt::UserRole + 5,
|
||||||
|
TypeRole = Qt::UserRole + 6,
|
||||||
|
NavFrequencyRole = Qt::UserRole + 7
|
||||||
|
};
|
||||||
|
|
||||||
|
public:
|
||||||
|
NavaidSearchModel() { }
|
||||||
|
|
||||||
|
enum AircraftType
|
||||||
|
{
|
||||||
|
Airplane = LauncherController::Airplane,
|
||||||
|
Seaplane = LauncherController::Seaplane,
|
||||||
|
Helicopter = LauncherController::Helicopter,
|
||||||
|
Airship = LauncherController::Airship
|
||||||
|
};
|
||||||
|
|
||||||
|
Q_ENUMS(AircraftType)
|
||||||
|
|
||||||
|
Q_INVOKABLE void setSearch(QString t, AircraftType aircraft);
|
||||||
|
|
||||||
|
Q_INVOKABLE void clear();
|
||||||
|
|
||||||
|
bool isSearchActive() const
|
||||||
|
{
|
||||||
|
return m_searchActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool haveExistingSearch() const;
|
||||||
|
|
||||||
|
int rowCount(const QModelIndex&) const override;
|
||||||
|
|
||||||
|
QVariant data(const QModelIndex& index, int role) const override;
|
||||||
|
|
||||||
|
FGPositionedRef itemAtRow(unsigned int row) const;
|
||||||
|
|
||||||
|
void setItems(const FGPositionedList& items);
|
||||||
|
|
||||||
|
QHash<int, QByteArray> roleNames() const override;
|
||||||
|
|
||||||
|
qlonglong exactMatch() const;
|
||||||
|
|
||||||
|
Q_SIGNALS:
|
||||||
|
void searchComplete();
|
||||||
|
void searchActiveChanged();
|
||||||
|
void haveExistingSearchChanged();
|
||||||
|
void airportsOnlyChanged();
|
||||||
|
void maxResultsChanged();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void onSearchResultsPoll();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PositionedIDVec m_ids;
|
||||||
|
mutable FGPositionedList m_items;
|
||||||
|
bool m_searchActive = false;
|
||||||
|
bool m_airportsOnly = false;
|
||||||
|
int m_maxResults = 0;
|
||||||
|
QScopedPointer<flightgear::NavDataCache::ThreadedGUISearch> m_search;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
#endif // NAVAIDSEARCHMODEL_HXX
|
|
@ -1,7 +1,145 @@
|
||||||
import QtQuick 2.0
|
import QtQuick 2.4
|
||||||
|
import QtQuick.Window 2.0
|
||||||
|
import FlightGear 1.0
|
||||||
|
import FlightGear.Launcher 1.0
|
||||||
|
import "."
|
||||||
|
|
||||||
LineEdit
|
LineEdit
|
||||||
{
|
{
|
||||||
|
id: root
|
||||||
placeholder: "KSFO"
|
placeholder: "KSFO"
|
||||||
suggestedWidthString: "XXXX"
|
suggestedWidthString: "XXXX"
|
||||||
|
|
||||||
|
Positioned {
|
||||||
|
id: airport
|
||||||
|
}
|
||||||
|
|
||||||
|
function selectAirport(guid)
|
||||||
|
{
|
||||||
|
airport.guid = guid
|
||||||
|
text = airport.ident
|
||||||
|
// we don't want this to trigger a search ....
|
||||||
|
searchTimer.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
onActiveFocusChanged: {
|
||||||
|
if (activeFocus) {
|
||||||
|
OverlayShared.globalOverlay.showOverlayAtItemOffset(overlay, root, Qt.point(xOffsetForEditFrame, root.height + Style.margin))
|
||||||
|
} else {
|
||||||
|
OverlayShared.globalOverlay.dismissOverlay()
|
||||||
|
searchCompleter.clear();
|
||||||
|
if (!airport.valid) {
|
||||||
|
text = ""; // ensure we always contain a valid ICAO or nothing
|
||||||
|
// we don't want this to trigger a search ....
|
||||||
|
searchTimer.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
NavaidSearch {
|
||||||
|
id: searchCompleter
|
||||||
|
airportsOnly: true
|
||||||
|
maxResults: 20
|
||||||
|
|
||||||
|
onSearchComplete: {
|
||||||
|
if (exactMatch !== 0) {
|
||||||
|
selectAirport(exactMatch)
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onTextChanged: {
|
||||||
|
searchTimer.restart();
|
||||||
|
}
|
||||||
|
|
||||||
|
Timer {
|
||||||
|
id: searchTimer
|
||||||
|
interval: 400
|
||||||
|
onTriggered: {
|
||||||
|
if (root.text.length >= 2) {
|
||||||
|
searchCompleter.setSearch(root.text, NavaidSearch.Airplane)
|
||||||
|
} else {
|
||||||
|
// ensure we update with no search (cancel, effectively)
|
||||||
|
searchCompleter.clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
StyledText {
|
||||||
|
anchors.left: parent.right
|
||||||
|
anchors.leftMargin: Style.margin
|
||||||
|
anchors.verticalCenter: parent.verticalCenter
|
||||||
|
text: airport.name
|
||||||
|
visible: airport.valid
|
||||||
|
width: Style.strutSize * 3
|
||||||
|
elide: Text.ElideRight
|
||||||
|
}
|
||||||
|
|
||||||
|
Component {
|
||||||
|
id: overlay
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: selectionPopup
|
||||||
|
color: "white"
|
||||||
|
height: choicesColumn.childrenRect.height + Style.margin * 2
|
||||||
|
width: choicesColumn.width + Style.margin * 2
|
||||||
|
|
||||||
|
visible: searchCompleter.haveExistingSearch
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
border.width: 1
|
||||||
|
border.color: Style.minorFrameColor
|
||||||
|
anchors.fill: parent
|
||||||
|
}
|
||||||
|
|
||||||
|
// choice layout column
|
||||||
|
Column {
|
||||||
|
id: choicesColumn
|
||||||
|
spacing: Style.margin
|
||||||
|
x: Style.margin
|
||||||
|
y: Style.margin
|
||||||
|
width: menuWidth
|
||||||
|
|
||||||
|
|
||||||
|
function calculateMenuWidth()
|
||||||
|
{
|
||||||
|
var minWidth = 0;
|
||||||
|
for (var i = 0; i < choicesRepeater.count; i++) {
|
||||||
|
minWidth = Math.max(minWidth, choicesRepeater.itemAt(i).implicitWidth);
|
||||||
|
}
|
||||||
|
return minWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
readonly property int menuWidth: calculateMenuWidth()
|
||||||
|
|
||||||
|
// main item repeater
|
||||||
|
Repeater {
|
||||||
|
id: choicesRepeater
|
||||||
|
model: searchCompleter
|
||||||
|
delegate:
|
||||||
|
Text {
|
||||||
|
id: choiceText
|
||||||
|
|
||||||
|
text: model.ident + " - " + model.name
|
||||||
|
height: implicitHeight + Style.margin
|
||||||
|
font.pixelSize: Style.baseFontPixelSize
|
||||||
|
color: choiceArea.containsMouse ? Style.themeColor : Style.baseTextColor
|
||||||
|
|
||||||
|
MouseArea {
|
||||||
|
id: choiceArea
|
||||||
|
width: selectionPopup.width // full width of the popup
|
||||||
|
height: parent.height
|
||||||
|
hoverEnabled: true
|
||||||
|
|
||||||
|
onClicked: {
|
||||||
|
root.selectAirport(model.guid)
|
||||||
|
root.focus = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // of Text delegate
|
||||||
|
} // text repeater
|
||||||
|
} // text column
|
||||||
|
}
|
||||||
|
} // of overlay component
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import QtQuick 2.4
|
import QtQuick 2.4
|
||||||
import FlightGear.Launcher 1.0
|
import FlightGear.Launcher 1.0
|
||||||
|
import FlightGear 1.0
|
||||||
import "."
|
import "."
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -128,20 +129,17 @@ Item {
|
||||||
width: parent.width
|
width: parent.width
|
||||||
spacing: Style.margin
|
spacing: Style.margin
|
||||||
|
|
||||||
IntegerSpinbox {
|
NumericalEdit {
|
||||||
label: qsTr("Cruise speed:")
|
label: qsTr("Cruise speed:")
|
||||||
suffix: "kts"
|
unitsMode: Units.Speed
|
||||||
min: 0
|
|
||||||
max: 10000 // more for spaceships?
|
|
||||||
step: 5
|
|
||||||
maxDigits: 5
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// padding
|
// padding
|
||||||
Item { width: Style.strutSize; height: 1 }
|
Item { width: Style.strutSize; height: 1 }
|
||||||
|
|
||||||
LocationAltitudeRow {
|
NumericalEdit {
|
||||||
|
label: qsTr("Cruise altitude:")
|
||||||
|
unitsMode: Units.AltitudeIncludingMeters
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -170,7 +168,7 @@ Item {
|
||||||
|
|
||||||
TimeEdit {
|
TimeEdit {
|
||||||
id: enrouteEstimate
|
id: enrouteEstimate
|
||||||
label: qsTr("Estimate enroute time:")
|
label: qsTr("Estimated enroute time:")
|
||||||
}
|
}
|
||||||
|
|
||||||
Item { width: Style.strutSize; height: 1 }
|
Item { width: Style.strutSize; height: 1 }
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import QtQuick 2.4
|
import QtQuick 2.4
|
||||||
|
import FlightGear 1.0
|
||||||
import "."
|
import "."
|
||||||
|
|
||||||
Item {
|
Item {
|
||||||
|
@ -158,5 +159,15 @@ Item {
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Overlay {
|
||||||
|
id: popupOverlay
|
||||||
|
anchors.fill: parent
|
||||||
|
z: 200
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
OverlayShared.globalOverlay = this
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,6 +16,8 @@ FocusScope {
|
||||||
|
|
||||||
property bool useFullWidth: false
|
property bool useFullWidth: false
|
||||||
|
|
||||||
|
readonly property int xOffsetForEditFrame: editFrame.x
|
||||||
|
|
||||||
implicitHeight: editFrame.height
|
implicitHeight: editFrame.height
|
||||||
implicitWidth: suggestedWidth + label.implicitWidth + (Style.margin * 3)
|
implicitWidth: suggestedWidth + label.implicitWidth + (Style.margin * 3)
|
||||||
|
|
||||||
|
|
31
src/GUI/qml/Overlay.qml
Normal file
31
src/GUI/qml/Overlay.qml
Normal file
|
@ -0,0 +1,31 @@
|
||||||
|
import QtQuick 2.4
|
||||||
|
|
||||||
|
Item {
|
||||||
|
|
||||||
|
function showOverlay(comp)
|
||||||
|
{
|
||||||
|
activeOverlayLoader.sourceComponent = comp;
|
||||||
|
activeOverlayLoader.visible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
function showOverlayAtItemOffset(comp, item, offset)
|
||||||
|
{
|
||||||
|
var pt = mapFromItem(item, offset.x, offset.y)
|
||||||
|
activeOverlayLoader.sourceComponent = comp;
|
||||||
|
activeOverlayLoader.visible = true;
|
||||||
|
activeOverlayLoader.x = pt.x;
|
||||||
|
activeOverlayLoader.y = pt.y;
|
||||||
|
}
|
||||||
|
|
||||||
|
function dismissOverlay()
|
||||||
|
{
|
||||||
|
activeOverlayLoader.sourceComponent = null
|
||||||
|
activeOverlayLoader.visible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
Loader
|
||||||
|
{
|
||||||
|
id: activeOverlayLoader
|
||||||
|
// no size, size to the component
|
||||||
|
}
|
||||||
|
}
|
6
src/GUI/qml/OverlayShared.qml
Normal file
6
src/GUI/qml/OverlayShared.qml
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
pragma Singleton
|
||||||
|
import QtQuick 2.0
|
||||||
|
|
||||||
|
QtObject {
|
||||||
|
property var globalOverlay
|
||||||
|
}
|
|
@ -115,6 +115,8 @@
|
||||||
<file>qml/TimeEdit.qml</file>
|
<file>qml/TimeEdit.qml</file>
|
||||||
<file>qml/AirportEntry.qml</file>
|
<file>qml/AirportEntry.qml</file>
|
||||||
<file>qml/NumericalEdit.qml</file>
|
<file>qml/NumericalEdit.qml</file>
|
||||||
|
<file>qml/Overlay.qml</file>
|
||||||
|
<file>qml/OverlayShared.qml</file>
|
||||||
</qresource>
|
</qresource>
|
||||||
<qresource prefix="/preview">
|
<qresource prefix="/preview">
|
||||||
<file alias="close-icon">preview-close.png</file>
|
<file alias="close-icon">preview-close.png</file>
|
||||||
|
|
|
@ -2444,7 +2444,7 @@ public:
|
||||||
bool quit;
|
bool quit;
|
||||||
};
|
};
|
||||||
|
|
||||||
NavDataCache::ThreadedGUISearch::ThreadedGUISearch(const std::string& term) :
|
NavDataCache::ThreadedGUISearch::ThreadedGUISearch(const std::string& term, bool onlyAirports) :
|
||||||
d(new ThreadedGUISearchPrivate)
|
d(new ThreadedGUISearchPrivate)
|
||||||
{
|
{
|
||||||
SGPath p = NavDataCache::instance()->path();
|
SGPath p = NavDataCache::instance()->path();
|
||||||
|
@ -2452,8 +2452,14 @@ NavDataCache::ThreadedGUISearch::ThreadedGUISearch(const std::string& term) :
|
||||||
std::string pathUtf8 = p.utf8Str();
|
std::string pathUtf8 = p.utf8Str();
|
||||||
sqlite3_open_v2(pathUtf8.c_str(), &d->db, openFlags, NULL);
|
sqlite3_open_v2(pathUtf8.c_str(), &d->db, openFlags, NULL);
|
||||||
|
|
||||||
std::string sql = "SELECT rowid FROM positioned WHERE name LIKE '%" + term
|
std::string sql;
|
||||||
+ "%' AND ((type >= 1 AND type <= 3) OR ((type >= 9 AND type <= 11))) ";
|
if (onlyAirports) {
|
||||||
|
sql = "SELECT rowid FROM positioned WHERE name LIKE '%" + term
|
||||||
|
+ "%' AND (type >= 1 AND type <= 3)";
|
||||||
|
} else {
|
||||||
|
sql = "SELECT rowid FROM positioned WHERE name LIKE '%" + term
|
||||||
|
+ "%' AND ((type >= 1 AND type <= 3) OR ((type >= 9 AND type <= 11))) ";
|
||||||
|
}
|
||||||
sqlite3_prepare_v2(d->db, sql.c_str(), sql.length(), &d->query, NULL);
|
sqlite3_prepare_v2(d->db, sql.c_str(), sql.length(), &d->query, NULL);
|
||||||
|
|
||||||
d->start();
|
d->start();
|
||||||
|
|
|
@ -309,7 +309,7 @@ public:
|
||||||
class ThreadedGUISearch
|
class ThreadedGUISearch
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
ThreadedGUISearch(const std::string& term);
|
ThreadedGUISearch(const std::string& term, bool onlyAirports);
|
||||||
~ThreadedGUISearch();
|
~ThreadedGUISearch();
|
||||||
|
|
||||||
PositionedIDVec results() const;
|
PositionedIDVec results() const;
|
||||||
|
|
Loading…
Reference in a new issue