diff --git a/src/blackgui/components/airportcompleter.cpp b/src/blackgui/components/airportcompleter.cpp index cd46d4006..29e861525 100644 --- a/src/blackgui/components/airportcompleter.cpp +++ b/src/blackgui/components/airportcompleter.cpp @@ -123,7 +123,7 @@ namespace BlackGui if (m_current.isNull()) { const QString icao = this->getIcaoText(); - if (CAirportIcaoCode::isValidIcaoDesignator(icao)) + if (CAirportIcaoCode::isValidIcaoDesignator(icao, true)) { const CAirport airport = sGui->getWebDataServices()->getAirportForIcaoDesignator(icao); if (!airport.isNull()) { this->setAirport(airport); } diff --git a/src/blackgui/components/airportsmallcompleter.cpp b/src/blackgui/components/airportsmallcompleter.cpp index 27117e991..bcd3d27de 100644 --- a/src/blackgui/components/airportsmallcompleter.cpp +++ b/src/blackgui/components/airportsmallcompleter.cpp @@ -115,7 +115,7 @@ namespace BlackGui if (m_current.isNull()) { const QString icao = this->getIcaoText(); - if (CAirportIcaoCode::isValidIcaoDesignator(icao)) + if (CAirportIcaoCode::isValidIcaoDesignator(icao, true)) { const CAirport airport = sGui->getWebDataServices()->getAirportForIcaoDesignator(icao); if (!airport.isNull()) { this->setAirport(airport); } diff --git a/src/blackgui/components/atcstationcomponent.cpp b/src/blackgui/components/atcstationcomponent.cpp index d973ad952..1975ae223 100644 --- a/src/blackgui/components/atcstationcomponent.cpp +++ b/src/blackgui/components/atcstationcomponent.cpp @@ -266,7 +266,7 @@ namespace BlackGui if (!this->canAccessContext()) { return; } const CAirportIcaoCode icao(airportIcaoCode.isEmpty() ? ui->le_AtcStationsOnlineMetar->text().trimmed().toUpper() : airportIcaoCode.trimmed().toUpper()); ui->le_AtcStationsOnlineMetar->setText(icao.asString()); - if (!icao.hasValidIcaoCode()) { return; } + if (!icao.hasValidIcaoCode(true)) { return; } const CMetar metar(sGui->getIContextNetwork()->getMetarForAirport(icao)); if (metar.hasMessage()) { diff --git a/src/blackgui/components/flightplancomponent.cpp b/src/blackgui/components/flightplancomponent.cpp index 196747ef1..d246e6b93 100644 --- a/src/blackgui/components/flightplancomponent.cpp +++ b/src/blackgui/components/flightplancomponent.cpp @@ -399,6 +399,14 @@ namespace BlackGui else { flightPlan.setDestinationAirportIcao(v); + if (strict && !flightPlan.getDestinationAirportIcao().hasValidIcaoCode(true)) + { + messages.push_back(CStatusMessage(this).validationError(u"Invalid destination ICAO code '%1'") << v); + } + else if (!flightPlan.getDestinationAirportIcao().hasValidIcaoCode(false)) + { + messages.push_back(CStatusMessage(this).validationWarning(u"Wrong or missing '%1'") << ui->lbl_DestinationAirport->text()); + } } // origin airport @@ -411,6 +419,14 @@ namespace BlackGui else { flightPlan.setOriginAirportIcao(v); + if (strict && !flightPlan.getOriginAirportIcao().hasValidIcaoCode(true)) + { + messages.push_back(CStatusMessage(this).validationError(u"Invalid origin ICAO code '%1'") << v); + } + else if (!flightPlan.getOriginAirportIcao().hasValidIcaoCode(false)) + { + messages.push_back(CStatusMessage(this).validationWarning(u"Wrong or missing '%1'") << ui->lbl_DestinationAirport->text()); + } } // TAS diff --git a/src/blackgui/views/viewbasenontemplate.cpp b/src/blackgui/views/viewbasenontemplate.cpp index 489ecfc44..050547390 100644 --- a/src/blackgui/views/viewbasenontemplate.cpp +++ b/src/blackgui/views/viewbasenontemplate.cpp @@ -61,32 +61,38 @@ namespace BlackGui // shortcuts QShortcut *filter = new QShortcut(CShortcut::keyDisplayFilter(), this); - connect(filter, &QShortcut::activated, this, &CViewBaseNonTemplate::displayFilterDialog); + bool s = connect(filter, &QShortcut::activated, this, &CViewBaseNonTemplate::displayFilterDialog); + Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut"); filter->setObjectName("Filter shortcut for " + this->objectName()); filter->setContext(Qt::WidgetShortcut); QShortcut *clearSelection = new QShortcut(CShortcut::keyClearSelection(), this); - connect(clearSelection, &QShortcut::activated, this, &CViewBaseNonTemplate::clearSelection); + s = connect(clearSelection, &QShortcut::activated, this, &CViewBaseNonTemplate::clearSelection); + Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut"); clearSelection->setObjectName("Clear selection shortcut for " + this->objectName()); clearSelection->setContext(Qt::WidgetShortcut); QShortcut *saveJson = new QShortcut(CShortcut::keySaveViews(), this); - connect(saveJson, &QShortcut::activated, this, &CViewBaseNonTemplate::saveJsonAction); + s = connect(saveJson, &QShortcut::activated, this, &CViewBaseNonTemplate::saveJsonAction); + Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut"); saveJson->setObjectName("Save JSON for " + this->objectName()); saveJson->setContext(Qt::WidgetShortcut); QShortcut *deleteRow = new QShortcut(CShortcut::keyDelete(), this); - connect(deleteRow, &QShortcut::activated, this, &CViewBaseNonTemplate::removeSelectedRowsChecked); + s = connect(deleteRow, &QShortcut::activated, this, &CViewBaseNonTemplate::removeSelectedRowsChecked); + Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut"); deleteRow->setObjectName("Remove selected rows for " + this->objectName()); deleteRow->setContext(Qt::WidgetShortcut); QShortcut *copy = new QShortcut(CShortcut::keyCopy(), this); - connect(copy, &QShortcut::activated, this, &CViewBaseNonTemplate::copy); + s = connect(copy, &QShortcut::activated, this, &CViewBaseNonTemplate::copy); + Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut"); copy->setObjectName("Copy selection shortcut for " + this->objectName()); copy->setContext(Qt::WidgetShortcut); QShortcut *resize = new QShortcut(CShortcut::keyResizeView(), this); - connect(resize, &QShortcut::activated, this, &CViewBaseNonTemplate::resizeToContents); + s = connect(resize, &QShortcut::activated, this, &CViewBaseNonTemplate::fullResizeToContents); + Q_ASSERT_X(s, Q_FUNC_INFO, "Shortcut"); resize->setObjectName("Resize view shortcut for " + this->objectName()); resize->setContext(Qt::WidgetShortcut); } diff --git a/src/blackmisc/aviation/airporticaocode.cpp b/src/blackmisc/aviation/airporticaocode.cpp index bb1003cf6..5f024aeb0 100644 --- a/src/blackmisc/aviation/airporticaocode.cpp +++ b/src/blackmisc/aviation/airporticaocode.cpp @@ -15,9 +15,9 @@ namespace BlackMisc return m_icaoCode; } - bool CAirportIcaoCode::hasValidIcaoCode() const + bool CAirportIcaoCode::hasValidIcaoCode(bool strict) const { - return CAirportIcaoCode::isValidIcaoDesignator(this->getIcaoCode()); + return CAirportIcaoCode::isValidIcaoDesignator(this->getIcaoCode(), strict); } bool CAirportIcaoCode::equalsString(const QString &icaoCode) const @@ -29,15 +29,15 @@ namespace BlackMisc QString CAirportIcaoCode::unifyAirportCode(const QString &icaoCode) { const QString code = icaoCode.trimmed().toUpper(); - if (code.length() != 4) return {}; + if (!validCodeLength(icaoCode.length(), false)) return {}; if (containsChar(code, [](QChar c) { return !c.isLetterOrNumber(); })) { return {}; } return code; } - bool CAirportIcaoCode::isValidIcaoDesignator(const QString &icaoCode) + bool CAirportIcaoCode::isValidIcaoDesignator(const QString &icaoCode, bool strict) { const QString icao = unifyAirportCode(icaoCode); - return icao.length() == 4; + return validCodeLength(icao.length(), strict); } bool CAirportIcaoCode::containsNumbers(const QString &icaoCode) @@ -63,5 +63,16 @@ namespace BlackMisc return m_icaoCode.compare(compareValue.getIcaoCode(), Qt::CaseInsensitive); } + bool CAirportIcaoCode::validCodeLength(int l, bool strict) + { + // FAA code 3 + // ICAO code 4 + if (strict) { return l == 4; } + return l >= 3 && l <= 6; + + // https://en.wikipedia.org/wiki/Location_identifier#FAA_identifier says can be up to 5 characters + // https://en.wikipedia.org/wiki/ICAO_airport_code#Pseudo_ICAO-codes says France has some 6-character airport codes + // and ZZZZ can be used in a flight plan as ICAO code for any airport that doesn't have one + } } // namespace } // namespace diff --git a/src/blackmisc/aviation/airporticaocode.h b/src/blackmisc/aviation/airporticaocode.h index 818140648..e83f74f33 100644 --- a/src/blackmisc/aviation/airporticaocode.h +++ b/src/blackmisc/aviation/airporticaocode.h @@ -41,7 +41,7 @@ namespace BlackMisc bool isEmpty() const { return this->m_icaoCode.isEmpty(); } //! Has valid code? - bool hasValidIcaoCode() const; + bool hasValidIcaoCode(bool strict) const; //! Get code. const QString &asString() const { return this->m_icaoCode; } @@ -56,7 +56,7 @@ namespace BlackMisc static QString unifyAirportCode(const QString &icaoCode); //! Valid ICAO designator - static bool isValidIcaoDesignator(const QString &icaoCode); + static bool isValidIcaoDesignator(const QString &icaoCode, bool strict); //! Containing numbers (normally indicator for small airfield/strip) static bool containsNumbers(const QString &icaoCode); @@ -73,6 +73,9 @@ namespace BlackMisc //! \copydoc BlackMisc::Mixin::Index::comparePropertyByIndex int comparePropertyByIndex(const CPropertyIndex &index, const CAirportIcaoCode &compareValue) const; + //! Valid code lenght + static bool validCodeLength(int l, bool strict); + private: QString m_icaoCode; diff --git a/src/blackmisc/network/user.cpp b/src/blackmisc/network/user.cpp index e0a1a5610..54f83e896 100644 --- a/src/blackmisc/network/user.cpp +++ b/src/blackmisc/network/user.cpp @@ -138,7 +138,7 @@ namespace BlackMisc bool CUser::hasValidHomeBase() const { - return m_homebase.hasValidIcaoCode(); + return m_homebase.hasValidIcaoCode(false); } CStatusMessageList CUser::validate() const diff --git a/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp b/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp index f0223411a..16f35296b 100644 --- a/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp +++ b/src/plugins/simulator/fsxcommon/simulatorfsxsimconnectproc.cpp @@ -362,7 +362,7 @@ namespace BlackSimPlugin if (!pFacilityAirport) { break; } const QString icao(pFacilityAirport->Icao); if (icao.isEmpty()) { continue; } // airfield without ICAO code - if (!CAirportIcaoCode::isValidIcaoDesignator(icao)) { continue; } // tiny airfields/strips in simulator + if (!CAirportIcaoCode::isValidIcaoDesignator(icao, true)) { continue; } // tiny airfields/strips in simulator if (CAirportIcaoCode::containsNumbers(icao)) { continue; } // tiny airfields/strips in simulator const CCoordinateGeodetic pos(pFacilityAirport->Latitude, pFacilityAirport->Longitude, pFacilityAirport->Altitude); const CAirport airport(CAirportIcaoCode(icao), pos);