diff --git a/src/blackgui/models/listmodelbase.cpp b/src/blackgui/models/listmodelbase.cpp index 38a98044d..abc08206d 100644 --- a/src/blackgui/models/listmodelbase.cpp +++ b/src/blackgui/models/listmodelbase.cpp @@ -127,8 +127,8 @@ namespace BlackGui if (!formatter) { return false; } ObjectType obj = m_container[index.row()]; - ObjectType currentObject(obj); - CPropertyIndex propertyIndex = this->columnToPropertyIndex(index.column()); + const ObjectType currentObject(obj); + const CPropertyIndex propertyIndex = this->columnToPropertyIndex(index.column()); obj.setPropertyByIndex(propertyIndex, value); if (obj != currentObject) @@ -149,7 +149,7 @@ namespace BlackGui bool CListModelBase::setInContainer(const QModelIndex &index, const ObjectType &obj) { if (!index.isValid()) { return false; } - int row = index.row(); + const int row = index.row(); if (row < 0 || row >= this->container().size()) { return false; } m_container[row] = obj; return true; @@ -215,8 +215,8 @@ namespace BlackGui { Q_UNUSED(sort); if (m_modelDestroyed) { return nullptr; } - auto sortColumn = this->getSortColumn(); - auto sortOrder = this->getSortOrder(); + const auto sortColumn = this->getSortColumn(); + const auto sortOrder = this->getSortOrder(); CWorker *worker = CWorker::fromTask(this, "ModelSort", [this, container, sortColumn, sortOrder]() { return this->sortContainerByColumn(container, sortColumn, sortOrder); @@ -399,11 +399,11 @@ namespace BlackGui template void CListModelBase::remove(const ObjectType &object) { - int oldSize = m_container.size(); + const int oldSize = m_container.size(); beginRemoveRows(QModelIndex(), 0, 0); m_container.remove(object); endRemoveRows(); - int newSize = m_container.size(); + const int newSize = m_container.size(); if (oldSize != newSize) { this->emitModelDataChanged(); @@ -533,10 +533,10 @@ namespace BlackGui } // sort the values - std::integral_constant marker {}; + const std::integral_constant marker {}; const auto p = [ = ](const ObjectType & a, const ObjectType & b) -> bool { - return Private::compareForModelSort(a, b, order, propertyIndex, marker); + return Private::compareForModelSort(a, b, order, propertyIndex, m_sortTieBreakers, marker); }; return container.sorted(p); @@ -555,7 +555,7 @@ namespace BlackGui for (const QModelIndex &index : indexes) { if (!index.isValid()) { continue; } - int r = index.row(); + const int r = index.row(); if (rows.contains(r)) { continue; } container.push_back(this->at(index)); rows.append(r); diff --git a/src/blackgui/models/listmodelbase.h b/src/blackgui/models/listmodelbase.h index 09873f009..0a35d9dbf 100644 --- a/src/blackgui/models/listmodelbase.h +++ b/src/blackgui/models/listmodelbase.h @@ -193,19 +193,32 @@ namespace BlackGui { //! Sort with compare function template - bool compareForModelSort(const ObjectType &a, const ObjectType &b, Qt::SortOrder order, const BlackMisc::CPropertyIndex &index, std::true_type) + bool compareForModelSort(const ObjectType &a, const ObjectType &b, Qt::SortOrder order, const BlackMisc::CPropertyIndex &index, const BlackMisc::CPropertyIndexList &tieBreakers, std::true_type) { const int c = a.comparePropertyByIndex(index, b); - if (c == 0) { return false; } + if (c == 0) + { + if (!tieBreakers.isEmpty()) + { + const std::integral_constant marker; + return compareForModelSort(a, b, order, tieBreakers.front(), tieBreakers.copyFrontRemoved(), marker); + } + return false; + } return (order == Qt::AscendingOrder) ? (c < 0) : (c > 0); } //! Sort without compare function template - bool compareForModelSort(const ObjectType &a, const ObjectType &b, Qt::SortOrder order, const BlackMisc::CPropertyIndex &index, std::false_type) + bool compareForModelSort(const ObjectType &a, const ObjectType &b, Qt::SortOrder order, const BlackMisc::CPropertyIndex &index, const BlackMisc::CPropertyIndexList &tieBreakers, std::false_type) { const BlackMisc::CVariant aQv = a.propertyByIndex(index); const BlackMisc::CVariant bQv = b.propertyByIndex(index); + if (!tieBreakers.isEmpty() && aQv == bQv) + { + const std::integral_constant marker; + return compareForModelSort(a, b, order, tieBreakers.front(), tieBreakers.copyFrontRemoved(), marker); + } return (order == Qt::AscendingOrder) ? (aQv < bQv) : (bQv < aQv); } } // namespace diff --git a/src/blackgui/models/listmodelbasenontemplate.h b/src/blackgui/models/listmodelbasenontemplate.h index 6fba05c2f..c392b5fc5 100644 --- a/src/blackgui/models/listmodelbasenontemplate.h +++ b/src/blackgui/models/listmodelbasenontemplate.h @@ -163,6 +163,7 @@ namespace BlackGui bool m_modelDestroyed = false; //!< model is about to be destroyed Qt::SortOrder m_sortOrder; //!< sort order (asc/desc) Qt::DropActions m_dropActions = Qt::IgnoreAction; //!< drop actions + BlackMisc::CPropertyIndexList m_sortTieBreakers; //!< how column values are sorted if equal, if no value is given this is random private: BlackMisc::CDigestSignal m_dsModelsChanged { this, &CListModelBaseNonTemplate::changed, &CListModelBaseNonTemplate::onChangedDigest, 500, 10 }; diff --git a/src/blackgui/models/listmodeldbobjects.cpp b/src/blackgui/models/listmodeldbobjects.cpp index d07d12f2f..77680bb14 100644 --- a/src/blackgui/models/listmodeldbobjects.cpp +++ b/src/blackgui/models/listmodeldbobjects.cpp @@ -40,8 +40,10 @@ namespace BlackGui CListModelDbObjects::CListModelDbObjects(const QString &translationContext, QObject *parent) : CListModelBase(translationContext, parent) { + CListModelBaseNonTemplate::m_sortTieBreakers.push_front(ObjectType::keyIndex()); + constexpr bool hasIntegerKey = std::is_base_of::value && std::is_same::value; - constexpr bool hasStringKey = std::is_base_of::value && std::is_base_of::value; + constexpr bool hasStringKey = std::is_base_of::value && std::is_base_of::value; static_assert(hasIntegerKey || hasStringKey, "ObjectType needs to implement IDatastoreObjectWithXXXXKey and have appropriate KeyType"); } @@ -49,7 +51,7 @@ namespace BlackGui QVariant CListModelDbObjects::data(const QModelIndex &index, int role) const { if (role != Qt::BackgroundRole) { return CListModelBase::data(index, role); } - if (isHighlightedIndex(index) ) { return QBrush(m_highlightColor); } + if (isHighlightedIndex(index)) { return QBrush(m_highlightColor); } return CListModelBase::data(index, role); } diff --git a/src/blackgui/models/statusmessagelistmodel.cpp b/src/blackgui/models/statusmessagelistmodel.cpp index 9f0785f27..e7f8d3efa 100644 --- a/src/blackgui/models/statusmessagelistmodel.cpp +++ b/src/blackgui/models/statusmessagelistmodel.cpp @@ -26,6 +26,8 @@ namespace BlackGui CListModelTimestampObjects("ViewStatusMessageList", parent) { this->setMode(Detailed); + m_sortTieBreakers.push_front(CStatusMessage::IndexMessage); + m_sortTieBreakers.push_front(CStatusMessage::IndexSeverity); // force strings for translation in resource files (void)QT_TRANSLATE_NOOP("ViewStatusMessageList", "time"); diff --git a/src/blackmisc/db/datastore.h b/src/blackmisc/db/datastore.h index 94cf8b3a7..96accefbd 100644 --- a/src/blackmisc/db/datastore.h +++ b/src/blackmisc/db/datastore.h @@ -120,6 +120,9 @@ namespace BlackMisc //! Convert string to DB key static int stringToDbKey(const QString &candidate); + //! The key index + static const CPropertyIndex &keyIndex() { static const CPropertyIndex k(IndexDbIntegerKey); return k; } + protected: //! Constructor IDatastoreObjectWithIntegerKey() {} @@ -201,6 +204,9 @@ namespace BlackMisc //! Invalid key static QString invalidDbKey() { return {}; } + //! The key index + static const CPropertyIndex &keyIndex() { static const CPropertyIndex k(IndexDbStringKey); return k; } + protected: //! Constructor IDatastoreObjectWithStringKey() {} diff --git a/src/blackmisc/propertyindexlist.cpp b/src/blackmisc/propertyindexlist.cpp index f550c98ce..44f059e7a 100644 --- a/src/blackmisc/propertyindexlist.cpp +++ b/src/blackmisc/propertyindexlist.cpp @@ -20,4 +20,12 @@ namespace BlackMisc CSequence(other) { } + CPropertyIndexList CPropertyIndexList::copyFrontRemoved() const + { + if (this->size() < 2) { return CPropertyIndexList(); } + CPropertyIndexList copy(*this); + copy.pop_front(); + return copy; + } + } // namespace diff --git a/src/blackmisc/propertyindexlist.h b/src/blackmisc/propertyindexlist.h index b44d87af9..701b87edb 100644 --- a/src/blackmisc/propertyindexlist.h +++ b/src/blackmisc/propertyindexlist.h @@ -35,6 +35,9 @@ namespace BlackMisc //! Construct from a base class object. CPropertyIndexList(const CSequence &other); + + //! List without front element, or empty list if not applicable + CPropertyIndexList copyFrontRemoved() const; }; } //namespace