1 // LocationWidget.cxx - GUI launcher dialog using Qt5
3 // Written by James Turner, started October 2015.
5 // Copyright (C) 2015 James Turner <zakalawe@mac.com>
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.
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.
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.
21 #include "LocationWidget.hxx"
22 #include "ui_LocationWidget.h"
25 #include <QAbstractListModel>
28 #include <QToolButton>
32 #include "AirportDiagram.hxx"
33 #include "NavaidDiagram.hxx"
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
44 using namespace flightgear;
46 const unsigned int MAX_RECENT_LOCATIONS = 64;
48 QString fixNavaidName(QString s)
51 QStringList words = s.split(QChar(' '));
52 QStringList changedWords;
53 Q_FOREACH(QString w, words) {
54 QString up = w.toUpper();
56 // expand common abbreviations
58 changedWords.append("Field");
63 changedWords.append("County");
67 if ((up == "MUNI") || (up == "MUN")) {
68 changedWords.append("Municipal");
73 changedWords.append("Memorial");
78 changedWords.append("Regional");
83 changedWords.append("Center");
88 changedWords.append("International");
92 // occurs in many Australian airport names in our DB
94 changedWords.append("(New South Wales)");
98 if ((up == "VOR") || (up == "NDB")
99 || (up == "VOR-DME") || (up == "VORTAC")
101 || (up == "AFB") || (up == "RAF"))
103 changedWords.append(w);
107 if ((up =="[X]") || (up == "[H]") || (up == "[S]")) {
111 QChar firstChar = w.at(0).toUpper();
112 w = w.mid(1).toLower();
113 w.prepend(firstChar);
115 changedWords.append(w);
118 return changedWords.join(QChar(' '));
121 QString formatGeodAsString(const SGGeod& geod)
123 QChar ns = (geod.getLatitudeDeg() > 0.0) ? 'N' : 'S';
124 QChar ew = (geod.getLongitudeDeg() > 0.0) ? 'E' : 'W';
126 return QString::number(fabs(geod.getLongitudeDeg()), 'f',2 ) + ew + " " +
127 QString::number(fabs(geod.getLatitudeDeg()), 'f',2 ) + ns;
130 bool parseStringAsGeod(const QString& s, SGGeod& result)
132 int commaPos = s.indexOf(QChar(','));
137 double lon = s.leftRef(commaPos).toDouble(&ok);
141 double lat = s.midRef(commaPos+1).toDouble(&ok);
145 result = SGGeod::fromDeg(lon, lat);
149 QVariant savePositionList(const FGPositionedList& posList)
152 FGPositionedList::const_iterator it;
153 for (it = posList.begin(); it != posList.end(); ++it) {
155 FGPositionedRef pos = *it;
156 vm.insert("ident", QString::fromStdString(pos->ident()));
157 vm.insert("type", pos->type());
158 vm.insert("lat", pos->geod().getLatitudeDeg());
159 vm.insert("lon", pos->geod().getLongitudeDeg());
165 FGPositionedList loadPositionedList(QVariant v)
167 QVariantList vl = v.toList();
168 FGPositionedList result;
169 result.reserve(vl.size());
170 NavDataCache* cache = NavDataCache::instance();
172 Q_FOREACH(QVariant v, vl) {
173 QVariantMap vm = v.toMap();
174 std::string ident(vm.value("ident").toString().toStdString());
175 double lat = vm.value("lat").toDouble();
176 double lon = vm.value("lon").toDouble();
177 FGPositioned::Type ty(static_cast<FGPositioned::Type>(vm.value("type").toInt()));
178 FGPositioned::TypeFilter filter(ty);
179 FGPositionedRef pos = cache->findClosestWithIdent(ident,
180 SGGeod::fromDeg(lon, lat),
183 result.push_back(pos);
189 class IdentSearchFilter : public FGPositioned::TypeFilter
192 IdentSearchFilter(LauncherAircraftType aircraft)
194 addType(FGPositioned::VOR);
195 addType(FGPositioned::FIX);
196 addType(FGPositioned::NDB);
198 if (aircraft == Helicopter) {
199 addType(FGPositioned::HELIPAD);
202 if (aircraft == Seaplane) {
203 addType(FGPositioned::SEAPORT);
205 addType(FGPositioned::AIRPORT);
210 class NavSearchModel : public QAbstractListModel
215 m_searchActive(false)
219 void setSearch(QString t, LauncherAircraftType aircraft)
226 std::string term(t.toUpper().toStdString());
228 IdentSearchFilter filter(aircraft);
229 FGPositionedList exactMatches = NavDataCache::instance()->findAllWithIdent(term, &filter, true);
231 for (unsigned int i=0; i<exactMatches.size(); ++i) {
232 m_ids.push_back(exactMatches[i]->guid());
233 m_items.push_back(exactMatches[i]);
237 m_search.reset(new NavDataCache::ThreadedGUISearch(term));
238 QTimer::singleShot(100, this, SLOT(onSearchResultsPoll()));
239 m_searchActive = true;
242 bool isSearchActive() const
244 return m_searchActive;
247 virtual int rowCount(const QModelIndex&) const
249 // if empty, return 1 for special 'no matches'?
253 virtual QVariant data(const QModelIndex& index, int role) const
255 if (!index.isValid())
258 FGPositionedRef pos = itemAtRow(index.row());
259 if (role == Qt::DisplayRole) {
260 if (pos->type() == FGPositioned::FIX) {
261 // fixes don't have a name, show position instead
262 return QString("Fix %1 (%2)").arg(QString::fromStdString(pos->ident()))
263 .arg(formatGeodAsString(pos->geod()));
265 QString name = fixNavaidName(QString::fromStdString(pos->name()));
266 return QString("%1: %2").arg(QString::fromStdString(pos->ident())).arg(name);
270 if (role == Qt::DecorationRole) {
271 return AirportDiagram::iconForPositioned(pos,
272 AirportDiagram::SmallIcons | AirportDiagram::LargeAirportPlans);
275 if (role == Qt::EditRole) {
276 return QString::fromStdString(pos->ident());
279 if (role == Qt::UserRole) {
280 return static_cast<qlonglong>(m_ids[index.row()]);
286 FGPositionedRef itemAtRow(unsigned int row) const
288 FGPositionedRef pos = m_items[row];
290 pos = NavDataCache::instance()->loadById(m_ids[row]);
297 void setItems(const FGPositionedList& items)
300 m_searchActive = false;
304 for (unsigned int i=0; i < items.size(); ++i) {
305 m_ids.push_back(m_items[i]->guid());
312 void searchComplete();
316 void onSearchResultsPoll()
318 PositionedIDVec newIds = m_search->results();
320 beginInsertRows(QModelIndex(), m_ids.size(), newIds.size() - 1);
321 for (unsigned int i=m_ids.size(); i < newIds.size(); ++i) {
322 m_ids.push_back(newIds[i]);
323 m_items.push_back(FGPositionedRef()); // null ref
327 if (m_search->isComplete()) {
328 m_searchActive = false;
330 emit searchComplete();
332 QTimer::singleShot(100, this, SLOT(onSearchResultsPoll()));
337 PositionedIDVec m_ids;
338 mutable FGPositionedList m_items;
340 QScopedPointer<NavDataCache::ThreadedGUISearch> m_search;
344 LocationWidget::LocationWidget(QWidget *parent) :
346 m_ui(new Ui::LocationWidget),
347 m_locationIsLatLon(false),
348 m_aircraftType(Airplane)
352 QIcon historyIcon(":/history-icon");
353 m_ui->searchHistory->setIcon(historyIcon);
356 m_ui->searchIcon->setMovie(new QMovie(":/spinner", format, this));
358 m_searchModel = new NavSearchModel;
359 m_ui->searchResultsList->setModel(m_searchModel);
360 connect(m_ui->searchResultsList, &QListView::clicked,
361 this, &LocationWidget::onSearchResultSelected);
362 connect(m_searchModel, &NavSearchModel::searchComplete,
363 this, &LocationWidget::onSearchComplete);
365 connect(m_ui->runwayCombo, SIGNAL(currentIndexChanged(int)),
366 this, SLOT(updateDescription()));
367 connect(m_ui->parkingCombo, SIGNAL(currentIndexChanged(int)),
368 this, SLOT(updateDescription()));
369 connect(m_ui->runwayRadio, SIGNAL(toggled(bool)),
370 this, SLOT(updateDescription()));
371 connect(m_ui->parkingRadio, SIGNAL(toggled(bool)),
372 this, SLOT(updateDescription()));
373 connect(m_ui->onFinalCheckbox, SIGNAL(toggled(bool)),
374 this, SLOT(updateDescription()));
375 connect(m_ui->approachDistanceSpin, SIGNAL(valueChanged(int)),
376 this, SLOT(updateDescription()));
378 connect(m_ui->airportDiagram, &AirportDiagram::clickedRunway,
379 this, &LocationWidget::onAirportRunwayClicked);
380 connect(m_ui->airportDiagram, &AirportDiagram::clickedParking,
381 this, &LocationWidget::onAirportParkingClicked);
383 connect(m_ui->locationSearchEdit, &QLineEdit::returnPressed,
384 this, &LocationWidget::onSearch);
386 connect(m_ui->searchHistory, &QPushButton::clicked,
387 this, &LocationWidget::onShowHistory);
389 connect(m_ui->trueBearing, &QCheckBox::toggled,
390 this, &LocationWidget::onOffsetBearingTrueChanged);
391 connect(m_ui->offsetGroup, &QGroupBox::toggled,
392 this, &LocationWidget::onOffsetEnabledToggled);
393 connect(m_ui->trueBearing, &QCheckBox::toggled, this,
394 &LocationWidget::onOffsetDataChanged);
395 connect(m_ui->offsetBearingSpinbox, SIGNAL(valueChanged(int)),
396 this, SLOT(onOffsetDataChanged()));
397 connect(m_ui->offsetNmSpinbox, SIGNAL(valueChanged(double)),
398 this, SLOT(onOffsetDataChanged()));
399 connect(m_ui->headingSpinbox, SIGNAL(valueChanged(int)),
400 this, SLOT(onHeadingChanged()));
402 m_backButton = new QToolButton(this);
403 m_backButton->setGeometry(0, 0, 64, 32);
404 m_backButton->setText("<< Back");
405 m_backButton->raise();
407 connect(m_backButton, &QAbstractButton::clicked,
408 this, &LocationWidget::onBackToSearch);
410 // force various pieces of UI into sync
411 onOffsetEnabledToggled(m_ui->offsetGroup->isChecked());
412 onOffsetBearingTrueChanged(m_ui->trueBearing->isChecked());
416 LocationWidget::~LocationWidget()
421 void LocationWidget::restoreSettings()
425 if (settings.contains("location-lat")) {
426 m_locationIsLatLon = true;
427 m_geodLocation = SGGeod::fromDeg(settings.value("location-lon").toDouble(),
428 settings.value("location-lat").toDouble());
429 } else if (settings.contains("location-id")) {
430 m_location = NavDataCache::instance()->loadById(settings.value("location-id").toULongLong());
431 m_locationIsLatLon = false;
434 m_ui->altitudeSpinbox->setValue(settings.value("altitude", 6000).toInt());
435 m_ui->airspeedSpinbox->setValue(settings.value("speed", 120).toInt());
436 m_ui->headingSpinbox->setValue(settings.value("heading").toInt());
437 m_ui->offsetGroup->setChecked(settings.value("offset-enabled").toBool());
438 m_ui->offsetBearingSpinbox->setValue(settings.value("offset-bearing").toInt());
439 m_ui->offsetNmSpinbox->setValue(settings.value("offset-distance", 10).toInt());
441 m_recentLocations = loadPositionedList(settings.value("recent-locations"));
442 m_searchModel->setItems(m_recentLocations);
448 bool LocationWidget::shouldStartPaused() const
451 return false; // defaults to on-ground at KSFO
454 if (FGAirport::isAirportType(m_location.ptr())) {
455 return m_ui->onFinalCheckbox->isChecked();
457 // navaid, start paused
464 void LocationWidget::saveSettings()
468 settings.remove("location-id");
469 settings.remove("location-lon");
470 settings.remove("location-lat");
471 if (m_locationIsLatLon) {
472 settings.setValue("location-lat", m_geodLocation.getLatitudeDeg());
473 settings.setValue("location-lon", m_geodLocation.getLongitudeDeg());
475 } else if (m_location) {
476 settings.setValue("location-id", static_cast<qlonglong>(m_location->guid()));
479 settings.setValue("altitude", m_ui->altitudeSpinbox->value());
480 settings.setValue("speed", m_ui->airspeedSpinbox->value());
482 settings.setValue("offset-enabled", m_ui->offsetGroup->isChecked());
483 settings.setValue("offset-bearing", m_ui->offsetBearingSpinbox->value());
484 settings.setValue("offset-distance", m_ui->offsetNmSpinbox->value());
486 // recent locations is saved on modification
489 void LocationWidget::setLocationOptions()
491 flightgear::Options* opt = flightgear::Options::sharedInstance();
493 std::string altStr = QString::number(m_ui->altitudeSpinbox->value()).toStdString();
494 std::string vcStr = QString::number(m_ui->airspeedSpinbox->value()).toStdString();
495 std::string headingStr = QString::number(m_ui->headingSpinbox->value()).toStdString();
497 // flip direction of azimuth to balance the flip done in fgApplyStartOffset
498 // I don't know why that flip exists but changing it there will break
499 // command-line compatability so compensating here instead
500 int offsetAzimuth = m_ui->offsetBearingSpinbox->value() - 180;
501 std::string azimuthStr = QString::number(offsetAzimuth).toStdString();
502 std::string distanceStr = QString::number(m_ui->offsetNmSpinbox->value()).toStdString();
504 if (m_locationIsLatLon) {
505 // bypass the options mechanism because converting to deg:min:sec notation
506 // just to parse back again is nasty.
507 fgSetDouble("/sim/presets/latitude-deg", m_geodLocation.getLatitudeDeg());
508 fgSetDouble("/position/latitude-deg", m_geodLocation.getLatitudeDeg());
509 fgSetDouble("/sim/presets/longitude-deg", m_geodLocation.getLongitudeDeg());
510 fgSetDouble("/position/longitude-deg", m_geodLocation.getLongitudeDeg());
512 opt->addOption("altitude", altStr);
513 opt->addOption("vc", vcStr);
514 opt->addOption("heading", headingStr);
516 if (m_ui->offsetGroup->isChecked()) {
517 opt->addOption("offset-azimuth", azimuthStr);
518 opt->addOption("offset-distance", distanceStr);
527 if (FGAirport::isAirportType(m_location.ptr())) {
528 FGAirport* apt = static_cast<FGAirport*>(m_location.ptr());
529 opt->addOption("airport", apt->ident());
531 if (m_ui->runwayRadio->isChecked()) {
532 if (apt->type() == FGPositioned::AIRPORT) {
533 int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt();
535 // explicit runway choice
536 FGRunwayRef runway = apt->getRunwayByIndex(index);
537 opt->addOption("runway", runway->ident());
539 // set nav-radio 1 based on selected runway
541 double mhz = runway->ILS()->get_freq() / 100.0;
542 QString navOpt = QString("%1:%2").arg(runway->headingDeg()).arg(mhz);
543 opt->addOption("nav1", navOpt.toStdString());
547 if (m_ui->onFinalCheckbox->isChecked()) {
548 opt->addOption("glideslope", "3.0");
549 double offsetNm = m_ui->approachDistanceSpin->value();
550 opt->addOption("offset-distance", QString::number(offsetNm).toStdString());
552 } else if (apt->type() == FGPositioned::HELIPORT) {
553 int index = m_ui->runwayCombo->itemData(m_ui->runwayCombo->currentIndex()).toInt();
555 // explicit pad choice
556 FGHelipadRef pad = apt->getHelipadByIndex(index);
557 opt->addOption("runway", pad->ident());
560 qWarning() << Q_FUNC_INFO << "implement me";
563 } else if (m_ui->parkingRadio->isChecked()) {
565 opt->addOption("parkpos", m_ui->parkingCombo->currentText().toStdString());
567 // of location is an airport
569 // location is a navaid
570 // note setting the ident here is ambigious, we really only need and
571 // want the 'navaid-id' property. However setting the 'real' option
572 // gives a better UI experience (eg existing Position in Air dialog)
573 FGPositioned::Type ty = m_location->type();
575 case FGPositioned::VOR:
576 opt->addOption("vor", m_location->ident());
580 case FGPositioned::NDB:
581 opt->addOption("ndb", m_location->ident());
585 case FGPositioned::FIX:
586 opt->addOption("fix", m_location->ident());
592 opt->addOption("altitude", altStr);
593 opt->addOption("vc", vcStr);
594 opt->addOption("heading", headingStr);
596 // set disambiguation property
597 globals->get_props()->setIntValue("/sim/presets/navaid-id",
598 static_cast<int>(m_location->guid()));
600 if (m_ui->offsetGroup->isChecked()) {
601 opt->addOption("offset-azimuth", azimuthStr);
602 opt->addOption("offset-distance", distanceStr);
604 } // of navaid location
607 void LocationWidget::setNavRadioOption()
609 flightgear::Options* opt = flightgear::Options::sharedInstance();
611 if (m_location->type() == FGPositioned::VOR) {
612 FGNavRecordRef nav(static_cast<FGNavRecord*>(m_location.ptr()));
613 double mhz = nav->get_freq() / 100.0;
614 int heading = 0; // add heading support
615 QString navOpt = QString("%1:%2").arg(heading).arg(mhz);
616 opt->addOption("nav1", navOpt.toStdString());
618 FGNavRecordRef nav(static_cast<FGNavRecord*>(m_location.ptr()));
619 int khz = nav->get_freq() / 100;
621 QString adfOpt = QString("%1:%2").arg(heading).arg(khz);
622 qDebug() << "ADF opt is:" << adfOpt;
623 opt->addOption("adf1", adfOpt.toStdString());
627 void LocationWidget::onSearch()
629 QString search = m_ui->locationSearchEdit->text();
631 m_locationIsLatLon = parseStringAsGeod(search, m_geodLocation);
632 if (m_locationIsLatLon) {
633 m_ui->searchIcon->setVisible(false);
634 m_ui->searchStatusText->setText(QString("Position '%1'").arg(formatGeodAsString(m_geodLocation)));
641 m_searchModel->setSearch(search, m_aircraftType);
643 if (m_searchModel->isSearchActive()) {
644 m_ui->searchStatusText->setText(QString("Searching for '%1'").arg(search));
645 m_ui->searchIcon->setVisible(true);
646 m_ui->searchIcon->movie()->start();
647 } else if (m_searchModel->rowCount(QModelIndex()) == 1) {
648 setBaseLocation(m_searchModel->itemAtRow(0));
652 void LocationWidget::onSearchComplete()
654 QString search = m_ui->locationSearchEdit->text();
655 m_ui->searchIcon->setVisible(false);
656 m_ui->searchStatusText->setText(QString("Results for '%1'").arg(search));
658 int numResults = m_searchModel->rowCount(QModelIndex());
659 if (numResults == 0) {
660 m_ui->searchStatusText->setText(QString("No matches for '%1'").arg(search));
661 } else if (numResults == 1) {
662 addToRecent(m_searchModel->itemAtRow(0));
663 setBaseLocation(m_searchModel->itemAtRow(0));
667 void LocationWidget::onLocationChanged()
669 bool locIsAirport = FGAirport::isAirportType(m_location.ptr());
670 m_backButton->show();
673 m_ui->stack->setCurrentIndex(0);
674 FGAirport* apt = static_cast<FGAirport*>(m_location.ptr());
675 m_ui->airportDiagram->setAirport(apt);
677 m_ui->runwayRadio->setChecked(true); // default back to runway mode
678 // unless multiplayer is enabled ?
679 m_ui->airportDiagram->setEnabled(true);
681 m_ui->runwayCombo->clear();
682 m_ui->runwayCombo->addItem("Automatic", -1);
684 if (apt->type() == FGPositioned::HELIPORT) {
685 for (unsigned int r=0; r<apt->numHelipads(); ++r) {
686 FGHelipadRef pad = apt->getHelipadByIndex(r);
687 // add pad with index as data role
688 m_ui->runwayCombo->addItem(QString::fromStdString(pad->ident()), r);
690 m_ui->airportDiagram->addHelipad(pad);
693 for (unsigned int r=0; r<apt->numRunways(); ++r) {
694 FGRunwayRef rwy = apt->getRunwayByIndex(r);
695 // add runway with index as data role
696 m_ui->runwayCombo->addItem(QString::fromStdString(rwy->ident()), r);
698 m_ui->airportDiagram->addRunway(rwy);
702 m_ui->parkingCombo->clear();
703 FGAirportDynamics* dynamics = apt->getDynamics();
704 PositionedIDVec parkings = NavDataCache::instance()->airportItemsOfType(m_location->guid(),
705 FGPositioned::PARKING);
706 if (parkings.empty()) {
707 m_ui->parkingCombo->setEnabled(false);
708 m_ui->parkingRadio->setEnabled(false);
710 m_ui->parkingCombo->setEnabled(true);
711 m_ui->parkingRadio->setEnabled(true);
712 Q_FOREACH(PositionedID parking, parkings) {
713 FGParking* park = dynamics->getParking(parking);
714 m_ui->parkingCombo->addItem(QString::fromStdString(park->getName()),
715 static_cast<qlonglong>(parking));
717 m_ui->airportDiagram->addParking(park);
721 } else if (m_locationIsLatLon) {
722 m_ui->stack->setCurrentIndex(1);
723 m_ui->navaidDiagram->setGeod(m_geodLocation);
724 } else if (m_location) {
726 m_ui->stack->setCurrentIndex(1);
727 m_ui->navaidDiagram->setNavaid(m_location);
731 void LocationWidget::onOffsetEnabledToggled(bool on)
733 m_ui->navaidDiagram->setOffsetEnabled(on);
737 void LocationWidget::onAirportRunwayClicked(FGRunwayRef rwy)
740 m_ui->runwayRadio->setChecked(true);
741 int rwyIndex = m_ui->runwayCombo->findText(QString::fromStdString(rwy->ident()));
742 m_ui->runwayCombo->setCurrentIndex(rwyIndex);
743 m_ui->airportDiagram->setSelectedRunway(rwy);
749 void LocationWidget::onAirportParkingClicked(FGParkingRef park)
752 m_ui->parkingRadio->setChecked(true);
753 int parkingIndex = m_ui->parkingCombo->findText(QString::fromStdString(park->name()));
754 m_ui->parkingCombo->setCurrentIndex(parkingIndex);
755 m_ui->airportDiagram->setSelectedRunway(FGRunwayRef());
761 QString compassPointFromHeading(int heading)
763 const int labelArc = 360 / 8;
764 heading += (labelArc >> 1);
765 SG_NORMALIZE_RANGE(heading, 0, 359);
767 switch (heading / labelArc) {
781 QString LocationWidget::locationDescription() const
784 if (m_locationIsLatLon) {
785 return QString("at position %1").arg(formatGeodAsString(m_geodLocation));
788 return QString("No location selected");
791 bool locIsAirport = FGAirport::isAirportType(m_location.ptr());
792 QString ident = QString::fromStdString(m_location->ident()),
793 name = QString::fromStdString(m_location->name());
795 name = fixNavaidName(name);
798 //FGAirport* apt = static_cast<FGAirport*>(m_location.ptr());
799 QString locationOnAirport;
801 if (m_ui->runwayRadio->isChecked()) {
802 bool onFinal = m_ui->onFinalCheckbox->isChecked();
803 int comboIndex = m_ui->runwayCombo->currentIndex();
804 QString runwayName = (comboIndex == 0) ?
806 QString("runway %1").arg(m_ui->runwayCombo->currentText());
809 int finalDistance = m_ui->approachDistanceSpin->value();
810 locationOnAirport = QString("on %2-mile final to %1").arg(runwayName).arg(finalDistance);
812 locationOnAirport = QString("on %1").arg(runwayName);
814 } else if (m_ui->parkingRadio->isChecked()) {
815 locationOnAirport = QString("at parking position %1").arg(m_ui->parkingCombo->currentText());
818 return QString("%2 (%1): %3").arg(ident).arg(name).arg(locationOnAirport);
820 QString offsetDesc = tr("at");
821 if (m_ui->offsetGroup->isChecked()) {
822 offsetDesc = QString("%1nm %2 of").
823 arg(m_ui->offsetNmSpinbox->value(), 0, 'f', 1).
824 arg(compassPointFromHeading(m_ui->offsetBearingSpinbox->value()));
828 switch (m_location->type()) {
829 case FGPositioned::VOR:
830 navaidType = QString("VOR"); break;
831 case FGPositioned::NDB:
832 navaidType = QString("NDB"); break;
833 case FGPositioned::FIX:
834 return QString("%2 waypoint %1").arg(ident).arg(offsetDesc);
840 return QString("%4 %1 %2 (%3)").arg(navaidType).arg(ident).arg(name).arg(offsetDesc);
843 return tr("No location selected");
847 void LocationWidget::updateDescription()
849 bool locIsAirport = FGAirport::isAirportType(m_location.ptr());
851 FGAirport* apt = static_cast<FGAirport*>(m_location.ptr());
853 if (m_ui->runwayRadio->isChecked()) {
854 int comboIndex = m_ui->runwayCombo->currentIndex();
855 int runwayIndex = m_ui->runwayCombo->itemData(comboIndex).toInt();
856 if (apt->type() == FGPositioned::HELIPORT) {
857 m_ui->airportDiagram->setSelectedRunway(FGRunwayRef());
858 FGHelipadRef pad = (runwayIndex >= 0) ?
859 apt->getHelipadByIndex(runwayIndex) : FGHelipadRef();
860 m_ui->airportDiagram->setSelectedHelipad(pad);
862 // we can't figure out the active runway in the launcher (yet)
863 FGRunwayRef rwy = (runwayIndex >= 0) ?
864 apt->getRunwayByIndex(runwayIndex) : FGRunwayRef();
865 m_ui->airportDiagram->setSelectedRunway(rwy);
869 if (m_ui->onFinalCheckbox->isChecked()) {
870 m_ui->airportDiagram->setApproachExtensionDistance(m_ui->approachDistanceSpin->value());
872 m_ui->airportDiagram->setApproachExtensionDistance(0.0);
880 QString locationOnAirport;
881 if (m_ui->runwayRadio->isChecked()) {
884 } else if (m_ui->parkingRadio->isChecked()) {
885 locationOnAirport = QString("at parking position %1").arg(m_ui->parkingCombo->currentText());
888 m_ui->airportDescription->setText();
891 emit descriptionChanged(locationDescription());
894 void LocationWidget::onSearchResultSelected(const QModelIndex& index)
896 FGPositionedRef pos = m_searchModel->itemAtRow(index.row());
898 setBaseLocation(pos);
901 void LocationWidget::onOffsetBearingTrueChanged(bool on)
903 m_ui->offsetBearingLabel->setText(on ? tr("True bearing:") :
904 tr("Magnetic bearing:"));
907 void LocationWidget::addToRecent(FGPositionedRef pos)
909 FGPositionedList::iterator it = std::find(m_recentLocations.begin(),
910 m_recentLocations.end(),
912 if (it != m_recentLocations.end()) {
913 m_recentLocations.erase(it);
916 if (m_recentLocations.size() >= MAX_RECENT_LOCATIONS) {
917 m_recentLocations.pop_back();
920 m_recentLocations.insert(m_recentLocations.begin(), pos);
922 settings.setValue("recent-locations", savePositionList(m_recentLocations));
926 void LocationWidget::onShowHistory()
928 qDebug() << Q_FUNC_INFO;
929 m_searchModel->setItems(m_recentLocations);
932 void LocationWidget::setBaseLocation(FGPositionedRef ref)
934 m_locationIsLatLon = false;
935 if (m_location == ref)
944 void LocationWidget::setAircraftType(LauncherAircraftType ty)
947 // nothing happens until next search
948 m_ui->navaidDiagram->setAircraftType(ty);
949 m_ui->airportDiagram->setAircraftType(ty);
952 void LocationWidget::onOffsetDataChanged()
954 m_ui->navaidDiagram->setOffsetEnabled(m_ui->offsetGroup->isChecked());
955 m_ui->navaidDiagram->setOffsetBearingDeg(m_ui->offsetBearingSpinbox->value());
956 m_ui->navaidDiagram->setOffsetDistanceNm(m_ui->offsetNmSpinbox->value());
961 void LocationWidget::onHeadingChanged()
963 m_ui->navaidDiagram->setHeadingDeg(m_ui->headingSpinbox->value());
966 void LocationWidget::onBackToSearch()
968 m_ui->stack->setCurrentIndex(2);
969 m_backButton->hide();
972 #include "LocationWidget.moc"