From 0da8d8599940ab880048b898149308ccab2c44f3 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Thu, 29 Nov 2018 03:19:45 +0100 Subject: [PATCH] Ref T442, utility functions for logically sorting by suffix --- src/blackgui/models/atcstationlistmodel.cpp | 14 +++- src/blackmisc/aviation/atcstation.cpp | 5 ++ src/blackmisc/aviation/atcstation.h | 3 + src/blackmisc/aviation/atcstationlist.cpp | 12 ++++ src/blackmisc/aviation/atcstationlist.h | 9 ++- src/blackmisc/aviation/callsign.cpp | 68 ++++++++++++++----- src/blackmisc/aviation/callsign.h | 57 ++++++++-------- src/blackmisc/aviation/callsignobjectlist.cpp | 17 ++++- src/blackmisc/aviation/callsignobjectlist.h | 7 +- src/blackmisc/icons.h | 2 +- 10 files changed, 141 insertions(+), 53 deletions(-) diff --git a/src/blackgui/models/atcstationlistmodel.cpp b/src/blackgui/models/atcstationlistmodel.cpp index 725ef0d7e..ed5653ab5 100644 --- a/src/blackgui/models/atcstationlistmodel.cpp +++ b/src/blackgui/models/atcstationlistmodel.cpp @@ -114,11 +114,13 @@ namespace BlackGui QStandardItemModel *model = new QStandardItemModel(); if (this->isEmpty()) { return model; } model->setColumnCount(4); - QMap types = this->container().getSuffixes(); - for (const QString &type : types.keys()) + const CAtcStationList atcStations = this->container().sortedByAtcSuffixSortOrderAndDistance(); + const QStringList types = atcStations.getSuffixes(); + for (const QString &type : types) { // ownership of QStandardItem is taken by model QStandardItem *typeFolderFirstColumn = new QStandardItem(CCallsign::atcSuffixToIcon(type).toQIcon(), type); + typeFolderFirstColumn->setEditable(false); QList typeFolderRow { typeFolderFirstColumn }; model->invisibleRootItem()->appendRow(typeFolderRow); CAtcStationList stations = this->container().findBySuffix(type); @@ -150,6 +152,14 @@ namespace BlackGui Q_ASSERT(false); break; } + + // make not editable + for (QStandardItem *si : stationRow) + { + si->setEditable(false); + } + + // add all items typeFolderFirstColumn->appendRow(stationRow); } } diff --git a/src/blackmisc/aviation/atcstation.cpp b/src/blackmisc/aviation/atcstation.cpp index 547e5286d..2849e9bc2 100644 --- a/src/blackmisc/aviation/atcstation.cpp +++ b/src/blackmisc/aviation/atcstation.cpp @@ -70,6 +70,11 @@ namespace BlackMisc return m_callsign.getSuffix(); } + int CAtcStation::getSuffixSortOrder() const + { + return m_callsign.getSuffixSortOrder(); + } + void CAtcStation::setCallsign(const CCallsign &callsign) { m_callsign = callsign; diff --git a/src/blackmisc/aviation/atcstation.h b/src/blackmisc/aviation/atcstation.h index a4a22ff98..08d7f60b2 100644 --- a/src/blackmisc/aviation/atcstation.h +++ b/src/blackmisc/aviation/atcstation.h @@ -95,6 +95,9 @@ namespace BlackMisc //! Callsign suffix (e.g. TWR) QString getCallsignSuffix() const; + //! Callsign suffix sort order + int getSuffixSortOrder() const; + //! Set callsign void setCallsign(const CCallsign &callsign); diff --git a/src/blackmisc/aviation/atcstationlist.cpp b/src/blackmisc/aviation/atcstationlist.cpp index 99114cfb5..87253879f 100644 --- a/src/blackmisc/aviation/atcstationlist.cpp +++ b/src/blackmisc/aviation/atcstationlist.cpp @@ -126,5 +126,17 @@ namespace BlackMisc Q_ASSERT_X(c == 0 || c == 1, Q_FUNC_INFO, "Found >1 matching station"); return c; } + + void CAtcStationList::sortByAtcSuffixSortOrderAndDistance() + { + this->sortBy(&CAtcStation::getSuffixSortOrder, &CAtcStation::getRelativeDistance); + } + + CAtcStationList CAtcStationList::sortedByAtcSuffixSortOrderAndDistance() const + { + CAtcStationList stations = *this; + stations.sortByAtcSuffixSortOrderAndDistance(); + return stations; + } } // namespace } // namespace diff --git a/src/blackmisc/aviation/atcstationlist.h b/src/blackmisc/aviation/atcstationlist.h index 23b27f9e8..8549c5241 100644 --- a/src/blackmisc/aviation/atcstationlist.h +++ b/src/blackmisc/aviation/atcstationlist.h @@ -66,7 +66,7 @@ namespace BlackMisc CAtcStationList stationsWithValidFrequency() const; //! All controllers (with valid data) - BlackMisc::Network::CUserList getControllers() const; + Network::CUserList getControllers() const; //! Remove if marked outside of range int removeIfOutsideRange(); @@ -75,8 +75,13 @@ namespace BlackMisc //! Both sides (booking, online station) will be updated. //! \pre Can be used only if the stored data in this list are online ATC stations int synchronizeWithBookedStation(CAtcStation &bookedAtcStation); - }; + //! Sort by ATC suffix sort order and distance + void sortByAtcSuffixSortOrderAndDistance(); + + //! Sorted by ATC suffix sort order and distance + CAtcStationList sortedByAtcSuffixSortOrderAndDistance() const; + }; } //namespace } // namespace diff --git a/src/blackmisc/aviation/callsign.cpp b/src/blackmisc/aviation/callsign.cpp index aa8764a1a..37aa620d8 100644 --- a/src/blackmisc/aviation/callsign.cpp +++ b/src/blackmisc/aviation/callsign.cpp @@ -21,6 +21,18 @@ namespace BlackMisc { namespace Aviation { + CCallsign::CCallsign(const QString &callsign, CCallsign::TypeHint hint) + : m_callsignAsSet(callsign.trimmed()), m_callsign(CCallsign::unifyCallsign(callsign)), m_typeHint(hint) + {} + + CCallsign::CCallsign(const QString &callsign, const QString &telephonyDesignator, CCallsign::TypeHint hint) + : m_callsignAsSet(callsign.trimmed()), m_callsign(CCallsign::unifyCallsign(callsign)), m_telephonyDesignator(telephonyDesignator.trimmed()), m_typeHint(hint) + {} + + CCallsign::CCallsign(const char *callsign, CCallsign::TypeHint hint) + : m_callsignAsSet(callsign), m_callsign(CCallsign::unifyCallsign(callsign)), m_typeHint(hint) + {} + void CCallsign::registerMetadata() { CValueObject::registerMetadata(); @@ -38,6 +50,23 @@ namespace BlackMisc *this = CCallsign(); } + int CCallsign::suffixToSortOrder(const QString &suffix) + { + if ("CTR" == suffix) { return 1; } + if ("APP" == suffix) { return 2; } + if ("FSS" == suffix) { return 3; } + if ("TWR" == suffix) { return 5; } + if ("GND" == suffix) { return 6; } + if ("DEL" == suffix) { return 7; } + if ("ATIS" == suffix) { return 8; } + if ("SUP" == suffix) { return 10; } + if ("OBS" == suffix) { return 11; } + if ("INS" == suffix) { return 13; } // instructor/mentor + if ("EXAM" == suffix) { return 14; } + if ("VATSIM" == suffix) { return 14; } + return std::numeric_limits::max(); + } + QString CCallsign::unifyCallsign(const QString &callsign) { return removeChars(callsign.toUpper().trimmed(), [](QChar c) { return !c.isLetterOrNumber() && c != '_'; }); @@ -58,18 +87,18 @@ namespace BlackMisc const CIcon &CCallsign::atcSuffixToIcon(const QString &suffix) { if (suffix.length() < 3) { return CIcon::iconByIndex(CIcons::NetworkRoleUnknown); } - QString sfx = suffix.toUpper(); - if ("APP" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleApproach); } - if ("GND" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleGround); } - if ("TWR" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleTower); } - if ("DEL" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleDelivery); } - if ("CTR" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleCenter); } - if ("SUP" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleSup); } - if ("OBS" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleObs); } - if ("INS" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleMnt); } - if ("FSS" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleFss); } + const QString sfx = suffix.toUpper(); + if ("APP" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleApproach); } + if ("GND" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleGround); } + if ("TWR" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleTower); } + if ("DEL" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleDelivery); } + if ("CTR" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleCenter); } + if ("SUP" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleSup); } + if ("OBS" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleObs); } + if ("INS" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleMnt); } + if ("FSS" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleFss); } if ("ATIS" == sfx) { return CIcon::iconByIndex(CIcons::AviationAtis); } - if ("EXAM" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleMnt); } + if ("EXAM" == sfx) { return CIcon::iconByIndex(CIcons::NetworkRoleMnt); } if ("VATSIM" == sfx) { return CIcon::iconByIndex(CIcons::NetworkVatsimLogoWhite); } return CIcon::iconByIndex(CIcons::NetworkRoleUnknown); } @@ -122,7 +151,7 @@ namespace BlackMisc { if (this->isEmpty()) { return ""; } QString obs = this->getStringAsSet(); - if (obs.endsWith("_OBS", Qt::CaseInsensitive)) { return obs; } + if (obs.endsWith("_OBS", Qt::CaseInsensitive)) { return obs; } // already OBS if (obs.contains('_')) { obs = obs.left(obs.lastIndexOf('_')); } return obs.append("_OBS").toUpper(); } @@ -169,6 +198,11 @@ namespace BlackMisc return !s.isEmpty() && atcCallsignSuffixes().contains(s); } + int CCallsign::getSuffixSortOrder() const + { + return suffixToSortOrder(this->getSuffix()); + } + bool CCallsign::equalsString(const QString &callsignString) const { CCallsign other(callsignString); @@ -181,10 +215,10 @@ namespace BlackMisc const ColumnIndex i = index.frontCasted(); switch (i) { - case IndexCallsignString: return CVariant(this->asString()); + case IndexCallsignString: return CVariant(this->asString()); case IndexCallsignStringAsSet: return CVariant(this->getStringAsSet()); case IndexTelephonyDesignator: return CVariant(this->getTelephonyDesignator()); - case IndexSuffix: return CVariant(this->getSuffix()); + case IndexSuffix: return CVariant(this->getSuffix()); default: return CValueObject::propertyByIndex(index); } } @@ -195,7 +229,7 @@ namespace BlackMisc const ColumnIndex i = index.frontCasted(); switch (i) { - case IndexCallsignString: m_callsign = m_callsign = unifyCallsign(variant.toQString()); break; + case IndexCallsignString: m_callsign = unifyCallsign(variant.toQString()); break; case IndexCallsignStringAsSet: m_callsignAsSet = variant.toQString(); break; case IndexTelephonyDesignator: m_telephonyDesignator = variant.toQString(); break; default: @@ -210,10 +244,10 @@ namespace BlackMisc const ColumnIndex i = index.frontCasted(); switch (i) { - case IndexCallsignString: return m_callsign.compare(compareValue.m_callsign, Qt::CaseInsensitive); + case IndexCallsignString: return m_callsign.compare(compareValue.m_callsign, Qt::CaseInsensitive); case IndexCallsignStringAsSet: return m_callsignAsSet.compare(compareValue.m_callsignAsSet, Qt::CaseInsensitive); case IndexTelephonyDesignator: return m_telephonyDesignator.compare(compareValue.m_telephonyDesignator, Qt::CaseInsensitive); - case IndexSuffix: return this->getSuffix().compare(compareValue.getSuffix(), Qt::CaseInsensitive); + case IndexSuffix: return this->getSuffix().compare(compareValue.getSuffix(), Qt::CaseInsensitive); default: return CValueObject::comparePropertyByIndex(index, compareValue); } diff --git a/src/blackmisc/aviation/callsign.h b/src/blackmisc/aviation/callsign.h index f7f41ad7f..b8d873fbf 100644 --- a/src/blackmisc/aviation/callsign.h +++ b/src/blackmisc/aviation/callsign.h @@ -38,7 +38,8 @@ namespace BlackMisc IndexCallsignString = CPropertyIndex::GlobalIndexCCallsign, IndexCallsignStringAsSet, IndexTelephonyDesignator, - IndexSuffix + IndexSuffix, + IndexSuffixSortOrder }; //! Representing what @@ -53,19 +54,13 @@ namespace BlackMisc CCallsign() {} //! Constructor - CCallsign(const QString &callsign, TypeHint hint = NoHint) - : m_callsignAsSet(callsign.trimmed()), m_callsign(CCallsign::unifyCallsign(callsign)), m_typeHint(hint) - {} + CCallsign(const QString &callsign, TypeHint hint = NoHint); //! Constructor - CCallsign(const QString &callsign, const QString &telephonyDesignator, TypeHint hint = NoHint) - : m_callsignAsSet(callsign.trimmed()), m_callsign(CCallsign::unifyCallsign(callsign)), m_telephonyDesignator(telephonyDesignator.trimmed()), m_typeHint(hint) - {} + CCallsign(const QString &callsign, const QString &telephonyDesignator, TypeHint hint = NoHint); //! Constructor, needed to disambiguate implicit conversion from string literal. - CCallsign(const char *callsign, TypeHint hint = NoHint) - : m_callsignAsSet(callsign), m_callsign(CCallsign::unifyCallsign(callsign)), m_typeHint(hint) - {} + CCallsign(const char *callsign, TypeHint hint = NoHint); //! Is empty? bool isEmpty() const { return m_callsignAsSet.isEmpty(); } @@ -122,12 +117,36 @@ namespace BlackMisc //! Has an ATC suffix? bool hasAtcSuffix() const; + //! Sort order by suffix + int getSuffixSortOrder() const; + //! Equals callsign string? bool equalsString(const QString &callsignString) const; //! Valid callsign? bool isValid() const; + //! \copydoc BlackMisc::Mixin::Icon::toIcon() + CIcon toIcon() const { return convertToIcon(*this); } + + //! \copydoc BlackMisc::Mixin::Index::propertyByIndex + CVariant propertyByIndex(const CPropertyIndex &index) const; + + //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex + void setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant); + + //! \copydoc BlackMisc::Mixin::Index::comparePropertyByIndex + int comparePropertyByIndex(const CPropertyIndex &index, const CCallsign &compareValue) const; + + //! \copydoc BlackMisc::Mixin::String::toQString() + QString convertToQString(bool i18n = false) const; + + //! Clear this callsign + void clear(); + + //! Index for ATC suffix, if unknown int max value + static int suffixToSortOrder(const QString &suffix); + //! Valid callsign? static bool isValidAircraftCallsign(const QString &callsign); @@ -155,24 +174,6 @@ namespace BlackMisc //! Representing icon static const CIcon &convertToIcon(const CCallsign &callsign); - //! \copydoc BlackMisc::Mixin::Icon::toIcon() - CIcon toIcon() const { return convertToIcon(*this); } - - //! \copydoc BlackMisc::Mixin::Index::propertyByIndex - CVariant propertyByIndex(const CPropertyIndex &index) const; - - //! \copydoc BlackMisc::Mixin::Index::setPropertyByIndex - void setPropertyByIndex(const CPropertyIndex &index, const CVariant &variant); - - //! \copydoc BlackMisc::Mixin::Index::comparePropertyByIndex - int comparePropertyByIndex(const CPropertyIndex &index, const CCallsign &compareValue) const; - - //! \copydoc BlackMisc::Mixin::String::toQString() - QString convertToQString(bool i18n = false) const; - - //! Clear this callsign - void clear(); - //! Register metadata static void registerMetadata(); diff --git a/src/blackmisc/aviation/callsignobjectlist.cpp b/src/blackmisc/aviation/callsignobjectlist.cpp index 91c591ba5..da3499adf 100644 --- a/src/blackmisc/aviation/callsignobjectlist.cpp +++ b/src/blackmisc/aviation/callsignobjectlist.cpp @@ -133,9 +133,9 @@ namespace BlackMisc } template - QMap ICallsignObjectList::getSuffixes() const + QMap ICallsignObjectList::getSuffixesAndCount() const { - QMap r; + QMap r; // sorted by key for (const OBJ &csObj : this->container()) { const QString s = csObj.getCallsign().getSuffix(); @@ -152,6 +152,19 @@ namespace BlackMisc return r; } + template + QStringList ICallsignObjectList::getSuffixes() const + { + QStringList suffixes; + for (const OBJ &csObj : this->container()) + { + const QString s = csObj.getCallsign().getSuffix(); + if (s.isEmpty() || suffixes.contains(s, Qt::CaseInsensitive)) { continue; } + suffixes << s; + } + return suffixes; + } + template QHash ICallsignObjectList::splitPerCallsign() const { diff --git a/src/blackmisc/aviation/callsignobjectlist.h b/src/blackmisc/aviation/callsignobjectlist.h index 3190a98fa..15bf1fdd0 100644 --- a/src/blackmisc/aviation/callsignobjectlist.h +++ b/src/blackmisc/aviation/callsignobjectlist.h @@ -88,7 +88,12 @@ namespace BlackMisc int removeByCallsigns(const CCallsignSet &callsigns); //! All suffixes with their respective count - QMap getSuffixes() const; + //! \remark since using QMap sorted by suffix + QMap getSuffixesAndCount() const; + + //! All suffixes, in the order of the list + //! \remark first found suffixes first + QStringList getSuffixes() const; //! Split into 0..n containers as per callsign QHash splitPerCallsign() const; diff --git a/src/blackmisc/icons.h b/src/blackmisc/icons.h index d4aa31032..594006da3 100644 --- a/src/blackmisc/icons.h +++ b/src/blackmisc/icons.h @@ -77,7 +77,7 @@ namespace BlackMisc NetworkRoleGround, NetworkRoleI1, NetworkRoleI3, - NetworkRoleMnt, + NetworkRoleMnt, //!< Mentor NetworkRoleObs, NetworkRolePilot, NetworkRoleS1,