]> git.mxchange.org Git - flightgear.git/commitdiff
Work on LocationWidget for Qt launcher
authorJames Turner <zakalawe@mac.com>
Tue, 3 Nov 2015 21:28:36 +0000 (15:28 -0600)
committerJames Turner <zakalawe@mac.com>
Mon, 23 Nov 2015 00:47:01 +0000 (00:47 +0000)
13 files changed:
src/GUI/AirportDiagram.cxx
src/GUI/AirportDiagram.hxx
src/GUI/BaseDiagram.cxx [new file with mode: 0644]
src/GUI/BaseDiagram.hxx [new file with mode: 0644]
src/GUI/CMakeLists.txt
src/GUI/Launcher.ui
src/GUI/LocationWidget.cxx [new file with mode: 0644]
src/GUI/LocationWidget.hxx [new file with mode: 0644]
src/GUI/LocationWidget.ui [new file with mode: 0644]
src/GUI/NavaidDiagram.cxx [new file with mode: 0644]
src/GUI/NavaidDiagram.hxx [new file with mode: 0644]
src/GUI/QtLauncher.cxx
src/GUI/QtLauncher_private.hxx

index f347c16fd2a311c5133284737029e31302833b11..07cd4f91e1637ba9c3804b6950dc65d10c089175 100644 (file)
 #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)
+{
+}
+
+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<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);
     }
index 07eaf829765514499e6f172e825dfd9a5d4701da..61a015407c03bbe13fec9388ddab3c690d535878 100644 (file)
 // 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 (file)
index 0000000..b1e114c
--- /dev/null
@@ -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 (file)
index 0000000..782344e
--- /dev/null
@@ -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
index b8475f81c4aa2923cf21c801d7761cd02c28aaa0..bdff1cce58f05bc53d5de03c9274c5cd620001b5 100644 (file)
@@ -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})
 
index 1eb88520487551d5832a2bead01b195dbf0cefa9..9e696ca2859aee9528812c87467245240931d737 100644 (file)
@@ -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>
         <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>
  </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 (file)
index 0000000..ba840c1
--- /dev/null
@@ -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 (file)
index 0000000..9922fb5
--- /dev/null
@@ -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 (file)
index 0000000..ff5ce8c
--- /dev/null
@@ -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 (file)
index 0000000..a913c7d
--- /dev/null
@@ -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 (file)
index 0000000..32c75b1
--- /dev/null
@@ -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
index e79585fe6d5f477c4a7a194ecd05ea68a29f1cf7..2c9cd0d84b298e0f9d575e373d120c4642c14adc 100644 (file)
@@ -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"
index 7eb6b38546cc31296efacdb25e4f6e9f020dc268..64b38014a778fd86772b82213240dadcb6b2c57d 100644 (file)
@@ -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;