]> git.mxchange.org Git - flightgear.git/blob - src/GUI/LocationWidget.cxx
Work on launcher diagrams.
[flightgear.git] / src / GUI / LocationWidget.cxx
1 // LocationWidget.cxx - GUI launcher dialog using Qt5
2 //
3 // Written by James Turner, started October 2015.
4 //
5 // Copyright (C) 2015 James Turner <zakalawe@mac.com>
6 //
7 // This program is free software; you can redistribute it and/or
8 // modify it under the terms of the GNU General Public License as
9 // published by the Free Software Foundation; either version 2 of the
10 // License, or (at your option) any later version.
11 //
12 // This program is distributed in the hope that it will be useful, but
13 // WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
15 // General Public License for more details.
16 //
17 // You should have received a copy of the GNU General Public License
18 // along with this program; if not, write to the Free Software
19 // Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20
21 #include "LocationWidget.hxx"
22 #include "ui_LocationWidget.h"
23
24 #include <QSettings>
25 #include <QAbstractListModel>
26 #include <QTimer>
27 #include <QDebug>
28 #include <QToolButton>
29 #include <QMovie>
30 #include <QPainter>
31
32 #include "AirportDiagram.hxx"
33 #include "NavaidDiagram.hxx"
34
35 #include <Airports/airport.hxx>
36 #include <Airports/dynamics.hxx> // for parking
37 #include <Main/globals.hxx>
38 #include <Navaids/NavDataCache.hxx>
39 #include <Navaids/navrecord.hxx>
40 #include <Main/options.hxx>
41 #include <Main/fg_init.hxx>
42 #include <Main/fg_props.hxx> // for fgSetDouble
43
44 const int MAX_RECENT_AIRPORTS = 32;
45
46 using namespace flightgear;
47
48 QString fixNavaidName(QString s)
49 {
50     // split into words
51     QStringList words = s.split(QChar(' '));
52     QStringList changedWords;
53     Q_FOREACH(QString w, words) {
54         QString up = w.toUpper();
55
56         // expand common abbreviations
57         if (up == "FLD") {
58             changedWords.append("Field");
59             continue;
60         }
61
62         if (up == "MUNI") {
63             changedWords.append("Municipal");
64             continue;
65         }
66
67         if (up == "RGNL") {
68             changedWords.append("Regional");
69             continue;
70         }
71
72         if (up == "CTR") {
73             changedWords.append("Center");
74             continue;
75         }
76
77         if (up == "INTL") {
78             changedWords.append("International");
79             continue;
80         }
81
82         // occurs in many Australian airport names in our DB
83         if (up == "(NSW)") {
84             changedWords.append("(New South Wales)");
85             continue;
86         }
87
88         if ((up == "VOR") || (up == "NDB") || (up == "VOR-DME") || (up == "VORTAC") || (up == "NDB-DME")) {
89             changedWords.append(w);
90             continue;
91         }
92
93         QChar firstChar = w.at(0).toUpper();
94         w = w.mid(1).toLower();
95         w.prepend(firstChar);
96
97         changedWords.append(w);
98     }
99
100     return changedWords.join(QChar(' '));
101 }
102
103 QString formatGeodAsString(const SGGeod& geod)
104 {
105     QChar ns = (geod.getLatitudeDeg() > 0.0) ? 'N' : 'S';
106     QChar ew = (geod.getLongitudeDeg() > 0.0) ? 'E' : 'W';
107
108     return QString::number(fabs(geod.getLongitudeDeg()), 'f',2 ) + ew + " " +
109             QString::number(fabs(geod.getLatitudeDeg()), 'f',2 ) + ns;
110 }
111
112 bool parseStringAsGeod(const QString& s, SGGeod& result)
113 {
114     int commaPos = s.indexOf(QChar(','));
115     if (commaPos < 0)
116         return false;
117
118     bool ok;
119     double lon = s.leftRef(commaPos).toDouble(&ok);
120     if (!ok)
121         return false;
122
123     double lat = s.midRef(commaPos+1).toDouble(&ok);
124     if (!ok)
125         return false;
126
127     result = SGGeod::fromDeg(lon, lat);
128     return true;
129 }
130
131 class IdentSearchFilter : public FGPositioned::TypeFilter
132 {
133 public:
134     IdentSearchFilter()
135     {
136         addType(FGPositioned::AIRPORT);
137         addType(FGPositioned::SEAPORT);
138         addType(FGPositioned::HELIPAD);
139         addType(FGPositioned::VOR);
140         addType(FGPositioned::FIX);
141         addType(FGPositioned::NDB);
142     }
143 };
144
145 class NavSearchModel : public QAbstractListModel
146 {
147     Q_OBJECT
148 public:
149     NavSearchModel() :
150         m_searchActive(false)
151     {
152     }
153
154     void setSearch(QString t)
155     {
156         beginResetModel();
157
158         m_items.clear();
159         m_ids.clear();
160
161         std::string term(t.toUpper().toStdString());
162
163         IdentSearchFilter filter;
164         FGPositionedList exactMatches = NavDataCache::instance()->findAllWithIdent(term, &filter, true);
165
166         for (unsigned int i=0; i<exactMatches.size(); ++i) {
167             m_ids.push_back(exactMatches[i]->guid());
168             m_items.push_back(exactMatches[i]);
169         }
170         endResetModel();
171
172
173         m_search.reset(new NavDataCache::ThreadedGUISearch(term));
174         QTimer::singleShot(100, this, &NavSearchModel::onSearchResultsPoll);
175         m_searchActive = true;
176         endResetModel();
177     }
178
179     bool isSearchActive() const
180     {
181         return m_searchActive;
182     }
183
184     virtual int rowCount(const QModelIndex&) const
185     {
186         // if empty, return 1 for special 'no matches'?
187         return m_ids.size();
188     }
189
190     virtual QVariant data(const QModelIndex& index, int role) const
191     {
192         if (!index.isValid())
193             return QVariant();
194
195         FGPositionedRef pos = itemAtRow(index.row());
196         if (role == Qt::DisplayRole) {
197             if (pos->type() == FGPositioned::FIX) {
198                 // fixes don't have a name, show position instead
199                 return QString("Fix %1 (%2)").arg(QString::fromStdString(pos->ident()))
200                         .arg(formatGeodAsString(pos->geod()));
201             } else {
202                 QString name = fixNavaidName(QString::fromStdString(pos->name()));
203                 return QString("%1: %2").arg(QString::fromStdString(pos->ident())).arg(name);
204             }
205         }
206
207         if (role == Qt::DecorationRole) {
208             return AirportDiagram::iconForPositioned(pos);
209         }
210
211         if (role == Qt::EditRole) {
212             return QString::fromStdString(pos->ident());
213         }
214
215         if (role == Qt::UserRole) {
216             return static_cast<qlonglong>(m_ids[index.row()]);
217         }
218
219         return QVariant();
220     }
221
222     FGPositionedRef itemAtRow(unsigned int row) const
223     {
224         FGPositionedRef pos = m_items[row];
225         if (!pos.valid()) {
226             pos = NavDataCache::instance()->loadById(m_ids[row]);
227             m_items[row] = pos;
228         }
229
230         return pos;
231     }
232 Q_SIGNALS:
233     void searchComplete();
234
235 private:
236
237     void onSearchResultsPoll()
238     {
239         PositionedIDVec newIds = m_search->results();
240
241         beginInsertRows(QModelIndex(), m_ids.size(), newIds.size() - 1);
242         for (unsigned int i=m_ids.size(); i < newIds.size(); ++i) {
243             m_ids.push_back(newIds[i]);
244             m_items.push_back(FGPositionedRef()); // null ref
245         }
246         endInsertRows();
247
248         if (m_search->isComplete()) {
249             m_searchActive = false;
250             m_search.reset();
251             emit searchComplete();
252         } else {
253             QTimer::singleShot(100, this, &NavSearchModel::onSearchResultsPoll);
254         }
255     }
256
257 private:
258     PositionedIDVec m_ids;
259     mutable FGPositionedList m_items;
260     bool m_searchActive;
261     QScopedPointer<NavDataCache::ThreadedGUISearch> m_search;
262 };
263
264
265 LocationWidget::LocationWidget(QWidget *parent) :
266     QWidget(parent),
267     m_ui(new Ui::LocationWidget)
268 {
269     m_ui->setupUi(this);
270
271
272     QIcon historyIcon(":/history-icon");
273     m_ui->searchHistory->setIcon(historyIcon);
274
275     QByteArray format;
276     m_ui->searchIcon->setMovie(new QMovie(":/spinner", format, this));
277
278     m_searchModel = new NavSearchModel;
279     m_ui->searchResultsList->setModel(m_searchModel);
280     connect(m_ui->searchResultsList, &QListView::clicked,
281             this, &LocationWidget::onSearchResultSelected);
282     connect(m_searchModel, &NavSearchModel::searchComplete,
283             this, &LocationWidget::onSearchComplete);
284
285     connect(m_ui->runwayCombo, SIGNAL(currentIndexChanged(int)),
286             this, SLOT(updateDescription()));
287     connect(m_ui->parkingCombo, SIGNAL(currentIndexChanged(int)),
288             this, SLOT(updateDescription()));
289     connect(m_ui->runwayRadio, SIGNAL(toggled(bool)),
290             this, SLOT(updateDescription()));
291     connect(m_ui->parkingRadio, SIGNAL(toggled(bool)),
292             this, SLOT(updateDescription()));
293     connect(m_ui->onFinalCheckbox, SIGNAL(toggled(bool)),
294             this, SLOT(updateDescription()));
295     connect(m_ui->approachDistanceSpin, SIGNAL(valueChanged(int)),
296             this, SLOT(updateDescription()));
297
298     connect(m_ui->airportDiagram, &AirportDiagram::clickedRunway,
299             this, &LocationWidget::onAirportDiagramClicked);
300
301     connect(m_ui->locationSearchEdit, &QLineEdit::returnPressed,
302             this, &LocationWidget::onSearch);
303
304     connect(m_ui->searchHistory, &QPushButton::clicked,
305             this, &LocationWidget::onPopupHistory);
306
307     connect(m_ui->trueBearing, &QCheckBox::toggled,
308             this, &LocationWidget::onOffsetBearingTrueChanged);
309     connect(m_ui->offsetGroup, &QGroupBox::toggled,
310             this, &LocationWidget::onOffsetEnabledToggled);
311     connect(m_ui->trueBearing, &QCheckBox::toggled, this,
312             &LocationWidget::onOffsetDataChanged);
313     connect(m_ui->offsetBearingSpinbox, SIGNAL(valueChanged(int)),
314             this, SLOT(onOffsetDataChanged()));
315     connect(m_ui->offsetNmSpinbox, SIGNAL(valueChanged(double)),
316             this, SLOT(onOffsetDataChanged()));
317
318     m_backButton = new QToolButton(this);
319     m_backButton->setGeometry(0, 0, 64, 32);
320     m_backButton->setText("<< Back");
321     //m_backButton->setIcon(QIcon(":/search-icon"));
322     m_backButton->raise();
323
324     connect(m_backButton, &QAbstractButton::clicked,
325             this, &LocationWidget::onBackToSearch);
326
327 // force various pieces of UI into sync
328     onOffsetEnabledToggled(m_ui->offsetGroup->isChecked());
329     onOffsetBearingTrueChanged(m_ui->trueBearing->isChecked());
330     onBackToSearch();
331 }
332
333 LocationWidget::~LocationWidget()
334 {
335     delete m_ui;
336 }
337
338 void LocationWidget::restoreSettings()
339 {
340     QSettings settings;
341     Q_FOREACH(QVariant v, settings.value("recent-locations").toList()) {
342         m_recentAirports.push_back(v.toLongLong());
343     }
344
345     if (!m_recentAirports.empty()) {
346         setBaseLocation(NavDataCache::instance()->loadById(m_recentAirports.front()));
347     }
348
349     updateDescription();
350 }
351
352 bool LocationWidget::shouldStartPaused() const
353 {
354     if (!m_location) {
355         return false; // defaults to on-ground at KSFO
356     }
357
358     if (FGAirport::isAirportType(m_location.ptr())) {
359         return m_ui->onFinalCheckbox->isChecked();
360     } else {
361         // navaid, start paused
362         return true;
363     }
364
365     return false;
366 }
367
368 void LocationWidget::saveSettings()
369 {
370     QSettings settings;
371
372     QVariantList locations;
373     Q_FOREACH(PositionedID v, m_recentAirports) {
374         locations.push_back(v);
375     }
376
377     settings.setValue("recent-airports", locations);
378 }
379
380 void LocationWidget::setLocationOptions()
381 {
382     flightgear::Options* opt = flightgear::Options::sharedInstance();
383
384     std::string altStr = QString::number(m_ui->altitudeSpinbox->value()).toStdString();
385     std::string vcStr = QString::number(m_ui->airspeedSpinbox->value()).toStdString();
386
387     if (m_locationIsLatLon) {
388         // bypass the options mechanism because converting to deg:min:sec notation
389         // just to parse back again is nasty.
390         fgSetDouble("/sim/presets/latitude-deg", m_geodLocation.getLatitudeDeg());
391         fgSetDouble("/position/latitude-deg", m_geodLocation.getLatitudeDeg());
392         fgSetDouble("/sim/presets/longitude-deg", m_geodLocation.getLongitudeDeg());
393         fgSetDouble("/position/longitude-deg", m_geodLocation.getLongitudeDeg());
394
395         opt->addOption("altitude", altStr);
396         opt->addOption("vc", vcStr);
397         return;
398     }
399
400     if (!m_location) {
401         return;
402     }
403
404     if (FGAirport::isAirportType(m_location.ptr())) {
405         FGAirport* apt = static_cast<FGAirport*>(m_location.ptr());
406         opt->addOption("airport", apt->ident());
407
408         if (m_ui->runwayRadio->isChecked()) {
409             int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt();
410             if (index >= 0) {
411                 // explicit runway choice
412                 FGRunwayRef runway = apt->getRunwayByIndex(index);
413                 opt->addOption("runway", runway->ident());
414
415                 // set nav-radio 1 based on selected runway
416                 if (runway->ILS()) {
417                     double mhz = runway->ILS()->get_freq() / 100.0;
418                     QString navOpt = QString("%1:%2").arg(runway->headingDeg()).arg(mhz);
419                     opt->addOption("nav1", navOpt.toStdString());
420                 }
421             }
422
423             if (m_ui->onFinalCheckbox->isChecked()) {
424                 opt->addOption("glideslope", "3.0");
425                 double offsetNm = m_ui->approachDistanceSpin->value();
426                 opt->addOption("offset-distance", QString::number(offsetNm).toStdString());
427             }
428
429         } else if (m_ui->parkingRadio->isChecked()) {
430             // parking selection
431             opt->addOption("parkpos", m_ui->parkingCombo->currentText().toStdString());
432         }
433         // of location is an airport
434     } else {
435         // location is a navaid
436         // note setting the ident here is ambigious, we really only need and
437         // want the 'navaid-id' property. However setting the 'real' option
438         // gives a better UI experience (eg existing Position in Air dialog)
439         FGPositioned::Type ty = m_location->type();
440         switch (ty) {
441         case FGPositioned::VOR:
442             opt->addOption("vor", m_location->ident());
443             setNavRadioOption();
444             break;
445
446         case FGPositioned::NDB:
447             opt->addOption("ndb", m_location->ident());
448             setNavRadioOption();
449             break;
450
451         case FGPositioned::FIX:
452             opt->addOption("fix", m_location->ident());
453             break;
454         default:
455             break;
456         }
457
458         opt->addOption("altitude", altStr);
459         opt->addOption("vc", vcStr);
460
461         // set disambiguation property
462         globals->get_props()->setIntValue("/sim/presets/navaid-id",
463                                           static_cast<int>(m_location->guid()));
464     }
465
466 }
467
468 void LocationWidget::setNavRadioOption()
469 {
470     flightgear::Options* opt = flightgear::Options::sharedInstance();
471
472     if (m_location->type() == FGPositioned::VOR) {
473         FGNavRecordRef nav(static_cast<FGNavRecord*>(m_location.ptr()));
474         double mhz = nav->get_freq() / 100.0;
475         int heading = 0; // add heading support
476         QString navOpt = QString("%1:%2").arg(heading).arg(mhz);
477         opt->addOption("nav1", navOpt.toStdString());
478     } else {
479         FGNavRecordRef nav(static_cast<FGNavRecord*>(m_location.ptr()));
480         int khz = nav->get_freq() / 100;
481         int heading = 0;
482         QString adfOpt = QString("%1:%2").arg(heading).arg(khz);
483         qDebug() << "ADF opt is:" << adfOpt;
484         opt->addOption("adf1", adfOpt.toStdString());
485     }
486 }
487
488 void LocationWidget::onSearch()
489 {
490     QString search = m_ui->locationSearchEdit->text();
491
492     m_locationIsLatLon = parseStringAsGeod(search, m_geodLocation);
493     if (m_locationIsLatLon) {
494         m_ui->searchIcon->setVisible(false);
495         m_ui->searchStatusText->setText(QString("Position '%1'").arg(formatGeodAsString(m_geodLocation)));
496         m_location.clear();
497         onLocationChanged();
498         updateDescription();
499         return;
500     }
501
502     m_searchModel->setSearch(search);
503
504     if (m_searchModel->isSearchActive()) {
505         m_ui->searchStatusText->setText(QString("Searching for '%1'").arg(search));
506         qDebug() << "setting icon visible";
507         m_ui->searchIcon->setVisible(true);
508         m_ui->searchIcon->movie()->start();
509     } else if (m_searchModel->rowCount(QModelIndex()) == 1) {
510         setBaseLocation(m_searchModel->itemAtRow(0));
511     }
512 }
513
514 void LocationWidget::onSearchComplete()
515 {
516     QString search = m_ui->locationSearchEdit->text();
517     m_ui->searchIcon->setVisible(false);
518     m_ui->searchStatusText->setText(QString("Results for '%1'").arg(search));
519
520     int numResults = m_searchModel->rowCount(QModelIndex());
521     if (numResults == 0) {
522         m_ui->searchStatusText->setText(QString("No matches for '%1'").arg(search));
523     } else if (numResults == 1) {
524         setBaseLocation(m_searchModel->itemAtRow(0));
525     }
526 }
527
528 void LocationWidget::onLocationChanged()
529 {
530     bool locIsAirport = FGAirport::isAirportType(m_location.ptr());
531     m_backButton->show();
532
533     if (locIsAirport) {
534         m_ui->stack->setCurrentIndex(0);
535         FGAirport* apt = static_cast<FGAirport*>(m_location.ptr());
536         m_ui->airportDiagram->setAirport(apt);
537
538         m_ui->runwayRadio->setChecked(true); // default back to runway mode
539         // unless multiplayer is enabled ?
540         m_ui->airportDiagram->setEnabled(true);
541
542         m_ui->runwayCombo->clear();
543         m_ui->runwayCombo->addItem("Automatic", -1);
544         for (unsigned int r=0; r<apt->numRunways(); ++r) {
545             FGRunwayRef rwy = apt->getRunwayByIndex(r);
546             // add runway with index as data role
547             m_ui->runwayCombo->addItem(QString::fromStdString(rwy->ident()), r);
548
549             m_ui->airportDiagram->addRunway(rwy);
550         }
551
552         m_ui->parkingCombo->clear();
553         FGAirportDynamics* dynamics = apt->getDynamics();
554         PositionedIDVec parkings = NavDataCache::instance()->airportItemsOfType(m_location->guid(),
555                                                                                 FGPositioned::PARKING);
556         if (parkings.empty()) {
557             m_ui->parkingCombo->setEnabled(false);
558             m_ui->parkingRadio->setEnabled(false);
559         } else {
560             m_ui->parkingCombo->setEnabled(true);
561             m_ui->parkingRadio->setEnabled(true);
562             Q_FOREACH(PositionedID parking, parkings) {
563                 FGParking* park = dynamics->getParking(parking);
564                 m_ui->parkingCombo->addItem(QString::fromStdString(park->getName()),
565                                             static_cast<qlonglong>(parking));
566
567                 m_ui->airportDiagram->addParking(park);
568             }
569         }
570
571     } else if (m_locationIsLatLon) {
572         m_ui->stack->setCurrentIndex(1);
573         m_ui->navaidDiagram->setGeod(m_geodLocation);
574     } else {
575         // navaid
576         m_ui->stack->setCurrentIndex(1);
577         m_ui->navaidDiagram->setNavaid(m_location);
578     }
579 }
580
581 void LocationWidget::onOffsetEnabledToggled(bool on)
582 {
583     m_ui->navaidDiagram->setOffsetEnabled(on);
584 }
585
586 void LocationWidget::onAirportDiagramClicked(FGRunwayRef rwy)
587 {
588     if (rwy) {
589         m_ui->runwayRadio->setChecked(true);
590         int rwyIndex = m_ui->runwayCombo->findText(QString::fromStdString(rwy->ident()));
591         m_ui->runwayCombo->setCurrentIndex(rwyIndex);
592         m_ui->airportDiagram->setSelectedRunway(rwy);
593     }
594
595     updateDescription();
596 }
597
598 QString LocationWidget::locationDescription() const
599 {
600     if (!m_location) {
601         if (m_locationIsLatLon) {
602             return QString("at position %1").arg(formatGeodAsString(m_geodLocation));
603         }
604
605         return QString("No location selected");
606     }
607
608     bool locIsAirport = FGAirport::isAirportType(m_location.ptr());
609     QString ident = QString::fromStdString(m_location->ident()),
610         name = QString::fromStdString(m_location->name());
611
612     name = fixNavaidName(name);
613
614     if (locIsAirport) {
615         //FGAirport* apt = static_cast<FGAirport*>(m_location.ptr());
616         QString locationOnAirport;
617
618         if (m_ui->runwayRadio->isChecked()) {
619             bool onFinal = m_ui->onFinalCheckbox->isChecked();
620             int comboIndex = m_ui->runwayCombo->currentIndex();
621             QString runwayName = (comboIndex == 0) ?
622                 "active runway" :
623                 QString("runway %1").arg(m_ui->runwayCombo->currentText());
624
625             if (onFinal) {
626                 int finalDistance = m_ui->approachDistanceSpin->value();
627                 locationOnAirport = QString("on %2-mile final to %1").arg(runwayName).arg(finalDistance);
628             } else {
629                 locationOnAirport = QString("on %1").arg(runwayName);
630             }
631         } else if (m_ui->parkingRadio->isChecked()) {
632             locationOnAirport = QString("at parking position %1").arg(m_ui->parkingCombo->currentText());
633         }
634
635         return QString("%2 (%1): %3").arg(ident).arg(name).arg(locationOnAirport);
636     } else {
637         QString navaidType;
638         switch (m_location->type()) {
639         case FGPositioned::VOR:
640             navaidType = QString("VOR"); break;
641         case FGPositioned::NDB:
642             navaidType = QString("NDB"); break;
643         case FGPositioned::FIX:
644             return QString("at waypoint %1").arg(ident);
645         default:
646             // unsupported type
647             break;
648         }
649
650         return QString("at %1 %2 (%3)").arg(navaidType).arg(ident).arg(name);
651     }
652
653     return QString("No location selected");
654 }
655
656
657 void LocationWidget::updateDescription()
658 {
659     bool locIsAirport = FGAirport::isAirportType(m_location.ptr());
660     if (locIsAirport) {
661         FGAirport* apt = static_cast<FGAirport*>(m_location.ptr());
662
663         if (m_ui->runwayRadio->isChecked()) {
664             int comboIndex = m_ui->runwayCombo->currentIndex();
665             int runwayIndex = m_ui->runwayCombo->itemData(comboIndex).toInt();
666             // we can't figure out the active runway in the launcher (yet)
667             FGRunwayRef rwy = (runwayIndex >= 0) ?
668                 apt->getRunwayByIndex(runwayIndex) : FGRunwayRef();
669             m_ui->airportDiagram->setSelectedRunway(rwy);
670         }
671
672         if (m_ui->onFinalCheckbox->isChecked()) {
673             m_ui->airportDiagram->setApproachExtensionDistance(m_ui->approachDistanceSpin->value());
674         } else {
675             m_ui->airportDiagram->setApproachExtensionDistance(0.0);
676         }        
677     } else {
678
679     }
680
681 #if 0
682
683     QString locationOnAirport;
684     if (m_ui->runwayRadio->isChecked()) {
685
686
687     } else if (m_ui->parkingRadio->isChecked()) {
688         locationOnAirport =  QString("at parking position %1").arg(m_ui->parkingCombo->currentText());
689     }
690
691     m_ui->airportDescription->setText();
692 #endif
693
694     emit descriptionChanged(locationDescription());
695 }
696
697 void LocationWidget::onSearchResultSelected(const QModelIndex& index)
698 {
699     setBaseLocation(m_searchModel->itemAtRow(index.row()));
700 }
701
702 void LocationWidget::onOffsetBearingTrueChanged(bool on)
703 {
704     m_ui->offsetBearingLabel->setText(on ? tr("True bearing:") :
705                                       tr("Magnetic bearing:"));
706 }
707
708
709 void LocationWidget::onPopupHistory()
710 {
711     if (m_recentAirports.isEmpty()) {
712         return;
713     }
714
715 #if 0
716     QMenu m;
717     Q_FOREACH(QString aptCode, m_recentAirports) {
718         FGAirportRef apt = FGAirport::findByIdent(aptCode.toStdString());
719         QString name = QString::fromStdString(apt->name());
720         QAction* act = m.addAction(QString("%1 - %2").arg(aptCode).arg(name));
721         act->setData(aptCode);
722     }
723
724     QPoint popupPos = m_ui->airportHistory->mapToGlobal(m_ui->airportHistory->rect().bottomLeft());
725     QAction* triggered = m.exec(popupPos);
726     if (triggered) {
727         FGAirportRef apt = FGAirport::findByIdent(triggered->data().toString().toStdString());
728         setAirport(apt);
729         m_ui->airportEdit->clear();
730         m_ui->locationStack->setCurrentIndex(0);
731     }
732 #endif
733 }
734
735 void LocationWidget::setBaseLocation(FGPositionedRef ref)
736 {
737     if (m_location == ref)
738         return;
739
740     m_location = ref;
741     onLocationChanged();
742
743 #if 0
744     if (ref.valid()) {
745         // maintain the recent airport list
746         QString icao = QString::fromStdString(ref->ident());
747         if (m_recentAirports.contains(icao)) {
748             // move to front
749             m_recentAirports.removeOne(icao);
750             m_recentAirports.push_front(icao);
751         } else {
752             // insert and trim list if necessary
753             m_recentAirports.push_front(icao);
754             if (m_recentAirports.size() > MAX_RECENT_AIRPORTS) {
755                 m_recentAirports.pop_back();
756             }
757         }
758     }
759 #endif
760     updateDescription();
761 }
762
763 void LocationWidget::onOffsetDataChanged()
764 {
765     m_ui->navaidDiagram->setOffsetEnabled(m_ui->offsetGroup->isChecked());
766     m_ui->navaidDiagram->setOffsetBearingDeg(m_ui->offsetBearingSpinbox->value());
767     m_ui->navaidDiagram->setOffsetDistanceNm(m_ui->offsetNmSpinbox->value());
768 }
769
770 void LocationWidget::onBackToSearch()
771 {
772     m_ui->stack->setCurrentIndex(2);
773     m_backButton->hide();
774 }
775
776 #include "LocationWidget.moc"