From 09cac140892bc578301747ea284e847531448924 Mon Sep 17 00:00:00 2001 From: James Turner Date: Tue, 3 Nov 2015 15:28:36 -0600 Subject: [PATCH] Work on LocationWidget for Qt launcher --- src/GUI/AirportDiagram.cxx | 210 +++++------ src/GUI/AirportDiagram.hxx | 50 ++- src/GUI/BaseDiagram.cxx | 205 ++++++++++ src/GUI/BaseDiagram.hxx | 65 ++++ src/GUI/CMakeLists.txt | 9 +- src/GUI/Launcher.ui | 208 +---------- src/GUI/LocationWidget.cxx | 660 +++++++++++++++++++++++++++++++++ src/GUI/LocationWidget.hxx | 90 +++++ src/GUI/LocationWidget.ui | 385 +++++++++++++++++++ src/GUI/NavaidDiagram.cxx | 52 +++ src/GUI/NavaidDiagram.hxx | 61 +++ src/GUI/QtLauncher.cxx | 226 ++--------- src/GUI/QtLauncher_private.hxx | 20 - 13 files changed, 1676 insertions(+), 565 deletions(-) create mode 100644 src/GUI/BaseDiagram.cxx create mode 100644 src/GUI/BaseDiagram.hxx create mode 100644 src/GUI/LocationWidget.cxx create mode 100644 src/GUI/LocationWidget.hxx create mode 100644 src/GUI/LocationWidget.ui create mode 100644 src/GUI/NavaidDiagram.cxx create mode 100644 src/GUI/NavaidDiagram.hxx diff --git a/src/GUI/AirportDiagram.cxx b/src/GUI/AirportDiagram.cxx index f347c16fd..07cd4f91e 100644 --- a/src/GUI/AirportDiagram.cxx +++ b/src/GUI/AirportDiagram.cxx @@ -32,18 +32,6 @@ #include #include -/* equatorial and polar earth radius */ -const float rec = 6378137; // earth radius, equator (?) -const float rpol = 6356752.314f; // earth radius, polar (?) - -//Returns Earth radius at a given latitude (Ellipsoide equation with two equal axis) -static float earth_radius_lat( float lat ) -{ - double a = cos(lat)/rec; - double b = sin(lat)/rpol; - return 1.0f / sqrt( a * a + b * b ); -} - static double distanceToLineSegment(const QVector2D& p, const QVector2D& a, const QVector2D& b, double* outT = NULL) { @@ -89,26 +77,29 @@ static double distanceToLineSegment(const QVector2D& p, const QVector2D& a, } AirportDiagram::AirportDiagram(QWidget* pr) : -QWidget(pr) + BaseDiagram(pr), + m_approachDistanceNm(-1.0) +{ +} + +AirportDiagram::~AirportDiagram() { - setSizePolicy(QSizePolicy::MinimumExpanding, - QSizePolicy::MinimumExpanding); - setMinimumSize(100, 100); + } void AirportDiagram::setAirport(FGAirportRef apt) { m_airport = apt; m_projectionCenter = apt ? apt->geod() : SGGeod(); - m_scale = 1.0; - m_bounds = QRectF(); // clear m_runways.clear(); + m_approachDistanceNm = -1.0; if (apt) { buildTaxiways(); buildPavements(); } + recomputeBounds(true); update(); } @@ -127,6 +118,13 @@ void AirportDiagram::setSelectedRunway(FGRunwayRef r) update(); } +void AirportDiagram::setApproachExtensionDistance(double distanceNm) +{ + m_approachDistanceNm = distanceNm; + recomputeBounds(true); + update(); +} + void AirportDiagram::addRunway(FGRunwayRef rwy) { Q_FOREACH(RunwayData rd, m_runways) { @@ -135,76 +133,78 @@ void AirportDiagram::addRunway(FGRunwayRef rwy) } } - QPointF p1 = project(rwy->geod()), - p2 = project(rwy->end()); - extendBounds(p1); - extendBounds(p2); - RunwayData r; - r.p1 = p1; - r.p2 = p2; + r.p1 = project(rwy->geod()); + r.p2 = project(rwy->end()); r.widthM = qRound(rwy->widthM()); r.runway = rwy; m_runways.append(r); + + recomputeBounds(false); update(); } -void AirportDiagram::addParking(FGParking* park) +void AirportDiagram::doComputeBounds() { - QPointF p = project(park->geod()); - extendBounds(p); - update(); + Q_FOREACH(const RunwayData& r, m_runways) { + extendBounds(r.p1); + extendBounds(r.p2); + } + + Q_FOREACH(const TaxiwayData& t, m_taxiways) { + extendBounds(t.p1); + extendBounds(t.p2); + } + + Q_FOREACH(const ParkingData& p, m_parking) { + extendBounds(p.pt); + } + + if (m_selectedRunway && (m_approachDistanceNm > 0.0)) { + double d = SG_NM_TO_METER * m_approachDistanceNm; + QPointF pt = project(m_selectedRunway->pointOnCenterline(-d)); + extendBounds(pt); + } } -QTransform AirportDiagram::transform() const +void AirportDiagram::addParking(FGParkingRef park) { - // fit bounds within our available space, allowing for a margin - const int MARGIN = 32; // pixels - double ratioInX = (width() - MARGIN * 2) / m_bounds.width(); - double ratioInY = (height() - MARGIN * 2) / m_bounds.height(); - double scale = std::min(ratioInX, ratioInY); - - QTransform t; - t.translate(width() / 2, height() / 2); // center projection origin in the widget - t.scale(scale, scale); - // center the bounding box (may not be at the origin) - t.translate(-m_bounds.center().x(), -m_bounds.center().y()); - return t; + ParkingData pd = { project(park->geod()), park }; + m_parking.push_back(pd); + recomputeBounds(false); + update(); } -void AirportDiagram::paintEvent(QPaintEvent* pe) -{ - QPainter p(this); - p.setRenderHints(QPainter::Antialiasing); - p.fillRect(rect(), QColor(0x3f, 0x3f, 0x3f)); - // fit bounds within our available space, allowing for a margin - const int MARGIN = 32; // pixels - double ratioInX = (width() - MARGIN * 2) / m_bounds.width(); - double ratioInY = (height() - MARGIN * 2) / m_bounds.height(); - double scale = std::min(ratioInX, ratioInY); +void AirportDiagram::paintContents(QPainter* p) +{ + // fit bounds within our available space, allowing for a margin +// const int MARGIN = 32; // pixels + // double ratioInX = (width() - MARGIN * 2) / m_bounds.width(); + // double ratioInY = (height() - MARGIN * 2) / m_bounds.height(); + // double scale = std::min(ratioInX, ratioInY); QTransform t(transform()); - p.setTransform(t); + p->setTransform(t); // pavements QBrush brush(QColor(0x9f, 0x9f, 0x9f)); Q_FOREACH(const QPainterPath& path, m_pavements) { - p.drawPath(path); + p->drawPath(path); } // taxiways Q_FOREACH(const TaxiwayData& t, m_taxiways) { QPen pen(QColor(0x9f, 0x9f, 0x9f)); pen.setWidth(t.widthM); - p.setPen(pen); - p.drawLine(t.p1, t.p2); + p->setPen(pen); + p->drawLine(t.p1, t.p2); } // runways QFont f; f.setPixelSize(14); - p.setFont(f); + p->setFont(f); Q_FOREACH(const RunwayData& r, m_runways) { QColor color(Qt::magenta); @@ -212,40 +212,54 @@ void AirportDiagram::paintEvent(QPaintEvent* pe) color = Qt::yellow; } - p.setTransform(t); + p->setTransform(t); QPen pen(color); pen.setWidth(r.widthM); - p.setPen(pen); + p->setPen(pen); - p.drawLine(r.p1, r.p2); + p->drawLine(r.p1, r.p2); // draw idents QString ident = QString::fromStdString(r.runway->ident()); - p.translate(r.p1); - p.rotate(r.runway->headingDeg()); + p->translate(r.p1); + p->rotate(r.runway->headingDeg()); // invert scaling factor so we can use screen pixel sizes here - p.scale(1.0 / scale, 1.0/ scale); + p->scale(1.0 / m_scale, 1.0/ m_scale); - p.setPen((r.runway == m_selectedRunway) ? Qt::yellow : Qt::magenta); - p.drawText(QRect(-100, 5, 200, 200), ident, Qt::AlignHCenter | Qt::AlignTop); + p->setPen((r.runway == m_selectedRunway) ? Qt::yellow : Qt::magenta); + p->drawText(QRect(-100, 5, 200, 200), ident, Qt::AlignHCenter | Qt::AlignTop); FGRunway* recip = r.runway->reciprocalRunway(); QString recipIdent = QString::fromStdString(recip->ident()); - p.setTransform(t); - p.translate(r.p2); - p.rotate(recip->headingDeg()); - p.scale(1.0 / scale, 1.0/ scale); + p->setTransform(t); + p->translate(r.p2); + p->rotate(recip->headingDeg()); + p->scale(1.0 / m_scale, 1.0/ m_scale); + + p->setPen((r.runway->reciprocalRunway() == m_selectedRunway) ? Qt::yellow : Qt::magenta); + p->drawText(QRect(-100, 5, 200, 200), recipIdent, Qt::AlignHCenter | Qt::AlignTop); + } - p.setPen((r.runway->reciprocalRunway() == m_selectedRunway) ? Qt::yellow : Qt::magenta); - p.drawText(QRect(-100, 5, 200, 200), recipIdent, Qt::AlignHCenter | Qt::AlignTop); + if (m_selectedRunway && (m_approachDistanceNm > 0.0)) { + p->setTransform(t); + // draw approach extension point + double d = SG_NM_TO_METER * m_approachDistanceNm; + QPointF pt = project(m_selectedRunway->pointOnCenterline(-d)); + QPointF pt2 = project(m_selectedRunway->geod()); + QPen pen(Qt::yellow, 10); + p->setPen(pen); + p->drawLine(pt, pt2); } } void AirportDiagram::mouseReleaseEvent(QMouseEvent* me) { + if (m_didPan) + return; // ignore panning drag+release ops here + QTransform t(transform()); double minDist = std::numeric_limits::max(); FGRunwayRef bestRunway; @@ -271,57 +285,6 @@ void AirportDiagram::mouseReleaseEvent(QMouseEvent* me) } } -void AirportDiagram::extendBounds(const QPointF& p) -{ - if (p.x() < m_bounds.left()) { - m_bounds.setLeft(p.x()); - } else if (p.x() > m_bounds.right()) { - m_bounds.setRight(p.x()); - } - - if (p.y() < m_bounds.top()) { - m_bounds.setTop(p.y()); - } else if (p.y() > m_bounds.bottom()) { - m_bounds.setBottom(p.y()); - } -} - -QPointF AirportDiagram::project(const SGGeod& geod) const -{ - double r = earth_radius_lat(geod.getLatitudeRad()); - double ref_lat = m_projectionCenter.getLatitudeRad(), - ref_lon = m_projectionCenter.getLongitudeRad(), - lat = geod.getLatitudeRad(), - lon = geod.getLongitudeRad(), - lonDiff = lon - ref_lon; - - double c = acos( sin(ref_lat) * sin(lat) + cos(ref_lat) * cos(lat) * cos(lonDiff) ); - if (c == 0.0) { - // angular distance from center is 0 - return QPointF(0.0, 0.0); - } - - double k = c / sin(c); - double x, y; - if (ref_lat == (90 * SG_DEGREES_TO_RADIANS)) - { - x = (SGD_PI / 2 - lat) * sin(lonDiff); - y = -(SGD_PI / 2 - lat) * cos(lonDiff); - } - else if (ref_lat == -(90 * SG_DEGREES_TO_RADIANS)) - { - x = (SGD_PI / 2 + lat) * sin(lonDiff); - y = (SGD_PI / 2 + lat) * cos(lonDiff); - } - else - { - x = k * cos(lat) * sin(lonDiff); - y = k * ( cos(ref_lat) * sin(lat) - sin(ref_lat) * cos(lat) * cos(lonDiff) ); - } - - return QPointF(x, -y) * r * m_scale; -} - void AirportDiagram::buildTaxiways() { m_taxiways.clear(); @@ -331,8 +294,7 @@ void AirportDiagram::buildTaxiways() TaxiwayData td; td.p1 = project(tx->geod()); td.p2 = project(tx->pointOnCenterline(tx->lengthM())); - extendBounds(td.p1); - extendBounds(td.p2); + td.widthM = tx->widthM(); m_taxiways.append(td); } diff --git a/src/GUI/AirportDiagram.hxx b/src/GUI/AirportDiagram.hxx index 07eaf8297..61a015407 100644 --- a/src/GUI/AirportDiagram.hxx +++ b/src/GUI/AirportDiagram.hxx @@ -18,49 +18,48 @@ // along with this program; if not, write to the Free Software // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -#include -#include +#ifndef GUI_AIRPORT_DIAGRAM_HXX +#define GUI_AIRPORT_DIAGRAM_HXX -#include +#include "BaseDiagram.hxx" + +#include +#include #include -class AirportDiagram : public QWidget +class AirportDiagram : public BaseDiagram { Q_OBJECT public: AirportDiagram(QWidget* pr); + virtual ~AirportDiagram(); void setAirport(FGAirportRef apt); void addRunway(FGRunwayRef rwy); - void addParking(FGParking* park); + void addParking(FGParkingRef park); FGRunwayRef selectedRunway() const; void setSelectedRunway(FGRunwayRef r); + + void setApproachExtensionDistance(double distanceNm); Q_SIGNALS: void clickedRunway(FGRunwayRef rwy); + void clickedParking(FGParkingRef park); protected: - virtual void paintEvent(QPaintEvent* pe); - // wheel event for zoom - - // mouse drag for pan virtual void mouseReleaseEvent(QMouseEvent* me); + void paintContents(QPainter*) Q_DECL_OVERRIDE; + void doComputeBounds() Q_DECL_OVERRIDE; private: - void extendBounds(const QPointF& p); - QPointF project(const SGGeod& geod) const; - QTransform transform() const; - + void buildTaxiways(); void buildPavements(); FGAirportRef m_airport; - SGGeod m_projectionCenter; - double m_scale; - QRectF m_bounds; struct RunwayData { QPointF p1, p2; @@ -68,7 +67,7 @@ private: FGRunwayRef runway; }; - QList m_runways; + QVector m_runways; struct TaxiwayData { QPointF p1, p2; @@ -80,8 +79,19 @@ private: } }; - QList m_taxiways; - QList m_pavements; - + QVector m_taxiways; + QVector m_pavements; + + struct ParkingData + { + QPointF pt; + FGParkingRef parking; + }; + + QVector m_parking; + + double m_approachDistanceNm; FGRunwayRef m_selectedRunway; }; + +#endif // of GUI_AIRPORT_DIAGRAM_HXX diff --git a/src/GUI/BaseDiagram.cxx b/src/GUI/BaseDiagram.cxx new file mode 100644 index 000000000..b1e114c07 --- /dev/null +++ b/src/GUI/BaseDiagram.cxx @@ -0,0 +1,205 @@ +// BaseDiagram.cxx - part of GUI launcher using Qt5 +// +// Written by James Turner, started December 2014. +// +// Copyright (C) 2014 James Turner +// +// 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 "BaseDiagram.hxx" + +#include + +#include +#include +#include +#include + +/* equatorial and polar earth radius */ +const float rec = 6378137; // earth radius, equator (?) +const float rpol = 6356752.314f; // earth radius, polar (?) + +//Returns Earth radius at a given latitude (Ellipsoide equation with two equal axis) +static float earth_radius_lat( float lat ) +{ + double a = cos(lat)/rec; + double b = sin(lat)/rpol; + return 1.0f / sqrt( a * a + b * b ); +} + +BaseDiagram::BaseDiagram(QWidget* pr) : + QWidget(pr), + m_autoScalePan(true), + m_wheelAngleDeltaAccumulator(0) +{ + setSizePolicy(QSizePolicy::MinimumExpanding, + QSizePolicy::MinimumExpanding); + setMinimumSize(100, 100); +} + +QTransform BaseDiagram::transform() const +{ + QTransform t; + t.translate(width() / 2, height() / 2); // center projection origin in the widget + t.scale(m_scale, m_scale); + + // apply any pan offset that exists + t.translate(m_panOffset.x(), m_panOffset.y()); + // center the bounding box (may not be at the origin) + t.translate(-m_bounds.center().x(), -m_bounds.center().y()); + return t; +} + +void BaseDiagram::paintEvent(QPaintEvent* pe) +{ + QPainter p(this); + p.setRenderHints(QPainter::Antialiasing); + p.fillRect(rect(), QColor(0x3f, 0x3f, 0x3f)); + + if (m_autoScalePan) { + // fit bounds within our available space, allowing for a margin + const int MARGIN = 32; // pixels + double ratioInX = (width() - MARGIN * 2) / m_bounds.width(); + double ratioInY = (height() - MARGIN * 2) / m_bounds.height(); + m_scale = std::min(ratioInX, ratioInY); + } + + QTransform t(transform()); + p.setTransform(t); + + paintContents(&p); +} + +void BaseDiagram::mousePressEvent(QMouseEvent *me) +{ + m_lastMousePos = me->pos(); + m_didPan = false; +} + +void BaseDiagram::mouseMoveEvent(QMouseEvent *me) +{ + m_autoScalePan = false; + + QPointF delta = me->pos() - m_lastMousePos; + m_lastMousePos = me->pos(); + + // offset is stored in metres so we don't have to modify it when + // zooming + m_panOffset += (delta / m_scale); + m_didPan = true; + + update(); +} + +int intSign(int v) +{ + return (v == 0) ? 0 : (v < 0) ? -1 : 1; +} + +void BaseDiagram::wheelEvent(QWheelEvent *we) +{ + m_autoScalePan = false; + + int delta = we->angleDelta().y(); + if (intSign(m_wheelAngleDeltaAccumulator) != intSign(delta)) { + m_wheelAngleDeltaAccumulator = 0; + } + + m_wheelAngleDeltaAccumulator += delta; + if (m_wheelAngleDeltaAccumulator > 120) { + m_wheelAngleDeltaAccumulator = 0; + m_scale *= 2.0; + } else if (m_wheelAngleDeltaAccumulator < 120) { + m_wheelAngleDeltaAccumulator = 0; + m_scale *= 0.5; + } + + update(); +} + +void BaseDiagram::paintContents(QPainter*) +{ + +} + +void BaseDiagram::recomputeBounds(bool resetZoom) +{ + m_bounds = QRectF(); + doComputeBounds(); + + if (resetZoom) { + m_autoScalePan = true; + m_scale = 1.0; + m_panOffset = QPointF(); + } + + update(); +} + +void BaseDiagram::doComputeBounds() +{ + // no-op in the base class +} + +void BaseDiagram::extendBounds(const QPointF& p) +{ + if (p.x() < m_bounds.left()) { + m_bounds.setLeft(p.x()); + } else if (p.x() > m_bounds.right()) { + m_bounds.setRight(p.x()); + } + + if (p.y() < m_bounds.top()) { + m_bounds.setTop(p.y()); + } else if (p.y() > m_bounds.bottom()) { + m_bounds.setBottom(p.y()); + } +} + +QPointF BaseDiagram::project(const SGGeod& geod) const +{ + double r = earth_radius_lat(geod.getLatitudeRad()); + double ref_lat = m_projectionCenter.getLatitudeRad(), + ref_lon = m_projectionCenter.getLongitudeRad(), + lat = geod.getLatitudeRad(), + lon = geod.getLongitudeRad(), + lonDiff = lon - ref_lon; + + double c = acos( sin(ref_lat) * sin(lat) + cos(ref_lat) * cos(lat) * cos(lonDiff) ); + if (c == 0.0) { + // angular distance from center is 0 + return QPointF(0.0, 0.0); + } + + double k = c / sin(c); + double x, y; + if (ref_lat == (90 * SG_DEGREES_TO_RADIANS)) + { + x = (SGD_PI / 2 - lat) * sin(lonDiff); + y = -(SGD_PI / 2 - lat) * cos(lonDiff); + } + else if (ref_lat == -(90 * SG_DEGREES_TO_RADIANS)) + { + x = (SGD_PI / 2 + lat) * sin(lonDiff); + y = (SGD_PI / 2 + lat) * cos(lonDiff); + } + else + { + x = k * cos(lat) * sin(lonDiff); + y = k * ( cos(ref_lat) * sin(lat) - sin(ref_lat) * cos(lat) * cos(lonDiff) ); + } + + return QPointF(x, -y) * r; +} diff --git a/src/GUI/BaseDiagram.hxx b/src/GUI/BaseDiagram.hxx new file mode 100644 index 000000000..782344e83 --- /dev/null +++ b/src/GUI/BaseDiagram.hxx @@ -0,0 +1,65 @@ +// BaseDiagram.hxx - part of GUI launcher using Qt5 +// +// Written by James Turner, started October 2015. +// +// Copyright (C) 2014 James Turner +// +// 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 GUI_BASEDIAGRAM_HXX +#define GUI_BASEDIAGRAM_HXX + +#include +#include + +#include + +class BaseDiagram : public QWidget +{ + Q_OBJECT +public: + BaseDiagram(QWidget* pr); + + + +protected: + virtual void paintEvent(QPaintEvent* pe); + + virtual void mousePressEvent(QMouseEvent* me); + virtual void mouseMoveEvent(QMouseEvent* me); + + virtual void wheelEvent(QWheelEvent* we); + + virtual void paintContents(QPainter*); + +protected: + void recomputeBounds(bool resetZoom); + + virtual void doComputeBounds(); + + void extendBounds(const QPointF& p); + QPointF project(const SGGeod& geod) const; + QTransform transform() const; + + SGGeod m_projectionCenter; + double m_scale; + QRectF m_bounds; + bool m_autoScalePan; + QPointF m_panOffset, m_lastMousePos; + int m_wheelAngleDeltaAccumulator; + bool m_didPan; +}; + +#endif // of GUI_BASEDIAGRAM_HXX diff --git a/src/GUI/CMakeLists.txt b/src/GUI/CMakeLists.txt index b8475f81c..bdff1cce5 100644 --- a/src/GUI/CMakeLists.txt +++ b/src/GUI/CMakeLists.txt @@ -74,7 +74,8 @@ if (HAVE_QT) EditRatingsFilterDialog.ui SetupRootDialog.ui AddCatalogDialog.ui - PathsDialog.ui) + PathsDialog.ui + LocationWidget.ui) qt5_add_resources(qrc_sources resources.qrc) include_directories(${PROJECT_BINARY_DIR}/src/GUI) @@ -82,8 +83,12 @@ if (HAVE_QT) add_library(fglauncher QtLauncher.cxx QtLauncher.hxx QtLauncher_private.hxx + BaseDiagram.cxx + BaseDiagram.hxx AirportDiagram.cxx AirportDiagram.hxx + NavaidDiagram.cxx + NavaidDiagram.hxx EditRatingsFilterDialog.cxx EditRatingsFilterDialog.hxx SetupRootDialog.cxx @@ -98,6 +103,8 @@ if (HAVE_QT) AddCatalogDialog.hxx PathsDialog.cxx PathsDialog.hxx + LocationWidget.cxx + LocationWidget.hxx ${uic_sources} ${qrc_sources}) diff --git a/src/GUI/Launcher.ui b/src/GUI/Launcher.ui index 1eb885204..9e696ca28 100644 --- a/src/GUI/Launcher.ui +++ b/src/GUI/Launcher.ui @@ -55,7 +55,7 @@ - + @@ -231,207 +231,7 @@ 4 - - - - - Search: - - - - - - - Enter an ICAO code, navaid or search by name - - - - - - - - 0 - 0 - - - - false - - - false - - - - - - - - - 0 - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - Parking: - - - - - - - On 10-mile final - - - - - - - Runway: - - - - - - - true - - - 359 - - - 5 - - - - - - - - - - - - - True - - - - - - - true - - - Bearing: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Bearing and distance offset: - - - - - - - Distance: - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - nm - - - 1 - - - 10000.000000000000000 - - - 10.000000000000000 - - - - - - - - 0 - 0 - - - - - - - - - - 0 - - - 0 - - - 0 - - - 0 - - - - - - - - - - - - TextLabel - - - Qt::AlignBottom|Qt::AlignHCenter - - - - - - - - - - Qt::AlignHCenter|Qt::AlignTop - - - - - - + @@ -721,9 +521,9 @@ - AirportDiagram + LocationWidget QWidget -
GUI/AirportDiagram.hxx
+
GUI/LocationWidget.hxx
1
diff --git a/src/GUI/LocationWidget.cxx b/src/GUI/LocationWidget.cxx new file mode 100644 index 000000000..ba840c1af --- /dev/null +++ b/src/GUI/LocationWidget.cxx @@ -0,0 +1,660 @@ +// LocationWidget.cxx - GUI launcher dialog using Qt5 +// +// Written by James Turner, started October 2015. +// +// Copyright (C) 2015 James Turner +// +// 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 "LocationWidget.hxx" +#include "ui_LocationWidget.h" + +#include +#include +#include +#include +#include + +#include "AirportDiagram.hxx" +#include "NavaidDiagram.hxx" + +#include +#include // for parking +#include
+#include +#include +#include
+#include
+ +const int MAX_RECENT_AIRPORTS = 32; + +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 + if (up == "FLD") { + changedWords.append("Field"); + continue; + } + + if (up == "MUNI") { + changedWords.append("Municipal"); + 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")) { + changedWords.append(w); + continue; + } + + QChar firstChar = w.at(0).toUpper(); + w = w.mid(1).toLower(); + w.prepend(firstChar); + + changedWords.append(w); + } + + return changedWords.join(QChar(' ')); +} + +QString formatGeodAsString(const SGGeod& geod) +{ + QChar ns = (geod.getLatitudeDeg() > 0.0) ? 'N' : 'S'; + QChar ew = (geod.getLongitudeDeg() > 0.0) ? 'E' : 'W'; + + return QString::number(fabs(geod.getLongitudeDeg()), 'f',2 ) + ew + " " + + QString::number(fabs(geod.getLatitudeDeg()), 'f',2 ) + ns; +} + +class IdentSearchFilter : public FGPositioned::TypeFilter +{ +public: + IdentSearchFilter() + { + addType(FGPositioned::AIRPORT); + addType(FGPositioned::SEAPORT); + addType(FGPositioned::HELIPAD); + addType(FGPositioned::VOR); + addType(FGPositioned::FIX); + addType(FGPositioned::NDB); + } +}; + +class NavSearchModel : public QAbstractListModel +{ + Q_OBJECT +public: + NavSearchModel() : + m_searchActive(false) + { + } + + void setSearch(QString t) + { + beginResetModel(); + + m_items.clear(); + m_ids.clear(); + + std::string term(t.toUpper().toStdString()); + + IdentSearchFilter filter; + FGPositionedList exactMatches = NavDataCache::instance()->findAllWithIdent(term, &filter, true); + + for (unsigned int i=0; iguid()); + m_items.push_back(exactMatches[i]); + } + endResetModel(); + + + m_search.reset(new NavDataCache::ThreadedGUISearch(term)); + QTimer::singleShot(100, this, &NavSearchModel::onSearchResultsPoll); + m_searchActive = true; + endResetModel(); + } + + bool isSearchActive() const + { + return m_searchActive; + } + + virtual int rowCount(const QModelIndex&) const + { + // if empty, return 1 for special 'no matches'? + return m_ids.size(); + } + + virtual QVariant data(const QModelIndex& index, int role) const + { + if (!index.isValid()) + return QVariant(); + + FGPositionedRef pos = itemAtRow(index.row()); + if (role == Qt::DisplayRole) { + if (pos->type() == FGPositioned::FIX) { + // fixes don't have a name, show position instead + return QString("Fix %1 (%2)").arg(QString::fromStdString(pos->ident())) + .arg(formatGeodAsString(pos->geod())); + } else { + QString name = fixNavaidName(QString::fromStdString(pos->name())); + return QString("%1: %2").arg(QString::fromStdString(pos->ident())).arg(name); + } + } + + if (role == Qt::EditRole) { + return QString::fromStdString(pos->ident()); + } + + if (role == Qt::UserRole) { + return static_cast(m_ids[index.row()]); + } + + return QVariant(); + } + + 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; + } +Q_SIGNALS: + void searchComplete(); + +private: + + + void onSearchResultsPoll() + { + PositionedIDVec newIds = m_search->results(); + + beginInsertRows(QModelIndex(), m_ids.size(), newIds.size() - 1); + for (unsigned int i=m_ids.size(); i < newIds.size(); ++i) { + m_ids.push_back(newIds[i]); + m_items.push_back(FGPositionedRef()); // null ref + } + endInsertRows(); + + if (m_search->isComplete()) { + m_searchActive = false; + m_search.reset(); + emit searchComplete(); + } else { + QTimer::singleShot(100, this, &NavSearchModel::onSearchResultsPoll); + } + } + +private: + PositionedIDVec m_ids; + mutable FGPositionedList m_items; + bool m_searchActive; + QScopedPointer m_search; +}; + + +LocationWidget::LocationWidget(QWidget *parent) : + QWidget(parent), + m_ui(new Ui::LocationWidget) +{ + m_ui->setupUi(this); + + + QIcon historyIcon(":/history-icon"); + m_ui->searchHistory->setIcon(historyIcon); + + m_ui->searchIcon->setPixmap(QPixmap(":/search-icon")); + + m_searchModel = new NavSearchModel; + m_ui->searchResultsList->setModel(m_searchModel); + connect(m_ui->searchResultsList, &QListView::clicked, + this, &LocationWidget::onSearchResultSelected); + connect(m_searchModel, &NavSearchModel::searchComplete, + this, &LocationWidget::onSearchComplete); + + connect(m_ui->runwayCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(updateDescription())); + connect(m_ui->parkingCombo, SIGNAL(currentIndexChanged(int)), + this, SLOT(updateDescription())); + connect(m_ui->runwayRadio, SIGNAL(toggled(bool)), + this, SLOT(updateDescription())); + connect(m_ui->parkingRadio, SIGNAL(toggled(bool)), + this, SLOT(updateDescription())); + connect(m_ui->onFinalCheckbox, SIGNAL(toggled(bool)), + this, SLOT(updateDescription())); + connect(m_ui->approachDistanceSpin, SIGNAL(valueChanged(int)), + this, SLOT(updateDescription())); + + connect(m_ui->airportDiagram, &AirportDiagram::clickedRunway, + this, &LocationWidget::onAirportDiagramClicked); + + connect(m_ui->locationSearchEdit, &QLineEdit::returnPressed, + this, &LocationWidget::onSearch); + + connect(m_ui->searchHistory, &QPushButton::clicked, + this, &LocationWidget::onPopupHistory); + + connect(m_ui->trueBearing, &QCheckBox::toggled, + this, &LocationWidget::onOffsetBearingTrueChanged); + connect(m_ui->offsetGroup, &QGroupBox::toggled, + this, &LocationWidget::onOffsetEnabledToggled); + connect(m_ui->trueBearing, &QCheckBox::toggled, this, + &LocationWidget::onOffsetDataChanged); + connect(m_ui->offsetBearingSpinbox, SIGNAL(valueChanged(int)), + this, SLOT(onOffsetDataChanged())); + connect(m_ui->offsetNmSpinbox, SIGNAL(valueChanged(double)), + this, SLOT(onOffsetDataChanged())); + + m_backButton = new QToolButton(this); + m_backButton->setGeometry(0, 0, 32, 32); + m_backButton->setIcon(QIcon(":/search-icon")); + m_backButton->raise(); + + connect(m_backButton, &QAbstractButton::clicked, + this, &LocationWidget::onBackToSearch); + +// force various pieces of UI into sync + onOffsetEnabledToggled(m_ui->offsetGroup->isChecked()); + onBackToSearch(); +} + +LocationWidget::~LocationWidget() +{ + delete m_ui; +} + +void LocationWidget::restoreSettings() +{ + QSettings settings; + Q_FOREACH(QVariant v, settings.value("recent-locations").toList()) { + m_recentAirports.push_back(v.toLongLong()); + } + + if (!m_recentAirports.empty()) { + setBaseLocation(NavDataCache::instance()->loadById(m_recentAirports.front())); + } + + updateDescription(); +} + +bool LocationWidget::shouldStartPaused() const +{ + qWarning() << Q_FUNC_INFO << "implement me"; + return false; +} + +void LocationWidget::saveSettings() +{ + QSettings settings; + + QVariantList locations; + Q_FOREACH(PositionedID v, m_recentAirports) { + locations.push_back(v); + } + + settings.setValue("recent-airports", locations); +} + +void LocationWidget::setLocationOptions() +{ + flightgear::Options* opt = flightgear::Options::sharedInstance(); + + if (!m_location) { + return; + } + + if (FGAirport::isAirportType(m_location.ptr())) { + FGAirport* apt = static_cast(m_location.ptr()); + opt->addOption("airport", apt->ident()); + + if (m_ui->runwayRadio->isChecked()) { + int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt(); + if (index >= 0) { + // explicit runway choice + opt->addOption("runway", apt->getRunwayByIndex(index)->ident()); + } + + if (m_ui->onFinalCheckbox->isChecked()) { + opt->addOption("glideslope", "3.0"); + opt->addOption("offset-distance", "10.0"); // in nautical miles + } + } else if (m_ui->parkingRadio->isChecked()) { + // parking selection + opt->addOption("parkpos", m_ui->parkingCombo->currentText().toStdString()); + } + // of location is an airport + } + + FGPositioned::Type ty = m_location->type(); + switch (ty) { + case FGPositioned::VOR: + case FGPositioned::NDB: + case FGPositioned::FIX: + // set disambiguation property + globals->get_props()->setIntValue("/sim/presets/navaid-id", + static_cast(m_location->guid())); + + // we always set 'fix', but really this is just to force positionInit + // code to check for the navaid-id value above. + opt->addOption("fix", m_location->ident()); + break; + default: + break; + } +} + +void LocationWidget::onSearch() +{ + QString search = m_ui->locationSearchEdit->text(); + m_searchModel->setSearch(search); + + if (m_searchModel->isSearchActive()) { + m_ui->searchStatusText->setText(QString("Searching for '%1'").arg(search)); + m_ui->searchIcon->setVisible(true); + } else if (m_searchModel->rowCount(QModelIndex()) == 1) { + setBaseLocation(m_searchModel->itemAtRow(0)); + } +} + +void LocationWidget::onSearchComplete() +{ + QString search = m_ui->locationSearchEdit->text(); + m_ui->searchIcon->setVisible(false); + m_ui->searchStatusText->setText(QString("Results for '%1'").arg(search)); + + int numResults = m_searchModel->rowCount(QModelIndex()); + if (numResults == 0) { + m_ui->searchStatusText->setText(QString("No matches for '%1'").arg(search)); + } else if (numResults == 1) { + setBaseLocation(m_searchModel->itemAtRow(0)); + } +} + +void LocationWidget::onLocationChanged() +{ + bool locIsAirport = FGAirport::isAirportType(m_location.ptr()); + m_backButton->show(); + + if (locIsAirport) { + m_ui->stack->setCurrentIndex(0); + FGAirport* apt = static_cast(m_location.ptr()); + m_ui->airportDiagram->setAirport(apt); + + m_ui->runwayRadio->setChecked(true); // default back to runway mode + // unless multiplayer is enabled ? + m_ui->airportDiagram->setEnabled(true); + + m_ui->runwayCombo->clear(); + m_ui->runwayCombo->addItem("Automatic", -1); + for (unsigned int r=0; rnumRunways(); ++r) { + FGRunwayRef rwy = apt->getRunwayByIndex(r); + // add runway with index as data role + m_ui->runwayCombo->addItem(QString::fromStdString(rwy->ident()), r); + + m_ui->airportDiagram->addRunway(rwy); + } + + m_ui->parkingCombo->clear(); + FGAirportDynamics* dynamics = apt->getDynamics(); + PositionedIDVec parkings = NavDataCache::instance()->airportItemsOfType(m_location->guid(), + FGPositioned::PARKING); + if (parkings.empty()) { + m_ui->parkingCombo->setEnabled(false); + m_ui->parkingRadio->setEnabled(false); + } else { + m_ui->parkingCombo->setEnabled(true); + m_ui->parkingRadio->setEnabled(true); + Q_FOREACH(PositionedID parking, parkings) { + FGParking* park = dynamics->getParking(parking); + m_ui->parkingCombo->addItem(QString::fromStdString(park->getName()), + static_cast(parking)); + + m_ui->airportDiagram->addParking(park); + } + } + + + } else {// of location is airport + // navaid + m_ui->stack->setCurrentIndex(1); + m_ui->navaidDiagram->setNavaid(m_location); + } +} + +void LocationWidget::onOffsetEnabledToggled(bool on) +{ + m_ui->offsetDistanceLabel->setEnabled(on); +// m_ui->offsetNmSpinbox->setEnabled(on); +// m_ui->offsetBearingSpinbox->setEnabled(on); +// m_ui->trueBearing->setEnabled(on); +// m_ui->offsetBearingLabel->setEnabled(on); +// m_ui->offsetDistanceLabel->setEnabled(on); +} + +void LocationWidget::onAirportDiagramClicked(FGRunwayRef rwy) +{ + if (rwy) { + m_ui->runwayRadio->setChecked(true); + int rwyIndex = m_ui->runwayCombo->findText(QString::fromStdString(rwy->ident())); + m_ui->runwayCombo->setCurrentIndex(rwyIndex); + m_ui->airportDiagram->setSelectedRunway(rwy); + } + + updateDescription(); +} + +QString LocationWidget::locationDescription() const +{ + if (!m_location) + return QString("No location selected"); + + bool locIsAirport = FGAirport::isAirportType(m_location.ptr()); + QString ident = QString::fromStdString(m_location->ident()), + name = QString::fromStdString(m_location->name()); + + if (locIsAirport) { + FGAirport* apt = static_cast(m_location.ptr()); + QString locationOnAirport; + + if (m_ui->runwayRadio->isChecked()) { + bool onFinal = m_ui->onFinalCheckbox->isChecked(); + int comboIndex = m_ui->runwayCombo->currentIndex(); + QString runwayName = (comboIndex == 0) ? + "active runway" : + QString("runway %1").arg(m_ui->runwayCombo->currentText()); + + if (onFinal) { + int finalDistance = m_ui->approachDistanceSpin->value(); + locationOnAirport = QString("on %2-mile final to %1").arg(runwayName).arg(finalDistance); + } else { + locationOnAirport = QString("on %1").arg(runwayName); + } + } else if (m_ui->parkingRadio->isChecked()) { + locationOnAirport = QString("at parking position %1").arg(m_ui->parkingCombo->currentText()); + } + + return QString("%2 (%1): %3").arg(ident).arg(name).arg(locationOnAirport); + } else { + QString navaidType; + switch (m_location->type()) { + case FGPositioned::VOR: + navaidType = QString("VOR"); break; + case FGPositioned::NDB: + navaidType = QString("NDB"); break; + case FGPositioned::FIX: + return QString("at waypoint %1").arg(ident); + default: + // unsupported type + break; + } + + return QString("at %1 %2 (%3").arg(navaidType).arg(ident).arg(name); + } + + return QString("Implement Me"); +} + + +void LocationWidget::updateDescription() +{ + bool locIsAirport = FGAirport::isAirportType(m_location.ptr()); + if (locIsAirport) { + FGAirport* apt = static_cast(m_location.ptr()); + + if (m_ui->runwayRadio->isChecked()) { + int comboIndex = m_ui->runwayCombo->currentIndex(); + int runwayIndex = m_ui->runwayCombo->itemData(comboIndex).toInt(); + // we can't figure out the active runway in the launcher (yet) + FGRunwayRef rwy = (runwayIndex >= 0) ? + apt->getRunwayByIndex(runwayIndex) : FGRunwayRef(); + m_ui->airportDiagram->setSelectedRunway(rwy); + } + + if (m_ui->onFinalCheckbox->isChecked()) { + m_ui->airportDiagram->setApproachExtensionDistance(m_ui->approachDistanceSpin->value()); + } else { + m_ui->airportDiagram->setApproachExtensionDistance(0.0); + } + } else { + + } + +#if 0 + + QString locationOnAirport; + if (m_ui->runwayRadio->isChecked()) { + + + } else if (m_ui->parkingRadio->isChecked()) { + locationOnAirport = QString("at parking position %1").arg(m_ui->parkingCombo->currentText()); + } + + m_ui->airportDescription->setText(); +#endif + + emit descriptionChanged(locationDescription()); +} + +void LocationWidget::onSearchResultSelected(const QModelIndex& index) +{ + qDebug() << "selected result:" << index.data(); + setBaseLocation(m_searchModel->itemAtRow(index.row())); +} + +void LocationWidget::onOffsetBearingTrueChanged(bool on) +{ + m_ui->offsetBearingLabel->setText(on ? tr("True bearing:") : + tr("Magnetic bearing:")); +} + + +void LocationWidget::onPopupHistory() +{ + if (m_recentAirports.isEmpty()) { + return; + } + +#if 0 + QMenu m; + Q_FOREACH(QString aptCode, m_recentAirports) { + FGAirportRef apt = FGAirport::findByIdent(aptCode.toStdString()); + QString name = QString::fromStdString(apt->name()); + QAction* act = m.addAction(QString("%1 - %2").arg(aptCode).arg(name)); + act->setData(aptCode); + } + + QPoint popupPos = m_ui->airportHistory->mapToGlobal(m_ui->airportHistory->rect().bottomLeft()); + QAction* triggered = m.exec(popupPos); + if (triggered) { + FGAirportRef apt = FGAirport::findByIdent(triggered->data().toString().toStdString()); + setAirport(apt); + m_ui->airportEdit->clear(); + m_ui->locationStack->setCurrentIndex(0); + } +#endif +} + +void LocationWidget::setBaseLocation(FGPositionedRef ref) +{ + if (m_location == ref) + return; + + m_location = ref; + onLocationChanged(); + +#if 0 + if (ref.valid()) { + // maintain the recent airport list + QString icao = QString::fromStdString(ref->ident()); + if (m_recentAirports.contains(icao)) { + // move to front + m_recentAirports.removeOne(icao); + m_recentAirports.push_front(icao); + } else { + // insert and trim list if necessary + m_recentAirports.push_front(icao); + if (m_recentAirports.size() > MAX_RECENT_AIRPORTS) { + m_recentAirports.pop_back(); + } + } + } +#endif + updateDescription(); +} + +void LocationWidget::onOffsetDataChanged() +{ + qDebug() << "implement me"; +} + +void LocationWidget::onBackToSearch() +{ + m_ui->stack->setCurrentIndex(2); + m_backButton->hide(); +} + +#include "LocationWidget.moc" diff --git a/src/GUI/LocationWidget.hxx b/src/GUI/LocationWidget.hxx new file mode 100644 index 000000000..9922fb5e4 --- /dev/null +++ b/src/GUI/LocationWidget.hxx @@ -0,0 +1,90 @@ +// LocationWidget.hxx - GUI launcher dialog using Qt5 +// +// Written by James Turner, started October 2015. +// +// Copyright (C) 2015 James Turner +// +// 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 LOCATIONWIDGET_H +#define LOCATIONWIDGET_H + +#include + +#include +#include +#include + +namespace Ui { + class LocationWidget; +} + +class NavSearchModel; + +class LocationWidget : public QWidget +{ + Q_OBJECT + +public: + explicit LocationWidget(QWidget *parent = 0); + ~LocationWidget(); + + void saveSettings(); + void restoreSettings(); + + QString locationDescription() const; + + void setBaseLocation(FGPositionedRef ref); + + bool shouldStartPaused() const; + + void setLocationOptions(); +Q_SIGNALS: + void descriptionChanged(QString t); + +private Q_SLOTS: + void updateDescription(); + + void onLocationChanged(); + + void onOffsetDataChanged(); + +private: + + void onSearch(); + + + void onSearchResultSelected(const QModelIndex& index); + void onPopupHistory(); + void onSearchComplete(); + + void onAirportDiagramClicked(FGRunwayRef rwy); + void onOffsetBearingTrueChanged(bool on); + + + Ui::LocationWidget *m_ui; + + NavSearchModel* m_searchModel; + + FGPositionedRef m_location; + QVector m_recentAirports; + + QToolButton* m_backButton; + + void onOffsetEnabledToggled(bool on); + void onBackToSearch(); +}; + +#endif // LOCATIONWIDGET_H diff --git a/src/GUI/LocationWidget.ui b/src/GUI/LocationWidget.ui new file mode 100644 index 000000000..ff5ce8cf0 --- /dev/null +++ b/src/GUI/LocationWidget.ui @@ -0,0 +1,385 @@ + + + LocationWidget + + + + 0 + 0 + 864 + 683 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + 1 + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + Parking: + + + + + + + Runway: + + + + + + + On final approach at distance: + + + + + + + + + + Qt::Horizontal + + + + 40 + 20 + + + + + + + + + + + + 0 + 0 + + + + + + + + nm + + + 10 + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + kts + + + 9999 + + + 120 + + + + + + + ft + + + -1000 + + + 120000 + + + 50 + + + 5000 + + + + + + + Altitude: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + + 0 + 0 + + + + + 200 + 200 + + + offsetGroup + + + + + + + Barometric altitude (ASL) + + + + + Above ground (AGL) + + + + + Flight Level (FL) + + + + + + + + Airspeed: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + Offset + + + true + + + false + + + + + + false + + + Bearing: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + true + + + 359 + + + 5 + + + + + + + True + + + + + + + Distance: + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + + + + + nm + + + 1 + + + 10000.000000000000000 + + + 10.000000000000000 + + + + + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + Search: + + + + + + + Enter an ICAO code, navaid or search by name + + + true + + + + + + + + 0 + 0 + + + + false + + + false + + + + + + + + + + + + + + Qt::AlignHCenter|Qt::AlignTop + + + + + + + TextLabel + + + Qt::AlignBottom|Qt::AlignHCenter + + + + + + + + + + + + + + + + + AirportDiagram + QWidget +
GUI/AirportDiagram.hxx
+ 1 +
+ + NavaidDiagram + QWidget +
GUI/NavaidDiagram.hxx
+ 1 +
+
+ + +
diff --git a/src/GUI/NavaidDiagram.cxx b/src/GUI/NavaidDiagram.cxx new file mode 100644 index 000000000..a913c7df8 --- /dev/null +++ b/src/GUI/NavaidDiagram.cxx @@ -0,0 +1,52 @@ +// NavaidDiagram.cxx - part of GUI launcher using Qt5 +// +// Written by James Turner, started October 2015. +// +// Copyright (C) 2014 James Turner +// +// 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 "NavaidDiagram.hxx" + +#include + +#include +#include +#include +#include + +NavaidDiagram::NavaidDiagram(QWidget* pr) : + BaseDiagram(pr), + m_offsetEnabled(false), + m_offsetDistanceNm(5.0), + m_offsetBearingDeg(0), + m_headingDeg(0) +{ + +} + +void NavaidDiagram::setNavaid(FGPositionedRef nav) +{ + m_navaid = nav; + m_projectionCenter = nav ? nav->geod() : SGGeod(); + m_scale = 1.0; + m_bounds = QRectF(); // clear + update(); +} + +void NavaidDiagram::paintContents(QPainter *) +{ + +} diff --git a/src/GUI/NavaidDiagram.hxx b/src/GUI/NavaidDiagram.hxx new file mode 100644 index 000000000..32c75b176 --- /dev/null +++ b/src/GUI/NavaidDiagram.hxx @@ -0,0 +1,61 @@ +// NavaidDiagram.hxx - part of GUI launcher using Qt5 +// +// Written by James Turner, started October 2015. +// +// Copyright (C) 2014 James Turner +// +// 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 GUI_NAVAID_DIAGRAM_HXX +#define GUI_NAVAID_DIAGRAM_HXX + +#include "BaseDiagram.hxx" +#include + +#include +#include + +class NavaidDiagram : public BaseDiagram +{ + Q_OBJECT +public: + NavaidDiagram(QWidget* pr); + + void setNavaid(FGPositionedRef nav); + + bool isOffsetEnabled() const; + void setOffsetEnabled(bool m_offset); + + void setOffsetDistanceNm(double distanceNm); + double offsetDistanceNm() const; + + void setOffsetBearingDeg(int bearingDeg); + int offsetBearingDeg() const; + + void setHeadingDeg(int headingDeg); + void headingDeg() const; +protected: + void paintContents(QPainter *) Q_DECL_OVERRIDE; + +private: + FGPositionedRef m_navaid; + + bool m_offsetEnabled; + double m_offsetDistanceNm; + int m_offsetBearingDeg; + int m_headingDeg; +}; + +#endif // of GUI_NAVAID_DIAGRAM_HXX diff --git a/src/GUI/QtLauncher.cxx b/src/GUI/QtLauncher.cxx index e79585fe6..2c9cd0d84 100644 --- a/src/GUI/QtLauncher.cxx +++ b/src/GUI/QtLauncher.cxx @@ -63,8 +63,6 @@ #include
#include -#include -#include // for parking #include #include
@@ -75,7 +73,6 @@ using namespace flightgear; using namespace simgear::pkg; -const int MAX_RECENT_AIRPORTS = 32; const int MAX_RECENT_AIRCRAFT = 20; namespace { // anonymous namespace @@ -170,6 +167,9 @@ public: // choosing to fail for now return QList(); } + } else if (c == QChar('#')) { + state = Comment; + break; } else if (c.isSpace()) { break; } @@ -211,6 +211,15 @@ public: value.append(c); } break; + + case Comment: + if ((c == QChar('\n')) || (c == QChar('\r'))) { + state = Start; + break; + } else { + // nothing to do, eat comment chars + } + break; } // of state switch } // of character loop @@ -229,12 +238,14 @@ private: Start = 0, Key, Value, - Quoted + Quoted, + Comment }; }; } // of anonymous namespace +<<<<<<< 56d7d049bc0b7361d1799298c38e61084f5d5e3f class IdentSearchFilter : public FGPositioned::TypeFilter { public: @@ -356,6 +367,8 @@ private: bool m_searchActive; QScopedPointer m_search; }; +======= +>>>>>>> Work on LocationWidget for Qt launcher class AircraftProxyModel : public QSortFilterProxyModel { @@ -525,13 +538,6 @@ QtLauncher::QtLauncher() : this, &QtLauncher::onSubsytemIdleTimeout); m_subsystemIdleTimer->start(); - m_airportsModel = new AirportSearchModel; - m_ui->searchList->setModel(m_airportsModel); - connect(m_ui->searchList, &QListView::clicked, - this, &QtLauncher::onAirportChoiceSelected); - connect(m_airportsModel, &AirportSearchModel::searchComplete, - this, &QtLauncher::onAirportSearchComplete); - // create and configure the proxy model m_aircraftProxy = new AircraftProxyModel(this); connect(m_ui->ratingsFilterCheck, &QAbstractButton::toggled, @@ -539,6 +545,7 @@ QtLauncher::QtLauncher() : connect(m_ui->aircraftFilter, &QLineEdit::textChanged, m_aircraftProxy, &QSortFilterProxyModel::setFilterFixedString); +<<<<<<< 56d7d049bc0b7361d1799298c38e61084f5d5e3f connect(m_ui->runwayCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateLocationDescription())); connect(m_ui->parkingCombo, SIGNAL(currentIndexChanged(int)), @@ -553,17 +560,18 @@ QtLauncher::QtLauncher() : connect(m_ui->airportDiagram, &AirportDiagram::clickedRunway, this, &QtLauncher::onAirportDiagramClicked); +======= +>>>>>>> Work on LocationWidget for Qt launcher connect(m_ui->runButton, SIGNAL(clicked()), this, SLOT(onRun())); connect(m_ui->quitButton, SIGNAL(clicked()), this, SLOT(onQuit())); - connect(m_ui->airportEdit, SIGNAL(returnPressed()), - this, SLOT(onSearchAirports())); - connect(m_ui->airportHistory, &QPushButton::clicked, - this, &QtLauncher::onPopupAirportHistory); connect(m_ui->aircraftHistory, &QPushButton::clicked, this, &QtLauncher::onPopupAircraftHistory); + connect(m_ui->location, &LocationWidget::descriptionChanged, + m_ui->locationDescription, &QLabel::setText); + QAction* qa = new QAction(this); qa->setShortcut(QKeySequence("Ctrl+Q")); connect(qa, &QAction::triggered, this, &QtLauncher::onQuit); @@ -574,9 +582,6 @@ QtLauncher::QtLauncher() : QIcon historyIcon(":/history-icon"); m_ui->aircraftHistory->setIcon(historyIcon); - m_ui->airportHistory->setIcon(historyIcon); - - m_ui->searchIcon->setPixmap(QPixmap(":/search-icon")); connect(m_ui->timeOfDayCombo, SIGNAL(currentIndexChanged(int)), this, SLOT(updateSettingsSummary())); @@ -629,15 +634,6 @@ QtLauncher::QtLauncher() : connect(m_ui->pathsButton, &QPushButton::clicked, this, &QtLauncher::onEditPaths); - - connect(m_ui->trueBearing, &QCheckBox::toggled, this, &QtLauncher::onOffsetBearingTrueChanged); - connect(m_ui->offsetRadioButton, &QRadioButton::toggled, - this, &QtLauncher::onOffsetRadioToggled); - connect(m_ui->trueBearing, &QCheckBox::toggled, this, &QtLauncher::onOffsetDataChanged); - connect(m_ui->offsetBearingSpinbox, SIGNAL(valueChanged(int)), this, SLOT(onOffsetDataChanged())); - connect(m_ui->offsetNmSpinbox, SIGNAL(valueChanged(double)), - this, SLOT(onOffsetDataChanged())); - restoreSettings(); QSettings settings; @@ -732,15 +728,7 @@ void QtLauncher::restoreSettings() } updateSelectedAircraft(); - - Q_FOREACH(QVariant v, settings.value("recent-locations").toList()) { - m_recentAirports.push_back(v.toLongLong()); - } - - if (!m_recentAirports.empty()) { - setBaseLocation(NavDataCache::instance()->loadById(m_recentAirports.front())); - } - updateLocationDescription(); + m_ui->location->restoreSettings(); // rating filters m_ui->ratingsFilterCheck->setChecked(settings.value("ratings-filter", true).toBool()); @@ -767,15 +755,11 @@ void QtLauncher::saveSettings() settings.setValue("ratings-filter", m_ui->ratingsFilterCheck->isChecked()); settings.setValue("recent-aircraft", QUrl::toStringList(m_recentAircraft)); - QVariantList locations; - Q_FOREACH(PositionedID v, m_recentAirports) { - locations.push_back(v); - } - - settings.setValue("recent-airports", locations); settings.setValue("timeofday", m_ui->timeOfDayCombo->currentIndex()); settings.setValue("season", m_ui->seasonCombo->currentIndex()); settings.setValue("additional-args", m_ui->commandLineArgs->toPlainText()); + + m_ui->location->saveSettings(); } void QtLauncher::setEnableDisableOptionFromCheckbox(QCheckBox* cbox, QString name) const @@ -836,7 +820,7 @@ void QtLauncher::onRun() m_recentAircraft.pop_back(); } - setLocationOptions(); + m_ui->location->setLocationOptions(); // time of day if (m_ui->timeOfDayCombo->currentIndex() != 0) { @@ -895,53 +879,6 @@ void QtLauncher::onRun() saveSettings(); } -void QtLauncher::setLocationOptions() -{ - flightgear::Options* opt = flightgear::Options::sharedInstance(); - - if (!m_location) { - return; - } - - if (FGAirport::isAirportType(m_location.ptr())) { - FGAirport* apt = static_cast(m_location.ptr()); - opt->addOption("airport", apt->ident()); - - if (m_ui->runwayRadio->isChecked()) { - int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt(); - if (index >= 0) { - // explicit runway choice - opt->addOption("runway", apt->getRunwayByIndex(index)->ident()); - } - - if (m_ui->onFinalCheckbox->isChecked()) { - opt->addOption("glideslope", "3.0"); - opt->addOption("offset-distance", "10.0"); // in nautical miles - } - } else if (m_ui->parkingRadio->isChecked()) { - // parking selection - opt->addOption("parkpos", m_ui->parkingCombo->currentText().toStdString()); - } - // of location is an airport - } - - FGPositioned::Type ty = m_location->type(); - switch (ty) { - case FGPositioned::VOR: - case FGPositioned::NDB: - case FGPositioned::FIX: - // set disambiguation property - globals->get_props()->setIntValue("/sim/presets/navaid-id", - static_cast(m_location->guid())); - - // we always set 'fix', but really this is just to force positionInit - // code to check for the navaid-id value above. - opt->addOption("fix", m_location->ident()); - break; - default: - break; - } -} void QtLauncher::onApply() { @@ -988,6 +925,7 @@ void QtLauncher::onQuit() reject(); } +<<<<<<< 56d7d049bc0b7361d1799298c38e61084f5d5e3f void QtLauncher::onSearchAirports() { QString search = m_ui->airportEdit->text(); @@ -1085,6 +1023,8 @@ void QtLauncher::onAirportDiagramClicked(FGRunwayRef rwy) updateLocationDescription(); } +======= +>>>>>>> Work on LocationWidget for Qt launcher void QtLauncher::onToggleTerrasync(bool enabled) { @@ -1142,53 +1082,6 @@ void QtLauncher::onAircraftInstallFailed(QModelIndex index, QString errorMessage maybeUpdateSelectedAircraft(index); } -void QtLauncher::updateLocationDescription() -{ - if (!m_location) { - m_ui->airportDescription->setText(QString("No location selected")); - return; - } - - QString ident = QString::fromStdString(m_location->ident()), - name = QString::fromStdString(m_location->name()); - QString locationOnAirport; - if (m_ui->runwayRadio->isChecked()) { - FGAirport* apt = static_cast(m_location.ptr()); - bool onFinal = m_ui->onFinalCheckbox->isChecked(); - int comboIndex = m_ui->runwayCombo->currentIndex(); - QString runwayName = (comboIndex == 0) ? - "active runway" : - QString("runway %1").arg(m_ui->runwayCombo->currentText()); - - if (onFinal) { - locationOnAirport = QString("on 10-mile final to %1").arg(runwayName); - } else { - locationOnAirport = QString("on %1").arg(runwayName); - } - - int runwayIndex = m_ui->runwayCombo->itemData(comboIndex).toInt(); - FGRunwayRef rwy = (runwayIndex >= 0) ? - apt->getRunwayByIndex(runwayIndex) : FGRunwayRef(); - m_ui->airportDiagram->setSelectedRunway(rwy); - } else if (m_ui->parkingRadio->isChecked()) { - locationOnAirport = QString("at parking position %1").arg(m_ui->parkingCombo->currentText()); - } - - m_ui->airportDescription->setText(QString("%2 (%1): %3").arg(ident).arg(name).arg(locationOnAirport)); -} - -void QtLauncher::onAirportChoiceSelected(const QModelIndex& index) -{ - m_ui->locationStack->setCurrentIndex(0); - setBaseLocation(m_airportsModel->itemAtRow(index.row())); -} - -void QtLauncher::onOffsetBearingTrueChanged(bool on) -{ - m_ui->offsetBearingLabel->setText(on ? tr("True bearing:") : - tr("Magnetic bearing:")); -} - void QtLauncher::onAircraftSelected(const QModelIndex& index) { m_selectedAircraft = index.data(AircraftURIRole).toUrl(); @@ -1244,32 +1137,6 @@ void QtLauncher::updateSelectedAircraft() } } -void QtLauncher::onPopupAirportHistory() -{ - if (m_recentAirports.isEmpty()) { - return; - } - -#if 0 - QMenu m; - Q_FOREACH(QString aptCode, m_recentAirports) { - FGAirportRef apt = FGAirport::findByIdent(aptCode.toStdString()); - QString name = QString::fromStdString(apt->name()); - QAction* act = m.addAction(QString("%1 - %2").arg(aptCode).arg(name)); - act->setData(aptCode); - } - - QPoint popupPos = m_ui->airportHistory->mapToGlobal(m_ui->airportHistory->rect().bottomLeft()); - QAction* triggered = m.exec(popupPos); - if (triggered) { - FGAirportRef apt = FGAirport::findByIdent(triggered->data().toString().toStdString()); - setAirport(apt); - m_ui->airportEdit->clear(); - m_ui->locationStack->setCurrentIndex(0); - } -#endif -} - QModelIndex QtLauncher::proxyIndexForAircraftURI(QUrl uri) const { return m_aircraftProxy->mapFromSource(sourceIndexForAircraftURI(uri)); @@ -1311,35 +1178,6 @@ void QtLauncher::onPopupAircraftHistory() } } -void QtLauncher::setBaseLocation(FGPositionedRef ref) -{ - if (m_location == ref) - return; - - m_location = ref; - onLocationChanged(); - -#if 0 - if (ref.valid()) { - // maintain the recent airport list - QString icao = QString::fromStdString(ref->ident()); - if (m_recentAirports.contains(icao)) { - // move to front - m_recentAirports.removeOne(icao); - m_recentAirports.push_front(icao); - } else { - // insert and trim list if necessary - m_recentAirports.push_front(icao); - if (m_recentAirports.size() > MAX_RECENT_AIRPORTS) { - m_recentAirports.pop_back(); - } - } - } -#endif - - updateLocationDescription(); -} - void QtLauncher::onEditRatingsFilter() { EditRatingsFilterDialog dialog(this); @@ -1427,9 +1265,5 @@ simgear::pkg::PackageRef QtLauncher::packageForAircraftURI(QUrl uri) const return globals->packageRoot()->getPackageById(ident.toStdString()); } -void QtLauncher::onOffsetDataChanged() -{ - qDebug() << "implement me"; -} #include "QtLauncher.moc" diff --git a/src/GUI/QtLauncher_private.hxx b/src/GUI/QtLauncher_private.hxx index 7eb6b3854..64b38014a 100644 --- a/src/GUI/QtLauncher_private.hxx +++ b/src/GUI/QtLauncher_private.hxx @@ -28,8 +28,6 @@ #include #include - -#include #include #include @@ -38,7 +36,6 @@ namespace Ui class Launcher; } -class AirportSearchModel; class QModelIndex; class AircraftProxyModel; class AircraftItemModel; @@ -64,24 +61,17 @@ private slots: void onQuit(); - void onSearchAirports(); - - void onLocationChanged(); - void onAirportChoiceSelected(const QModelIndex& index); void onAircraftSelected(const QModelIndex& index); void onRequestPackageInstall(const QModelIndex& index); void onCancelDownload(const QModelIndex& index); - void onPopupAirportHistory(); void onPopupAircraftHistory(); void onEditRatingsFilter(); - void updateLocationDescription(); void updateSettingsSummary(); - void onAirportSearchComplete(); void onRembrandtToggled(bool b); void onToggleTerrasync(bool enabled); @@ -90,17 +80,10 @@ private slots: void onEditPaths(); - void onAirportDiagramClicked(FGRunwayRef rwy); - void onOffsetBearingTrueChanged(bool on); - - void onOffsetDataChanged(); - void onOffsetRadioToggled(bool on); void onAircraftInstalledCompleted(QModelIndex index); void onAircraftInstallFailed(QModelIndex index, QString errorMessage); private: - void setLocationOptions(); - void setBaseLocation(FGPositionedRef ref); /** * Check if the passed index is the selected aircraft, and if so, refresh @@ -120,14 +103,11 @@ private: simgear::pkg::PackageRef packageForAircraftURI(QUrl uri) const; QScopedPointer m_ui; - AirportSearchModel* m_airportsModel; AircraftProxyModel* m_aircraftProxy; AircraftItemModel* m_aircraftModel; - FGPositionedRef m_location; QUrl m_selectedAircraft; QList m_recentAircraft; - QVector m_recentAirports; QTimer* m_subsystemIdleTimer; bool m_inAppMode; -- 2.39.5