diff --git a/src/blackgui/models/listmodelbase.cpp b/src/blackgui/models/listmodelbase.cpp index fc76308b1..43b5ef88d 100644 --- a/src/blackgui/models/listmodelbase.cpp +++ b/src/blackgui/models/listmodelbase.cpp @@ -132,9 +132,15 @@ namespace BlackGui return this->columnToPropertyIndex(index.column()); } + void CListModelBaseNonTemplate::sortByPropertyIndex(const CPropertyIndex &propertyIndex, Qt::SortOrder order) + { + const int column = this->propertyIndexToColumn(propertyIndex); + this->sort(column, order); + } + void CListModelBaseNonTemplate::setSortColumnByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex) { - this->m_sortedColumn = this->m_columns.propertyIndexToColumn(propertyIndex); + this->m_sortColumn = this->m_columns.propertyIndexToColumn(propertyIndex); } void CListModelBaseNonTemplate::setSorting(const CPropertyIndex &propertyIndex, Qt::SortOrder order) @@ -146,8 +152,8 @@ namespace BlackGui bool CListModelBaseNonTemplate::hasValidSortColumn() const { - if (!(this->m_sortedColumn >= 0 && this->m_sortedColumn < this->m_columns.size())) { return false; } - return this->m_columns.isSortable(this->m_sortedColumn); + if (!(this->m_sortColumn >= 0 && this->m_sortColumn < this->m_columns.size())) { return false; } + return this->m_columns.isSortable(this->m_sortColumn); } Qt::ItemFlags CListModelBaseNonTemplate::flags(const QModelIndex &index) const @@ -223,7 +229,7 @@ namespace BlackGui } CListModelBaseNonTemplate::CListModelBaseNonTemplate(const QString &translationContext, QObject *parent) - : QStandardItemModel(parent), m_columns(translationContext), m_sortedColumn(-1), m_sortOrder(Qt::AscendingOrder) + : QStandardItemModel(parent), m_columns(translationContext), m_sortColumn(-1), m_sortOrder(Qt::AscendingOrder) { // non unique default name, set translation context as default this->setObjectName(translationContext); @@ -339,7 +345,7 @@ namespace BlackGui const QModelIndex topLeft = index.sibling(index.row(), 0); const QModelIndex bottomRight = index.sibling(index.row(), this->columnCount() - 1); this->m_container[index.row()] = obj; - const CVariant co = CVariant::from(obj); + const CVariant co = CVariant::fromValue(obj); emit objectChanged(co, propertyIndex); emit this->dataChanged(topLeft, bottomRight); this->updateFilteredContainer(); @@ -417,7 +423,7 @@ namespace BlackGui worker->thenWithResult(this, [this](const ContainerType & sortedContainer) { if (this->m_modelDestroyed) { return; } - this->ps_updateContainer(CVariant::from(sortedContainer), false); + this->ps_updateContainer(CVariant::fromValue(sortedContainer), false); }); worker->then(this, &CListModelBase::asyncUpdateFinished); return worker; @@ -660,11 +666,11 @@ namespace BlackGui template void CListModelBase::sort(int column, Qt::SortOrder order) { - if (column == this->m_sortedColumn && order == this->m_sortOrder) { return; } + if (column == this->m_sortColumn && order == this->m_sortOrder) { return; } // new order - this->m_sortedColumn = column; - this->m_sortOrder = order; + this->m_sortColumn = column; + this->m_sortOrder = order; if (this->m_container.size() < 2) { return; // nothing to do diff --git a/src/blackgui/models/listmodelbase.h b/src/blackgui/models/listmodelbase.h index 583a0f67c..24d8a6782 100644 --- a/src/blackgui/models/listmodelbase.h +++ b/src/blackgui/models/listmodelbase.h @@ -77,7 +77,10 @@ namespace BlackGui virtual BlackMisc::CPropertyIndex modelIndexToPropertyIndex(const QModelIndex &index) const; //! Set sort column - virtual void setSortColumn(int column) { this->m_sortedColumn = column; } + virtual void setSortColumn(int column) { this->m_sortColumn = column; } + + //! Sort by index + void sortByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder); //! Set column for sorting //! \param propertyIndex index of column to be sorted @@ -87,7 +90,7 @@ namespace BlackGui virtual void setSorting(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder); //! Get sort column property index - virtual int getSortColumn() const { return this->m_sortedColumn; } + virtual int getSortColumn() const { return this->m_sortColumn; } //! Has valid sort column? virtual bool hasValidSortColumn() const; @@ -128,6 +131,7 @@ namespace BlackGui void asyncUpdateFinished(); //! Data changed + //! \remark passing back selected objects so they can be reselected void modelDataChanged(int count, bool withFilter); //! Data changed, digest version @@ -164,7 +168,7 @@ namespace BlackGui virtual int performUpdateContainer(const BlackMisc::CVariant &variant, bool sort) = 0; CColumns m_columns; //!< columns metadata - int m_sortedColumn; //!< currently sorted column + int m_sortColumn; //!< currently sorted column 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 @@ -303,7 +307,7 @@ namespace BlackGui //! Update filtered container void updateFilteredContainer(); - //! Row count changed + //! Model changed void emitModelDataChanged(); std::unique_ptr > m_filter; //!< Used filter diff --git a/src/blackgui/models/statusmessagelistmodel.cpp b/src/blackgui/models/statusmessagelistmodel.cpp index a4d9579c0..847bde440 100644 --- a/src/blackgui/models/statusmessagelistmodel.cpp +++ b/src/blackgui/models/statusmessagelistmodel.cpp @@ -49,7 +49,7 @@ namespace BlackGui this->m_columns.addColumn(CColumn::standardString("category", CStatusMessage::IndexCategoryHumanReadableOrTechnicalAsString)); this->m_columns.addColumn(CColumn::standardString("message", CStatusMessage::IndexMessage)); - this->m_sortedColumn = CStatusMessage::IndexUtcTimestamp; + this->m_sortColumn = CStatusMessage::IndexUtcTimestamp; this->m_sortOrder = Qt::DescendingOrder; } break; @@ -61,7 +61,7 @@ namespace BlackGui this->m_columns.addColumn(col); this->m_columns.addColumn(CColumn::standardString("message", CStatusMessage::IndexMessage)); - this->m_sortedColumn = CStatusMessage::IndexUtcTimestamp; + this->m_sortColumn = CStatusMessage::IndexUtcTimestamp; this->m_sortOrder = Qt::DescendingOrder; } break; diff --git a/src/blackgui/views/aircrafticaoview.h b/src/blackgui/views/aircrafticaoview.h index 1708f9dbc..5f0b411c9 100644 --- a/src/blackgui/views/aircrafticaoview.h +++ b/src/blackgui/views/aircrafticaoview.h @@ -39,6 +39,6 @@ namespace BlackGui //! Constructor explicit CAircraftIcaoCodeView(QWidget *parent = nullptr); }; - } -} + } // ns +} // ns #endif // guard diff --git a/src/blackgui/views/viewbase.cpp b/src/blackgui/views/viewbase.cpp index b6a6035c8..b23d9b266 100644 --- a/src/blackgui/views/viewbase.cpp +++ b/src/blackgui/views/viewbase.cpp @@ -97,11 +97,6 @@ namespace BlackGui CViewBaseNonTemplate::CViewBaseNonTemplate(QWidget *parent) : QTableView(parent) { - // this->viewport()->setAttribute(Qt::WA_Hover, true); - // this->viewport()->setMouseTracking(true); - // this->setStyle(new CViewBaseProxyStyle(this, this->style())); - // this->setItemDelegate(new CViewBaseItemDelegate(this)); - this->setContextMenuPolicy(Qt::CustomContextMenu); connect(this, &QWidget::customContextMenuRequested, this, &CViewBaseNonTemplate::ps_customMenuRequested); connect(this, &QTableView::clicked, this, &CViewBaseNonTemplate::ps_clicked); @@ -126,7 +121,7 @@ namespace BlackGui bool CViewBaseNonTemplate::setParentDockWidgetInfoArea(CDockWidgetInfoArea *parentDockableWidget) { - bool c = CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea(parentDockableWidget); + const bool c = CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea(parentDockableWidget); return c; } @@ -142,7 +137,7 @@ namespace BlackGui { disconnect(this->m_filterWidget); this->menuRemoveItems(MenuFilter); - if (m_filterWidget->parent() == this) { m_filterWidget->deleteLater(); } + if (this->m_filterWidget->parent() == this) { m_filterWidget->deleteLater(); } m_filterWidget = nullptr; } @@ -379,8 +374,8 @@ namespace BlackGui int CViewBaseNonTemplate::getHorizontalHeaderFontHeight() const { - QFontMetrics m(this->getHorizontalHeaderFont()); - int h = m.height(); + const QFontMetrics m(this->getHorizontalHeaderFont()); + const int h = m.height(); return h; } @@ -412,7 +407,7 @@ namespace BlackGui void CViewBaseNonTemplate::init() { - int fh = qRound(1.5 * this->getHorizontalHeaderFontHeight()); + const int fh = qRound(1.5 * this->getHorizontalHeaderFontHeight()); this->horizontalHeader()->setSectionResizeMode(QHeaderView::Interactive); // faster mode this->horizontalHeader()->setStretchLastSection(true); this->verticalHeader()->setDefaultSectionSize(fh); // for height @@ -738,7 +733,10 @@ namespace BlackGui this->fullResizeToContents(); } } + + const ContainerType selected(this->selectedObjects()); const int c = this->m_model->update(container, sort); + this->reselect(selected); // resize after real update according to mode if (presizeThresholdReached) @@ -792,11 +790,11 @@ namespace BlackGui if (container.size() > ASyncRowsCountThreshold && sort) { // larger container with sorting - updateContainerAsync(container, sort, resize); + this->updateContainerAsync(container, sort, resize); } else { - updateContainer(container, sort, resize); + this->updateContainer(container, sort, resize); } } @@ -866,6 +864,22 @@ namespace BlackGui return c; } + template + ObjectType CViewBase::firstSelectedOrDefaultObject() const + { + if (this->hasSelection()) + { + return this->selectedObjects().front(); + } + if (this->rowCount() < 2) + { + return this->containerOrFilteredContainer().frontOrDefault(); + } + + // too many, not selected + return ObjectType(); + } + template int CViewBase::updateSelected(const CPropertyIndexVariantMap &vm) { @@ -1045,6 +1059,30 @@ namespace BlackGui this->m_model->setSorting(propertyIndex, order); } + template + void CViewBase::sortByPropertyIndex(const CPropertyIndex &propertyIndex, Qt::SortOrder order, bool reselect) + { + if (!reselect) + { + this->m_model->sortByPropertyIndex(propertyIndex, order); + } + else + { + // hack: we reselect the already selected objects + // as sorting takes place (sync/async) in the model, and the model does not know about the selection + // we do this deferred as the model sort can be asynchronously + const ContainerType selected(this->selectedObjects()); + this->m_model->sortByPropertyIndex(propertyIndex, order); + if (!selected.isEmpty()) + { + QTimer::singleShot(2000, [ = ]() + { + this->reselect(selected); + }); + } + } + } + template QJsonObject CViewBase::toJson() const { @@ -1137,6 +1175,8 @@ namespace BlackGui Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed"); c = connect(this->m_model, &ModelClass::changed, this, &CViewBase::onModelChanged); Q_ASSERT_X(c, Q_FUNC_INFO, "Connect failed"); + + Q_UNUSED(c); } @@ -1199,6 +1239,12 @@ namespace BlackGui this->m_dropIndicator = indicator; } + template + void CViewBase::reselect(const ContainerType &selectedObjects) + { + Q_UNUSED(selectedObjects); + } + template CStatusMessage CViewBase::modifyLoadedJsonData(ContainerType &data) const { @@ -1277,6 +1323,13 @@ namespace BlackGui } } + template + void CViewBase::ps_selectedObjectsLoopback(const CVariant &selectedObjects) + { + const ContainerType selectedObjs = selectedObjects.value(); + this->reselect(selectedObjs); + } + template bool CViewBase::ps_filterDialogFinished(int status) { diff --git a/src/blackgui/views/viewbase.h b/src/blackgui/views/viewbase.h index 3ad41e619..1f78fa493 100644 --- a/src/blackgui/views/viewbase.h +++ b/src/blackgui/views/viewbase.h @@ -50,7 +50,6 @@ class QShowEvent; class QWidget; namespace BlackMisc { class CWorker; } - namespace BlackGui { class CDockWidgetInfoArea; @@ -147,6 +146,9 @@ namespace BlackGui //! \copydoc BlackGui::Models::CListModelBaseNonTemplate::setSorting virtual void setSorting(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder) = 0; + //! Sort by index + virtual void sortByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder, bool reselect = false) = 0; + //! Allow to drag and/or drop value objects virtual void allowDragDrop(bool allowDrag, bool allowDrop) = 0; @@ -400,6 +402,9 @@ namespace BlackGui //! Helper method with template free signature serving as callback from threaded worker int ps_updateContainer(const BlackMisc::CVariant &variant, bool sort, bool resize); + //! Helper method with template free signature to allow reselection of objects + virtual void ps_selectedObjectsLoopback(const BlackMisc::CVariant &selectedObjects) = 0; + //! Display the filter dialog void ps_displayFilterDialog(); @@ -516,6 +521,9 @@ namespace BlackGui //! Selected objects ContainerType selectedObjects() const; + //! First selected, the only one, or default + ObjectType firstSelectedOrDefaultObject() const; + //! Update selected objects int updateSelected(const BlackMisc::CVariant &variant, const BlackMisc::CPropertyIndex &index); @@ -564,6 +572,7 @@ namespace BlackGui virtual bool isDropAllowed() const override; virtual bool acceptDrop(const QMimeData *mimeData) const override; virtual void setSorting(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder) override; + virtual void sortByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder, bool reselect = false) override; //! @} //! Column count @@ -618,6 +627,10 @@ namespace BlackGui virtual void drawDropIndicator(bool indicator) override; //! @} + //! Reselect given objects + //! \remark override this function to select models again + virtual void reselect(const ContainerType &selectedObjects); + //! Modify JSON data loaded in BlackGui::Views::CViewBaseNonTemplate::ps_loadJson virtual BlackMisc::CStatusMessage modifyLoadedJsonData(ContainerType &data) const; @@ -640,6 +653,7 @@ namespace BlackGui virtual void ps_rowSelected(const QModelIndex &index) override; virtual BlackMisc::CStatusMessage ps_loadJson() override; virtual BlackMisc::CStatusMessage ps_saveJson() const override; + virtual void ps_selectedObjectsLoopback(const BlackMisc::CVariant &selectedObjects) override; //! @} }; } // namespace diff --git a/src/blackgui/views/viewdbobjects.cpp b/src/blackgui/views/viewdbobjects.cpp index eefb8bee6..b6a212721 100644 --- a/src/blackgui/views/viewdbobjects.cpp +++ b/src/blackgui/views/viewdbobjects.cpp @@ -47,7 +47,14 @@ namespace BlackGui } template - void CViewWithDbObjects::selectDbKeys(const QList &keys) + void CViewWithDbObjects::selectDbKey(const KeyType &key) + { + const QSet set({key}); + this->selectDbKeys(set); + } + + template + void CViewWithDbObjects::selectDbKeys(const QSet &keys) { if (keys.isEmpty()) { return; } this->clearSelection(); @@ -64,7 +71,15 @@ namespace BlackGui } template - int CViewWithDbObjects::removeDbKeys(const QList &keys) + QSet CViewWithDbObjects::selectedDbKeys() const + { + if (!this->hasSelection()) { return QSet(); } + const ContainerType selected(this->selectedObjects()); + return selected.toDbKeySet(); + } + + template + int CViewWithDbObjects::removeDbKeys(const QSet &keys) { if (keys.isEmpty()) { return 0; } if (this->isEmpty()) { return 0; } @@ -148,6 +163,15 @@ namespace BlackGui CViewWithDbObjects::customMenu(menuActions); } + template + void COrderableViewWithDbObjects::reselect(const ContainerType &selectedObjects) + { + if (!selectedObjects.isEmpty()) + { + this->selectDbKeys(selectedObjects.toDbKeySet()); + } + } + template void COrderableViewWithDbObjects::moveSelectedItems(int order) { diff --git a/src/blackgui/views/viewdbobjects.h b/src/blackgui/views/viewdbobjects.h index e2cb687ea..903e8b7cb 100644 --- a/src/blackgui/views/viewdbobjects.h +++ b/src/blackgui/views/viewdbobjects.h @@ -27,7 +27,7 @@ #include "blackmisc/simulation/distributor.h" #include "blackmisc/simulation/distributorlist.h" -#include +#include #include #include #include @@ -40,7 +40,6 @@ class QWidget; namespace BlackGui { namespace Menus { class CMenuActions; } - namespace Views { //! Base class for views with DB objects @@ -54,11 +53,17 @@ namespace BlackGui //! Get oldets object ObjectType oldestObject() const; + //! Select given DB key + void selectDbKey(const KeyType &key); + //! Select given DB keys - void selectDbKeys(const QList &keys); + void selectDbKeys(const QSet &keys); + + //! Get selected DB keys + QSet selectedDbKeys() const; //! Remove keys - int removeDbKeys(const QList &keys); + int removeDbKeys(const QSet &keys); //! Update or insert data (based on DB key) int replaceOrAddObjectsByKey(const ContainerType &container); @@ -82,6 +87,9 @@ namespace BlackGui //! \copydoc BlackGui::Views::CViewBaseNonTemplate::customMenu virtual void customMenu(BlackGui::Menus::CMenuActions &menuActions) override; + //! Reselect by DB keys + virtual void reselect(const ContainerType &selectedObjects) override; + //! Move selected items void moveSelectedItems(int order);