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 <Airports/parking.hxx> #include <Airports/pavement.hxx> -/* 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) { - setSizePolicy(QSizePolicy::MinimumExpanding, - QSizePolicy::MinimumExpanding); - setMinimumSize(100, 100); +} + +AirportDiagram::~AirportDiagram() +{ + } 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); + 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); + } +} + +void AirportDiagram::addParking(FGParkingRef park) +{ + ParkingData pd = { project(park->geod()), park }; + m_parking.push_back(pd); + recomputeBounds(false); update(); } -QTransform AirportDiagram::transform() const -{ - // 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; -} -void AirportDiagram::paintEvent(QPaintEvent* pe) +void AirportDiagram::paintContents(QPainter* p) { - 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); + // 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<double>::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 <QWidget> -#include <QPainterPath> +#ifndef GUI_AIRPORT_DIAGRAM_HXX +#define GUI_AIRPORT_DIAGRAM_HXX -#include <Airports/airports_fwd.hxx> +#include "BaseDiagram.hxx" + +#include <Airports/parking.hxx> +#include <Airports/runways.hxx> #include <simgear/math/sg_geodesy.hxx> -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<RunwayData> m_runways; + QVector<RunwayData> m_runways; struct TaxiwayData { QPointF p1, p2; @@ -80,8 +79,19 @@ private: } }; - QList<TaxiwayData> m_taxiways; - QList<QPainterPath> m_pavements; - + QVector<TaxiwayData> m_taxiways; + QVector<QPainterPath> m_pavements; + + struct ParkingData + { + QPointF pt; + FGParkingRef parking; + }; + + QVector<ParkingData> 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 <zakalawe@mac.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "BaseDiagram.hxx" + +#include <limits> + +#include <QPainter> +#include <QDebug> +#include <QVector2D> +#include <QMouseEvent> + +/* 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 <zakalawe@mac.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef GUI_BASEDIAGRAM_HXX +#define GUI_BASEDIAGRAM_HXX + +#include <QWidget> +#include <QPainterPath> + +#include <simgear/math/sg_geodesy.hxx> + +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 @@ </widget> </item> <item row="1" column="2"> - <widget class="QLabel" name="airportDescription"> + <widget class="QLabel" name="locationDescription"> <property name="text"> <string/> </property> @@ -231,207 +231,7 @@ <number>4</number> </property> <item> - <layout class="QHBoxLayout" name="horizontalLayout_6"> - <item> - <widget class="QLabel" name="airportIdent"> - <property name="text"> - <string>Search:</string> - </property> - </widget> - </item> - <item> - <widget class="QLineEdit" name="airportEdit"> - <property name="placeholderText"> - <string>Enter an ICAO code, navaid or search by name</string> - </property> - </widget> - </item> - <item> - <widget class="QPushButton" name="airportHistory"> - <property name="sizePolicy"> - <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - <property name="autoDefault"> - <bool>false</bool> - </property> - <property name="flat"> - <bool>false</bool> - </property> - </widget> - </item> - </layout> - </item> - <item> - <widget class="QStackedWidget" name="locationStack"> - <property name="currentIndex"> - <number>0</number> - </property> - <widget class="QWidget" name="diagramPage"> - <layout class="QGridLayout" name="gridLayout" columnstretch="0,1,1,0"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item row="3" column="0"> - <widget class="QRadioButton" name="parkingRadio"> - <property name="text"> - <string>Parking:</string> - </property> - </widget> - </item> - <item row="2" column="1"> - <widget class="QCheckBox" name="onFinalCheckbox"> - <property name="text"> - <string>On 10-mile final</string> - </property> - </widget> - </item> - <item row="1" column="0"> - <widget class="QRadioButton" name="runwayRadio"> - <property name="text"> - <string>Runway:</string> - </property> - </widget> - </item> - <item row="6" column="2"> - <widget class="QSpinBox" name="offsetBearingSpinbox"> - <property name="wrapping"> - <bool>true</bool> - </property> - <property name="maximum"> - <number>359</number> - </property> - <property name="singleStep"> - <number>5</number> - </property> - </widget> - </item> - <item row="3" column="1" colspan="3"> - <widget class="QComboBox" name="parkingCombo"/> - </item> - <item row="1" column="1" colspan="3"> - <widget class="QComboBox" name="runwayCombo"/> - </item> - <item row="6" column="3"> - <widget class="QCheckBox" name="trueBearing"> - <property name="text"> - <string>True</string> - </property> - </widget> - </item> - <item row="6" column="1"> - <widget class="QLabel" name="offsetBearingLabel"> - <property name="enabled"> - <bool>true</bool> - </property> - <property name="text"> - <string>Bearing:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="5" column="0"> - <widget class="QRadioButton" name="offsetRadioButton"> - <property name="text"> - <string>Bearing and distance offset:</string> - </property> - </widget> - </item> - <item row="5" column="1"> - <widget class="QLabel" name="offsetDistanceLabel"> - <property name="text"> - <string>Distance:</string> - </property> - <property name="alignment"> - <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> - </property> - </widget> - </item> - <item row="5" column="2"> - <widget class="QDoubleSpinBox" name="offsetNmSpinbox"> - <property name="suffix"> - <string>nm</string> - </property> - <property name="decimals"> - <number>1</number> - </property> - <property name="maximum"> - <double>10000.000000000000000</double> - </property> - <property name="value"> - <double>10.000000000000000</double> - </property> - </widget> - </item> - <item row="0" column="0" colspan="4"> - <widget class="AirportDiagram" name="airportDiagram" native="true"> - <property name="sizePolicy"> - <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> - <horstretch>0</horstretch> - <verstretch>0</verstretch> - </sizepolicy> - </property> - </widget> - </item> - </layout> - </widget> - <widget class="QWidget" name="searchResultsPage"> - <layout class="QVBoxLayout" name="verticalLayout_5"> - <property name="leftMargin"> - <number>0</number> - </property> - <property name="topMargin"> - <number>0</number> - </property> - <property name="rightMargin"> - <number>0</number> - </property> - <property name="bottomMargin"> - <number>0</number> - </property> - <item> - <widget class="QListView" name="searchList"/> - </item> - </layout> - </widget> - <widget class="QWidget" name="searchStatusPage"> - <layout class="QVBoxLayout" name="verticalLayout_6"> - <item> - <widget class="QLabel" name="searchIcon"> - <property name="text"> - <string>TextLabel</string> - </property> - <property name="alignment"> - <set>Qt::AlignBottom|Qt::AlignHCenter</set> - </property> - </widget> - </item> - <item> - <widget class="QLabel" name="searchStatusText"> - <property name="text"> - <string/> - </property> - <property name="alignment"> - <set>Qt::AlignHCenter|Qt::AlignTop</set> - </property> - </widget> - </item> - </layout> - </widget> - </widget> + <widget class="LocationWidget" name="location" native="true"/> </item> </layout> </widget> @@ -721,9 +521,9 @@ </widget> <customwidgets> <customwidget> - <class>AirportDiagram</class> + <class>LocationWidget</class> <extends>QWidget</extends> - <header location="global">GUI/AirportDiagram.hxx</header> + <header location="global">GUI/LocationWidget.hxx</header> <container>1</container> </customwidget> </customwidgets> 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 <zakalawe@mac.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "LocationWidget.hxx" +#include "ui_LocationWidget.h" + +#include <QSettings> +#include <QAbstractListModel> +#include <QTimer> +#include <QDebug> +#include <QToolButton> + +#include "AirportDiagram.hxx" +#include "NavaidDiagram.hxx" + +#include <Airports/airport.hxx> +#include <Airports/dynamics.hxx> // for parking +#include <Main/globals.hxx> +#include <Navaids/NavDataCache.hxx> +#include <Navaids/navrecord.hxx> +#include <Main/options.hxx> +#include <Main/fg_init.hxx> + +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; i<exactMatches.size(); ++i) { + m_ids.push_back(exactMatches[i]->guid()); + 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<qlonglong>(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<NavDataCache::ThreadedGUISearch> 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<FGAirport*>(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<int>(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<FGAirport*>(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; r<apt->numRunways(); ++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<qlonglong>(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<FGAirport*>(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<FGAirport*>(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 <zakalawe@mac.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef LOCATIONWIDGET_H +#define LOCATIONWIDGET_H + +#include <QWidget> + +#include <QToolButton> +#include <Navaids/positioned.hxx> +#include <Airports/airports_fwd.hxx> + +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<PositionedID> 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 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>LocationWidget</class> + <widget class="QWidget" name="LocationWidget"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>864</width> + <height>683</height> + </rect> + </property> + <layout class="QVBoxLayout" name="verticalLayout"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <widget class="QStackedWidget" name="stack"> + <property name="currentIndex"> + <number>1</number> + </property> + <widget class="QWidget" name="airportPage"> + <layout class="QGridLayout" name="gridLayout" columnstretch="1,0,0,0"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item row="4" column="0"> + <widget class="QRadioButton" name="parkingRadio"> + <property name="text"> + <string>Parking:</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QRadioButton" name="runwayRadio"> + <property name="text"> + <string>Runway:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QCheckBox" name="onFinalCheckbox"> + <property name="text"> + <string>On final approach at distance:</string> + </property> + </widget> + </item> + <item row="4" column="1" colspan="3"> + <widget class="QComboBox" name="parkingCombo"/> + </item> + <item row="3" column="3"> + <spacer name="horizontalSpacer"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="2" column="1" colspan="3"> + <widget class="QComboBox" name="runwayCombo"/> + </item> + <item row="1" column="0" colspan="4"> + <widget class="AirportDiagram" name="airportDiagram" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="MinimumExpanding" vsizetype="MinimumExpanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + </widget> + </item> + <item row="3" column="2"> + <widget class="QSpinBox" name="approachDistanceSpin"> + <property name="suffix"> + <string>nm</string> + </property> + <property name="value"> + <number>10</number> + </property> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="navaidPage"> + <layout class="QGridLayout" name="gridLayout_3" rowstretch="1,0,0,0,0"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item row="2" column="1" colspan="2"> + <widget class="QSpinBox" name="airspeedSpinbox"> + <property name="suffix"> + <string>kts</string> + </property> + <property name="maximum"> + <number>9999</number> + </property> + <property name="value"> + <number>120</number> + </property> + </widget> + </item> + <item row="2" column="4" colspan="2"> + <widget class="QSpinBox" name="altitudeSpinbox"> + <property name="suffix"> + <string>ft</string> + </property> + <property name="minimum"> + <number>-1000</number> + </property> + <property name="maximum"> + <number>120000</number> + </property> + <property name="singleStep"> + <number>50</number> + </property> + <property name="value"> + <number>5000</number> + </property> + </widget> + </item> + <item row="2" column="3"> + <widget class="QLabel" name="altitudeLabel"> + <property name="text"> + <string>Altitude:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="0" column="0" rowspan="2" colspan="8"> + <widget class="NavaidDiagram" name="navaidDiagram" native="true"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Expanding" vsizetype="Expanding"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="minimumSize"> + <size> + <width>200</width> + <height>200</height> + </size> + </property> + <zorder>offsetGroup</zorder> + </widget> + </item> + <item row="2" column="6" colspan="2"> + <widget class="QComboBox" name="altitudeModeCombo"> + <item> + <property name="text"> + <string>Barometric altitude (ASL)</string> + </property> + </item> + <item> + <property name="text"> + <string>Above ground (AGL)</string> + </property> + </item> + <item> + <property name="text"> + <string>Flight Level (FL)</string> + </property> + </item> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="aispeedLabel"> + <property name="text"> + <string>Airspeed:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item row="3" column="0" colspan="8"> + <widget class="QGroupBox" name="offsetGroup"> + <property name="title"> + <string>Offset</string> + </property> + <property name="checkable"> + <bool>true</bool> + </property> + <property name="checked"> + <bool>false</bool> + </property> + <layout class="QHBoxLayout" name="horizontalLayout_2"> + <item> + <widget class="QLabel" name="offsetBearingLabel"> + <property name="enabled"> + <bool>false</bool> + </property> + <property name="text"> + <string>Bearing:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QSpinBox" name="offsetBearingSpinbox"> + <property name="wrapping"> + <bool>true</bool> + </property> + <property name="maximum"> + <number>359</number> + </property> + <property name="singleStep"> + <number>5</number> + </property> + </widget> + </item> + <item> + <widget class="QCheckBox" name="trueBearing"> + <property name="text"> + <string>True</string> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="offsetDistanceLabel"> + <property name="text"> + <string>Distance:</string> + </property> + <property name="alignment"> + <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set> + </property> + </widget> + </item> + <item> + <widget class="QDoubleSpinBox" name="offsetNmSpinbox"> + <property name="suffix"> + <string>nm</string> + </property> + <property name="decimals"> + <number>1</number> + </property> + <property name="maximum"> + <double>10000.000000000000000</double> + </property> + <property name="value"> + <double>10.000000000000000</double> + </property> + </widget> + </item> + </layout> + </widget> + </item> + </layout> + </widget> + <widget class="QWidget" name="searchPage"> + <layout class="QVBoxLayout" name="verticalLayout_2"> + <property name="leftMargin"> + <number>0</number> + </property> + <property name="topMargin"> + <number>0</number> + </property> + <property name="rightMargin"> + <number>0</number> + </property> + <property name="bottomMargin"> + <number>0</number> + </property> + <item> + <layout class="QHBoxLayout" name="horizontalLayout_6"> + <item> + <widget class="QLabel" name="searchLabel"> + <property name="text"> + <string>Search:</string> + </property> + </widget> + </item> + <item> + <widget class="QLineEdit" name="locationSearchEdit"> + <property name="placeholderText"> + <string>Enter an ICAO code, navaid or search by name</string> + </property> + <property name="clearButtonEnabled"> + <bool>true</bool> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="searchHistory"> + <property name="sizePolicy"> + <sizepolicy hsizetype="Fixed" vsizetype="Fixed"> + <horstretch>0</horstretch> + <verstretch>0</verstretch> + </sizepolicy> + </property> + <property name="autoDefault"> + <bool>false</bool> + </property> + <property name="flat"> + <bool>false</bool> + </property> + </widget> + </item> + </layout> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <widget class="QLabel" name="searchStatusText"> + <property name="text"> + <string/> + </property> + <property name="alignment"> + <set>Qt::AlignHCenter|Qt::AlignTop</set> + </property> + </widget> + </item> + <item> + <widget class="QLabel" name="searchIcon"> + <property name="text"> + <string>TextLabel</string> + </property> + <property name="alignment"> + <set>Qt::AlignBottom|Qt::AlignHCenter</set> + </property> + </widget> + </item> + </layout> + </item> + <item> + <widget class="QListView" name="searchResultsList"/> + </item> + </layout> + </widget> + </widget> + </item> + </layout> + </widget> + <customwidgets> + <customwidget> + <class>AirportDiagram</class> + <extends>QWidget</extends> + <header location="global">GUI/AirportDiagram.hxx</header> + <container>1</container> + </customwidget> + <customwidget> + <class>NavaidDiagram</class> + <extends>QWidget</extends> + <header location="global">GUI/NavaidDiagram.hxx</header> + <container>1</container> + </customwidget> + </customwidgets> + <resources/> + <connections/> +</ui> 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 <zakalawe@mac.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#include "NavaidDiagram.hxx" + +#include <limits> + +#include <QPainter> +#include <QDebug> +#include <QVector2D> +#include <QMouseEvent> + +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 <zakalawe@mac.com> +// +// This program is free software; you can redistribute it and/or +// modify it under the terms of the GNU General Public License as +// published by the Free Software Foundation; either version 2 of the +// License, or (at your option) any later version. +// +// This program is distributed in the hope that it will be useful, but +// WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +// General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with this program; if not, write to the Free Software +// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + +#ifndef GUI_NAVAID_DIAGRAM_HXX +#define GUI_NAVAID_DIAGRAM_HXX + +#include "BaseDiagram.hxx" +#include <QPainterPath> + +#include <Navaids/navrecord.hxx> +#include <simgear/math/sg_geodesy.hxx> + +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 <Main/globals.hxx> #include <Navaids/NavDataCache.hxx> -#include <Airports/airport.hxx> -#include <Airports/dynamics.hxx> // for parking #include <Navaids/navrecord.hxx> #include <Main/options.hxx> @@ -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<Arg>(); } + } 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<NavDataCache::ThreadedGUISearch> 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<FGAirport*>(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<int>(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<FGAirport*>(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 <QTimer> #include <QUrl> - -#include <Airports/airport.hxx> #include <simgear/package/Package.hxx> #include <simgear/package/Catalog.hxx> @@ -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<Ui::Launcher> m_ui; - AirportSearchModel* m_airportsModel; AircraftProxyModel* m_aircraftProxy; AircraftItemModel* m_aircraftModel; - FGPositionedRef m_location; QUrl m_selectedAircraft; QList<QUrl> m_recentAircraft; - QVector<PositionedID> m_recentAirports; QTimer* m_subsystemIdleTimer; bool m_inAppMode;