From 7d43af343e65c946618b9615c77f30d5e63f9c54 Mon Sep 17 00:00:00 2001 From: Klaus Basan Date: Sat, 23 Apr 2016 02:42:36 +0200 Subject: [PATCH] refs #641, support for IOrderable in specialized views/models * menu to order objects per drag and drop * changed model/views to specialized model/views --- .../models/aircraftmodellistmodel.cpp | 11 +- src/blackgui/models/aircraftmodellistmodel.h | 14 ++- src/blackgui/models/columns.cpp | 5 + src/blackgui/models/columns.h | 3 + src/blackgui/models/distributorlistmodel.cpp | 4 +- src/blackgui/models/distributorlistmodel.h | 6 +- src/blackgui/models/listmodelbase.cpp | 64 ++++++++++- src/blackgui/models/listmodelbase.h | 97 ++++++++-------- src/blackgui/models/listmodeldbobjects.cpp | 33 ++++++ src/blackgui/models/listmodeldbobjects.h | 19 ++++ src/blackgui/views/aircraftmodelview.cpp | 14 ++- src/blackgui/views/aircraftmodelview.h | 4 +- src/blackgui/views/distributorview.cpp | 2 +- src/blackgui/views/distributorview.h | 2 +- src/blackgui/views/viewbase.cpp | 105 +++++++++++------- src/blackgui/views/viewbase.h | 96 ++++++++-------- src/blackgui/views/viewdbobjects.cpp | 86 +++++++++++++- src/blackgui/views/viewdbobjects.h | 31 ++++++ 18 files changed, 435 insertions(+), 161 deletions(-) diff --git a/src/blackgui/models/aircraftmodellistmodel.cpp b/src/blackgui/models/aircraftmodellistmodel.cpp index fb6f2e93c..4e5ed4035 100644 --- a/src/blackgui/models/aircraftmodellistmodel.cpp +++ b/src/blackgui/models/aircraftmodellistmodel.cpp @@ -20,7 +20,7 @@ namespace BlackGui namespace Models { CAircraftModelListModel::CAircraftModelListModel(AircraftModelMode mode, QObject *parent) : - CListModelDbObjects("CAircraftModelListModel", parent) + COrderableListModelDbObjects("CAircraftModelListModel", parent) { this->setAircraftModelMode(mode); @@ -60,16 +60,23 @@ namespace BlackGui this->m_sortOrder = Qt::AscendingOrder; break; + case OwnModelSet: + // intentional fall thru + this->m_columns.addColumn(CColumn::orderColumn()); + case OwnSimulatorModelMapping: this->m_columns.addColumn(CColumn::standardString("model", CAircraftModel::IndexModelString)); this->m_columns.addColumn(CColumn("DB", "DB metadata", CAircraftModel::IndexDatabaseIcon, new CPixmapFormatter())); this->m_columns.addColumn(CColumn("mode", "model mode(include, exclude)", CAircraftModel::IndexModelModeAsIcon, new CPixmapFormatter())); // this->m_columns.addColumn(CColumn::standardValueObject("call", "callsign", CAircraftModel::IndexCallsign)); this->m_columns.addColumn(CColumn::standardString("dist.", "distributor", { CAircraftModel::IndexDistributor, CDistributor::IndexDbStringKey})); + if (mode == OwnModelSet) + { + this->m_columns.addColumn(CColumn::standardString("d#", "distributor order", { CAircraftModel::IndexDistributor, CDistributor::IndexOrderString})); + } this->m_columns.addColumn(CColumn::standardString("ac", "aircraft ICAO", { CAircraftModel::IndexAircraftIcaoCode, CAircraftIcaoCode::IndexAircraftDesignator})); this->m_columns.addColumn(CColumn::standardString("fam.", "aircraft family", { CAircraftModel::IndexAircraftIcaoCode, CAircraftIcaoCode::IndexFamily})); this->m_columns.addColumn(CColumn::standardString("al", "airline ICAO", { CAircraftModel::IndexLivery, CLivery::IndexAirlineIcaoCode, CAirlineIcaoCode::IndexAirlineDesignator})); - // this->m_columns.addColumn(CColumn::standardString("ct", "combined type", { CAircraftModel::IndexIcao, CAircraftIcaoData::IndexCombinedAircraftType})); this->m_columns.addColumn(CColumn::standardString("description", CAircraftModel::IndexDescription)); this->m_columns.addColumn(CColumn::standardString("filename", CAircraftModel::IndexFileName)); this->m_columns.addColumn(CColumn::standardString("icon", CAircraftModel::IndexIconPath)); diff --git a/src/blackgui/models/aircraftmodellistmodel.h b/src/blackgui/models/aircraftmodellistmodel.h index c644d6886..99582500d 100644 --- a/src/blackgui/models/aircraftmodellistmodel.h +++ b/src/blackgui/models/aircraftmodellistmodel.h @@ -25,7 +25,7 @@ namespace BlackGui { //! Aircraft model list model class BLACKGUI_EXPORT CAircraftModelListModel : - public CListModelDbObjects + public COrderableListModelDbObjects { public: //! How to display @@ -34,6 +34,7 @@ namespace BlackGui { NotSet, OwnSimulatorModel, //!< models existing for my simulator + OwnModelSet, //!< own model set OwnSimulatorModelMapping, //!< models of my simulator, but in mapping mode Database, //!< Database entry VPilotRuleModel, //!< vPilot rule turned into model @@ -73,11 +74,14 @@ namespace BlackGui //! \copydoc QAbstractItemModel::data virtual QVariant data(const QModelIndex &index, int role) const override; + //! \copydoc BlackGui::Models::CListModelBaseNonTemplate::isOrderable + virtual bool isOrderable() const override { return true; } + private: - AircraftModelMode m_mode = NotSet; //!< current mode - bool m_highlightModelStrings = false; //!< highlight in in model strings - QStringList m_highlightStrings; //!< model strings to highlight - QBrush m_highlightColor{Qt::yellow}; //!< how to highlight + AircraftModelMode m_mode = NotSet; //!< current mode + bool m_highlightModelStrings = false; //!< highlight in in model strings + QStringList m_highlightStrings; //!< model strings to highlight + QBrush m_highlightColor{Qt::yellow}; //!< how to highlight }; } // ns } // ns diff --git a/src/blackgui/models/columns.cpp b/src/blackgui/models/columns.cpp index 0eb640e24..7fdf0f759 100644 --- a/src/blackgui/models/columns.cpp +++ b/src/blackgui/models/columns.cpp @@ -88,6 +88,11 @@ namespace BlackGui return CColumn(headerName, toolTip, propertyIndex, new CStringFormatter(alignment)); } + CColumn CColumn::orderColumn(const CPropertyIndex &propertyIndex, int alignment) + { + return CColumn("#", "order", propertyIndex, new CStringFormatter(alignment)); + } + // --------------- columns ---------------------------------------------- CColumns::CColumns(const QString &translationContext, QObject *parent) : diff --git a/src/blackgui/models/columns.h b/src/blackgui/models/columns.h index 9fc3e705a..4241efdce 100644 --- a/src/blackgui/models/columns.h +++ b/src/blackgui/models/columns.h @@ -100,6 +100,9 @@ namespace BlackGui //! Get a standard string object formatted column static CColumn standardString(const QString &headerName, const QString &toolTip, const BlackMisc::CPropertyIndex &propertyIndex, int alignment = CDefaultFormatter::alignDefault()); + //! Get a standard string object formatted column + static CColumn orderColumn(const BlackMisc::CPropertyIndex &propertyIndex = BlackMisc::CPropertyIndex::GlobalIndexIOrderable, int alignment = CDefaultFormatter::alignRightVCenter()); + private: QString m_translationContext; QString m_columnName; diff --git a/src/blackgui/models/distributorlistmodel.cpp b/src/blackgui/models/distributorlistmodel.cpp index c1110a1c9..bdb7c2ac2 100644 --- a/src/blackgui/models/distributorlistmodel.cpp +++ b/src/blackgui/models/distributorlistmodel.cpp @@ -18,7 +18,7 @@ namespace BlackGui namespace Models { CDistributorListModel::CDistributorListModel(QObject *parent) : - CListModelDbObjects("ModelDistributorList", parent) + COrderableListModelDbObjects("ModelDistributorList", parent) { this->setDistributorMode(Normal); @@ -36,6 +36,8 @@ namespace BlackGui this->m_columns.clear(); switch (distributorMode) { + case NormalWithOrder: + this->m_columns.addColumn(CColumn::orderColumn()); case NotSet: case Normal: { diff --git a/src/blackgui/models/distributorlistmodel.h b/src/blackgui/models/distributorlistmodel.h index 3bc93c8a3..d9477770c 100644 --- a/src/blackgui/models/distributorlistmodel.h +++ b/src/blackgui/models/distributorlistmodel.h @@ -23,7 +23,7 @@ namespace BlackGui { //! Distributor list model class BLACKGUI_EXPORT CDistributorListModel : - public CListModelDbObjects + public COrderableListModelDbObjects { public: //! What kind of stations @@ -31,6 +31,7 @@ namespace BlackGui { NotSet, Normal, + NormalWithOrder, Minimal }; @@ -46,6 +47,9 @@ namespace BlackGui //! Mode DistributorMode getDistributorMode() const { return this->m_distributorMode; } + //! \copydoc BlackGui::Models::CListModelBaseNonTemplate::isOrderable + virtual bool isOrderable() const override { return true; } + private: DistributorMode m_distributorMode = NotSet; }; diff --git a/src/blackgui/models/listmodelbase.cpp b/src/blackgui/models/listmodelbase.cpp index e81de60e2..7cfa054fc 100644 --- a/src/blackgui/models/listmodelbase.cpp +++ b/src/blackgui/models/listmodelbase.cpp @@ -96,6 +96,12 @@ namespace BlackGui this->m_sortedColumn = this->m_columns.propertyIndexToColumn(propertyIndex); } + void CListModelBaseNonTemplate::setSorting(const CPropertyIndex &propertyIndex, Qt::SortOrder order) + { + this->setSortColumnByPropertyIndex(propertyIndex); + this->m_sortOrder = order; + } + bool CListModelBaseNonTemplate::hasValidSortColumn() const { @@ -107,7 +113,7 @@ namespace BlackGui { Qt::ItemFlags f = QStandardItemModel::flags(index); if (!index.isValid()) { return f; } - bool editable = this->m_columns.isEditable(index); + const bool editable = this->m_columns.isEditable(index); f = editable ? (f | Qt::ItemIsEditable) : (f & ~Qt::ItemIsEditable); // flags from formatter @@ -127,12 +133,12 @@ namespace BlackGui Qt::DropActions CListModelBaseNonTemplate::supportedDragActions() const { - return Qt::CopyAction; + return isOrderable() ? Qt::CopyAction | Qt::MoveAction : Qt::CopyAction; } Qt::DropActions CListModelBaseNonTemplate::supportedDropActions() const { - return QStandardItemModel::supportedDropActions(); + return this->m_dropActions; } QStringList CListModelBaseNonTemplate::mimeTypes() const @@ -161,8 +167,8 @@ namespace BlackGui if (columns < 1) { return; } if (startRowIndex < 0) { startRowIndex = 0; } if (endRowIndex >= rows) { endRowIndex = rows - 1; } - QModelIndex topLeft(createIndex(startRowIndex, 0)); - QModelIndex bottomRight(createIndex(endRowIndex, columns - 1)); + const QModelIndex topLeft(createIndex(startRowIndex, 0)); + const QModelIndex bottomRight(createIndex(endRowIndex, columns - 1)); emit dataChanged(topLeft, bottomRight); } @@ -190,6 +196,38 @@ namespace BlackGui return this->containerOrFilteredContainer().size(); } + template + bool CListModelBase::canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const + { + Q_UNUSED(action); + Q_UNUSED(row); + Q_UNUSED(column); + Q_UNUSED(parent); + if (!this->isDropAllowed()) { return false; } + if (!this->acceptDrop(data)) { return false; } + return true; + } + + template + bool CListModelBase::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) + { + Q_UNUSED(row); + Q_UNUSED(column); + if (!this->isOrderable() || !this->acceptDrop(data)) { return false; } + const CVariant valueVariant(this->toCVariant(data)); + if (valueVariant.isValid()) + { + if (action == Qt::MoveAction) + { + const ContainerType container(valueVariant.value()); + if (container.isEmpty()) { return false; } + const int position = parent.row(); + this->moveItems(container, position); + } + } + return true; + } + template bool CListModelBase::isValidIndex(const QModelIndex &index) const { @@ -370,7 +408,7 @@ namespace BlackGui { if (!filter) { - this->removeFilter(); // clear filter + this->removeFilter(); // clear filter return; } if (filter->isValid()) @@ -528,6 +566,14 @@ namespace BlackGui emit this->changed(); } + template + void CListModelBase::moveItems(const ContainerType &items, int position) + { + // overridden in specialized class + Q_UNUSED(items); + Q_UNUSED(position); + } + template void CListModelBase::sort() { @@ -630,6 +676,12 @@ namespace BlackGui return container().toJsonString(format); } + template + bool CListModelBase::isOrderable() const + { + return false; + } + // see here for the reason of thess forward instantiations // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html template class CListModelBase; diff --git a/src/blackgui/models/listmodelbase.h b/src/blackgui/models/listmodelbase.h index aeed4cbd3..ec9ba8499 100644 --- a/src/blackgui/models/listmodelbase.h +++ b/src/blackgui/models/listmodelbase.h @@ -15,6 +15,7 @@ #include "blackgui/blackguiexport.h" #include "blackgui/models/columns.h" #include "blackgui/models/modelfilter.h" +#include "blackgui/dropbase.h" #include "blackmisc/worker.h" #include #include @@ -27,7 +28,9 @@ namespace BlackGui namespace Models { //! Non templated base class, allows Q_OBJECT and signals to be used - class BLACKGUI_EXPORT CListModelBaseNonTemplate : public QStandardItemModel + class BLACKGUI_EXPORT CListModelBaseNonTemplate : + public QStandardItemModel, + public BlackGui::CDropBase { Q_OBJECT @@ -38,17 +41,17 @@ namespace BlackGui //! Destructor virtual ~CListModelBaseNonTemplate() {} - //! \copydoc QStandardItemModel::columnCount() + //! \name Functions from QStandardItemModel + //! @{ virtual int columnCount(const QModelIndex &modelIndex = QModelIndex()) const override; - - //! \copydoc QStandardItemModel::headerData() virtual QVariant headerData(int section, Qt::Orientation orientation, int role) const override; - - //! \copydoc QStandardItemModel::headerData() virtual QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const override; - - //! \copydoc QStandardItemModel::parent() virtual QModelIndex parent(const QModelIndex &child) const override; + virtual Qt::ItemFlags flags(const QModelIndex &index) const override; + virtual Qt::DropActions supportedDragActions() const override; + virtual Qt::DropActions supportedDropActions() const override; + virtual QStringList mimeTypes() const override; + //! @} //! Column to property index virtual BlackMisc::CPropertyIndex columnToPropertyIndex(int column) const; @@ -66,6 +69,9 @@ namespace BlackGui //! \param propertyIndex index of column to be sorted virtual void setSortColumnByPropertyIndex(const BlackMisc::CPropertyIndex &propertyIndex); + //! Sorting + 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; } @@ -78,17 +84,8 @@ namespace BlackGui //! Translation context virtual const QString &getTranslationContext() const; - //! \copydoc QStandardItemModel::flags - virtual Qt::ItemFlags flags(const QModelIndex &index) const override; - - //! \copydoc QStandardItemModel::supportedDragActions - virtual Qt::DropActions supportedDragActions() const override; - - //! \copydoc QStandardItemModel::supportedDropActions - virtual Qt::DropActions supportedDropActions() const override; - - //! \copydoc QStandardItemModel::mimeTypes - virtual QStringList mimeTypes() const override; + //! Orderable, normally use a container BlackMisc::IOrderableList + virtual bool isOrderable() const = 0; //! Mark as about to be destroyed, normally marked from view void markDestroyed(); @@ -96,6 +93,9 @@ namespace BlackGui //! Model about to be destroyed? bool isModelDestroyed(); + //! Drop actions + void setDropActions(Qt::DropActions dropActions) { this->m_dropActions = dropActions; } + //! Send signal that data have been changed. //! \note Meant for scenarios where the container is directly updated and a subsequent signal is required void sendDataChanged(int startRowIndex, int endRowIndex); @@ -127,17 +127,18 @@ namespace BlackGui protected: //! Constructor - //! \param translationContext I18N context + //! \param translationContext I18N context //! \param parent CListModelBaseNonTemplate(const QString &translationContext, QObject *parent = nullptr); //! Helper method with template free signature virtual int performUpdateContainer(const BlackMisc::CVariant &variant, bool sort) = 0; - CColumns m_columns; //!< columns metadata - int m_sortedColumn; //!< current sort column - Qt::SortOrder m_sortOrder; //!< sort order (asc/desc) - bool m_modelDestroyed = false; //!< model is about to be destroyed + CColumns m_columns; //!< columns metadata + int m_sortedColumn; //!< current sort column + Qt::SortOrder m_sortOrder; //!< sort order (asc/desc) + bool m_modelDestroyed = false; //!< model is about to be destroyed + Qt::DropActions m_dropActions = Qt::IgnoreAction; //!< drop actions }; //! List model @@ -147,6 +148,24 @@ namespace BlackGui //! Destructor virtual ~CListModelBase() {} + //! \name Functions from QStandardItemModel + //! @{ + virtual QVariant data(const QModelIndex &index, int role) const override; + virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; + virtual QMimeData *mimeData(const QModelIndexList &indexes) const override; + virtual void sort(int column, Qt::SortOrder order) override; + virtual int rowCount(const QModelIndex &parentIndex = QModelIndex()) const override; + virtual bool canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const override; + virtual bool dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) override; + //! @} + + //! \name Functions from CListModelBaseNonTemplate + //! @{ + virtual QJsonObject toJson() const override; + virtual QString toJsonString(QJsonDocument::JsonFormat format = QJsonDocument::Indented) const override; + virtual bool isOrderable() const override; + //! @} + //! Valid index (in range) virtual bool isValidIndex(const QModelIndex &index) const; @@ -156,20 +175,10 @@ namespace BlackGui //! Full container or cached filtered container as approproiate const ContainerType &containerOrFilteredContainer() const; - //! \copydoc QStandardItemModel::data() - virtual QVariant data(const QModelIndex &index, int role) const override; - - //! \copydoc QStandardItemModel::setData() - //! \sa CListModelBaseNonTemplate::flags - virtual bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) override; - //! Simple set of data in container, using class is responsible for firing signals etc. //! \sa sendDataChanged bool setInContainer(const QModelIndex &index, const ObjectType &obj); - //! \copydoc QStandardItemModel::rowCount() - virtual int rowCount(const QModelIndex &parentIndex = QModelIndex()) const override; - //! Update by new container //! \return int size after update //! \remarks a sorting is performed only if a valid sort column is set @@ -187,15 +196,15 @@ namespace BlackGui //! Update single element virtual void update(int rowIndex, const ObjectType &object); + //! Move items to position + virtual void moveItems(const ContainerType &items, int position); + //! Object at row position virtual const ObjectType &at(const QModelIndex &index) const; //! Sort by given sort order \sa getSortColumn() \sa getSortOrder() void sort(); - //! \copydoc QStandardItemModel::sort() - virtual void sort(int column, Qt::SortOrder order) override; - //! Truncate to given number void truncate(int maxNumber, bool forceSort = false); @@ -235,15 +244,6 @@ namespace BlackGui //! Empty? virtual bool isEmpty() const; - //! \copydoc QStandardItemModel::mimeData - virtual QMimeData *mimeData(const QModelIndexList &indexes) const override; - - //! \copydoc BlackGui::Models::CListModelBaseNonTemplate::toJson - virtual QJsonObject toJson() const override; - - //! \copydoc BlackGui::Models::CListModelBaseNonTemplate::toJsonString - virtual QString toJsonString(QJsonDocument::JsonFormat format = QJsonDocument::Indented) const override; - //! Filter available bool hasFilter() const; @@ -286,9 +286,8 @@ namespace BlackGui template bool compareForModelSort(const ObjectType &a, const ObjectType &b, Qt::SortOrder order, const BlackMisc::CPropertyIndex &index, std::false_type) { - Q_UNUSED(index); - BlackMisc::CVariant aQv = a.propertyByIndex(index); - BlackMisc::CVariant bQv = b.propertyByIndex(index); + const BlackMisc::CVariant aQv = a.propertyByIndex(index); + const BlackMisc::CVariant bQv = b.propertyByIndex(index); return (order == Qt::AscendingOrder) ? (aQv < bQv) : (bQv < aQv); } } // namespace diff --git a/src/blackgui/models/listmodeldbobjects.cpp b/src/blackgui/models/listmodeldbobjects.cpp index 2b48a4021..db24b23a6 100644 --- a/src/blackgui/models/listmodeldbobjects.cpp +++ b/src/blackgui/models/listmodeldbobjects.cpp @@ -57,6 +57,37 @@ namespace BlackGui return m_highlightKeys.contains(dbKeyForIndex(index)); } + template + COrderableListModelDbObjects::COrderableListModelDbObjects(const QString &translationContext, QObject *parent) + : CListModelDbObjects(translationContext, parent) + { } + + template + void COrderableListModelDbObjects::moveItems(const ContainerType &items, int position) + { + if (items.isEmpty()) { return; } + ContainerType container(this->container()); + int order = 0; + if (position >= 0 && position < container.size()) + { + order = container[position].getOrder(); + } + container.moveTo(items, order); + this->updateContainerMaybeAsync(container); + } + + template + int COrderableListModelDbObjects::update(const ContainerType &container, bool sort) + { + if (container.needsOrder()) + { + ContainerType orderable(container); + orderable.resetOrder(); + return CListModelDbObjects::update(orderable, sort); + } + return CListModelDbObjects::update(container, sort); + } + // see here for the reason of thess forward instantiations // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html template class CListModelDbObjects; @@ -65,6 +96,8 @@ namespace BlackGui template class CListModelDbObjects; template class CListModelDbObjects; template class CListModelDbObjects; + template class COrderableListModelDbObjects; + template class COrderableListModelDbObjects; } // namespace } // namespace diff --git a/src/blackgui/models/listmodeldbobjects.h b/src/blackgui/models/listmodeldbobjects.h index ef7c3997d..5f1619dba 100644 --- a/src/blackgui/models/listmodeldbobjects.h +++ b/src/blackgui/models/listmodeldbobjects.h @@ -58,6 +58,25 @@ namespace BlackGui QColor m_highlightColor = Qt::green; }; + + //! List model for DB objects + template class COrderableListModelDbObjects : + public CListModelDbObjects + { + public: + //! Destructor + virtual ~COrderableListModelDbObjects() {} + + //! \name specialized BlackGui::Models::CListModelDbObjects functions for ordering + //! @{ + virtual int update(const ContainerType &container, bool sort) override; + virtual void moveItems(const ContainerType &items, int position) override; + //! @} + + protected: + //! Constructor + COrderableListModelDbObjects(const QString &translationContext, QObject *parent = nullptr); + }; } // namespace } // namespace #endif // guard diff --git a/src/blackgui/views/aircraftmodelview.cpp b/src/blackgui/views/aircraftmodelview.cpp index dcd852524..642f951b2 100644 --- a/src/blackgui/views/aircraftmodelview.cpp +++ b/src/blackgui/views/aircraftmodelview.cpp @@ -32,7 +32,7 @@ namespace BlackGui { namespace Views { - CAircraftModelView::CAircraftModelView(QWidget *parent) : CViewWithDbObjects(parent) + CAircraftModelView::CAircraftModelView(QWidget *parent) : COrderableViewWithDbObjects(parent) { // default this->standardInit(new CAircraftModelListModel(CAircraftModelListModel::OwnSimulatorModel, this)); @@ -105,9 +105,10 @@ namespace BlackGui return m_menus.testFlag(MenuCanStashModels) && hasSelection(); } - void CAircraftModelView::setImplementedMetaTypeIds() + void CAircraftModelView::setAcceptedMetaTypeIds() { - this->setAcceptedMetaTypeIds( + Q_ASSERT(this->m_model); + this->m_model->setAcceptedMetaTypeIds( { qMetaTypeId(), qMetaTypeId(), qMetaTypeId(), qMetaTypeId(), @@ -176,7 +177,8 @@ namespace BlackGui void CAircraftModelView::dropEvent(QDropEvent *event) { - if (!isDropAllowed()) { return; } + + if (!this->isDropAllowed()) { return; } if (!event) { return; } const QMimeData *mime = event->mimeData(); if (!mime) { return; } @@ -290,7 +292,7 @@ namespace BlackGui menu.addMenu(stashMenu); stashMenu->setIcon(CIcons::appDbStash16()); } - CViewWithDbObjects::customMenu(menu); + COrderableViewWithDbObjects::customMenu(menu); } CStatusMessage CAircraftModelView::modifyLoadedJsonData(CAircraftModelList &models) const @@ -338,7 +340,7 @@ namespace BlackGui if (sim.isSingleSimulator()) { return ok; } return CStatusMessage(this, CStatusMessage::SeverityError, "data need to be from one simulator"); } - return CViewWithDbObjects::validateLoadedJsonData(models); + return COrderableViewWithDbObjects::validateLoadedJsonData(models); } void CAircraftModelView::jsonLoadedAndModelUpdated(const CAircraftModelList &models) diff --git a/src/blackgui/views/aircraftmodelview.h b/src/blackgui/views/aircraftmodelview.h index cfe56f379..4ec59736b 100644 --- a/src/blackgui/views/aircraftmodelview.h +++ b/src/blackgui/views/aircraftmodelview.h @@ -23,7 +23,7 @@ namespace BlackGui { //! Aircraft view class BLACKGUI_EXPORT CAircraftModelView : - public CViewWithDbObjects + public COrderableViewWithDbObjects { Q_OBJECT @@ -59,7 +59,7 @@ namespace BlackGui bool hasSelectedModelsToStash() const; //! Add the technically supported metatypes allowed for drag and drop - void setImplementedMetaTypeIds(); + void setAcceptedMetaTypeIds(); //! Add my own filter dialog void addFilterDialog(); diff --git a/src/blackgui/views/distributorview.cpp b/src/blackgui/views/distributorview.cpp index 232e594ab..ecda4ede7 100644 --- a/src/blackgui/views/distributorview.cpp +++ b/src/blackgui/views/distributorview.cpp @@ -17,7 +17,7 @@ namespace BlackGui namespace Views { CDistributorView::CDistributorView(QWidget *parent) : - CViewWithDbObjects(parent) + COrderableViewWithDbObjects(parent) { this->standardInit(new CDistributorListModel(this)); this->setMenu(MenuDefaultDbViews); diff --git a/src/blackgui/views/distributorview.h b/src/blackgui/views/distributorview.h index 8d8a5b775..e3f0e1bb6 100644 --- a/src/blackgui/views/distributorview.h +++ b/src/blackgui/views/distributorview.h @@ -22,7 +22,7 @@ namespace BlackGui { //! Distributors class BLACKGUI_EXPORT CDistributorView : - public CViewWithDbObjects + public COrderableViewWithDbObjects { public: //! Constructor diff --git a/src/blackgui/views/viewbase.cpp b/src/blackgui/views/viewbase.cpp index bcecf97a1..4f5025077 100644 --- a/src/blackgui/views/viewbase.cpp +++ b/src/blackgui/views/viewbase.cpp @@ -94,7 +94,6 @@ namespace BlackGui } } - void CViewBaseNonTemplate::setFilterDialog(CFilterDialog *filterDialog) { this->setFilterWidgetImpl(filterDialog); @@ -303,25 +302,6 @@ namespace BlackGui QTableView::showEvent(event); } - void CViewBaseNonTemplate::allowDragDropValueObjects(bool allowDrag, bool allowDrop) - { - // see model for implementing logic of drag - this->setAcceptDrops(allowDrop); - this->setDragEnabled(allowDrag); - this->setDropIndicatorShown(allowDrop); - CDropBase::allowDrop(allowDrop); - } - - void CViewBaseNonTemplate::allowDrop(bool allow) - { - this->allowDragDropValueObjects(this->dragEnabled(), allow); - } - - bool CViewBaseNonTemplate::isDropAllowed() const - { - return this->acceptDrops(); - } - int CViewBaseNonTemplate::getHorizontalHeaderFontHeight() const { QFontMetrics m(this->getHorizontalHeaderFont()); @@ -518,10 +498,10 @@ namespace BlackGui this->setVisible(false); // magic trick from: // http://stackoverflow.com/q/3433664/356726 - const QRect vporig = this->viewport()->geometry(); - QRect vpnew = vporig; - vpnew.setWidth(std::numeric_limits::max()); - this->viewport()->setGeometry(vpnew); + const QRect vpOriginal = this->viewport()->geometry(); + QRect vpNew = vpOriginal; + vpNew.setWidth(std::numeric_limits::max()); + this->viewport()->setGeometry(vpNew); this->m_resizeCount++; this->resizeColumnsToContents(); // columns @@ -531,16 +511,10 @@ namespace BlackGui // re-stretch this->horizontalHeader()->setStretchLastSection(true); } - - this->viewport()->setGeometry(vporig); + this->viewport()->setGeometry(vpOriginal); this->setVisible(true); } - void CViewBaseNonTemplate::presizeOrFullResizeToContents() - { - - } - void CViewBaseNonTemplate::ps_customMenuRequested(QPoint pos) { QMenu menu; @@ -601,14 +575,14 @@ namespace BlackGui void CViewBaseNonTemplate::dragEnterEvent(QDragEnterEvent *event) { - if (!event || !acceptDrop(event->mimeData())) { return; } + if (!event || !this->acceptDrop(event->mimeData())) { return; } setBackgroundRole(QPalette::Highlight); event->acceptProposedAction(); } void CViewBaseNonTemplate::dragMoveEvent(QDragMoveEvent *event) { - if (!event || !acceptDrop(event->mimeData())) { return; } + if (!event || !this->acceptDrop(event->mimeData())) { return; } event->acceptProposedAction(); } @@ -618,11 +592,6 @@ namespace BlackGui event->accept(); } - void CViewBaseNonTemplate::dropEvent(QDropEvent *event) - { - Q_UNUSED(event); - } - template CViewBase::CViewBase(QWidget *parent, ModelClass *model) : CViewBaseNonTemplate(parent), m_model(model) { @@ -646,7 +615,7 @@ namespace BlackGui // we have data this->showLoadIndicator(container.size()); - bool reallyResize = resize && isResizeConditionMet(container.size()); // do we really perform resizing + const bool reallyResize = resize && isResizeConditionMet(container.size()); // do we really perform resizing bool presize = (m_resizeMode == PresizeSubset) && this->isEmpty() && // only when no data yet !reallyResize; // not when we resize later @@ -894,6 +863,47 @@ namespace BlackGui return this->m_model->rowCount() < 1; } + template + bool CViewBase::isOrderable() const + { + Q_ASSERT(this->m_model); + return this->m_model->isOrderable(); + } + + template + void CViewBase::allowDragDrop(bool allowDrag, bool allowDrop) + { + Q_ASSERT(this->m_model); + + // see model for implementing logic of drag + this->viewport()->setAcceptDrops(allowDrop); + this->setDragEnabled(allowDrag); + this->setDropIndicatorShown(allowDrop); + this->m_model->allowDrop(allowDrop); + } + + template + bool CViewBase::isDropAllowed() const + { + Q_ASSERT(this->m_model); + return this->m_model->isDropAllowed(); + } + + template + bool CViewBase::acceptDrop(const QMimeData *mimeData) const + { + Q_ASSERT(this->m_model); + const bool a = this->m_model->acceptDrop(mimeData); + return a; + } + + template + void CViewBase::setSorting(const CPropertyIndex &propertyIndex, Qt::SortOrder order) + { + Q_ASSERT(this->m_model); + this->m_model->setSorting(propertyIndex, order); + } + template QJsonObject CViewBase::toJson() const { @@ -936,6 +946,23 @@ namespace BlackGui return derivedModel()->hasFilter(); } + template + void CViewBase::addContainerTypesAsDropTypes(bool objectType, bool containerType) + { + if (objectType) { this->m_model->addAcceptedMetaTypeId(qMetaTypeId()); } + if (containerType) { this->m_model->addAcceptedMetaTypeId(qMetaTypeId()); } + } + + template + void CViewBase::initAsOrderable() + { + Q_ASSERT_X(isOrderable(), Q_FUNC_INFO, "Model not orderable"); + this->allowDragDrop(true, true); + this->setDragDropMode(InternalMove); + this->setDropActions(Qt::MoveAction); + this->addContainerTypesAsDropTypes(true, true); + } + template void CViewBase::setSortIndicator() { diff --git a/src/blackgui/views/viewbase.h b/src/blackgui/views/viewbase.h index 95a578988..6cba4c013 100644 --- a/src/blackgui/views/viewbase.h +++ b/src/blackgui/views/viewbase.h @@ -18,7 +18,6 @@ #include "blackgui/models/modelfilter.h" #include "blackgui/menus/menudelegate.h" #include "blackgui/loadindicator.h" -#include "blackgui/dropbase.h" #include "blackgui/blackguiexport.h" #include "blackmisc/icons.h" #include "blackmisc/worker.h" @@ -39,8 +38,7 @@ namespace BlackGui //! Non templated base class, allows Q_OBJECT and signals / slots to be used class BLACKGUI_EXPORT CViewBaseNonTemplate : public QTableView, - public BlackGui::Components::CEnableForDockWidgetInfoArea, - public BlackGui::CDropBase + public BlackGui::Components::CEnableForDockWidgetInfoArea { Q_OBJECT @@ -79,6 +77,7 @@ namespace BlackGui MenuSave = 1 << 6, //!< save as JSON MenuLoad = 1 << 7, //!< load from JSON MenuToggleSelectionMode = 1 << 8, //!< allow to toggle selection mode + MenuOrderable = 1 << 9, //!< items can be ordered (if container is BlackMisc::IOrderableList MenuStandard = MenuClear | MenuRemoveSelectedRows | MenuRefresh | MenuBackend | MenuDisplayAutomatically | MenuFilter | MenuSave | MenuLoad | MenuToggleSelectionMode, MenuLoadAndSave = MenuLoad | MenuSave, @@ -87,9 +86,9 @@ namespace BlackGui MenuDefaultDbViews = MenuToggleSelectionMode | MenuBackend, // special menus, should be in derived classes, but enums cannot be inherited // maybe shifted in the future to elsewhere - MenuHighlightDbData = 1 << 9, //!< highlight DB data - MenuHighlightStashed = 1 << 10, //!< highlight stashed models - MenuCanStashModels = 1 << 11, //!< stash models + MenuHighlightDbData = 1 << 10, //!< highlight DB data + MenuHighlightStashed = 1 << 11, //!< highlight stashed models + MenuCanStashModels = 1 << 12, //!< stash models MenuStashing = MenuHighlightStashed | MenuCanStashModels, }; Q_DECLARE_FLAGS(Menu, MenuFlag) @@ -106,21 +105,27 @@ namespace BlackGui //! Empty? virtual bool isEmpty() const = 0 ; + //! Elements in container + virtual int rowCount() const = 0; + + //! Is the corresponding model orderable, BlackMisc::Models::CListModelBaseNonTemplate::isOrderable + virtual bool isOrderable() const = 0; + + //! \copydoc BlackGui::Models::CListModelBaseNonTemplate::setSorting + virtual void setSorting(const BlackMisc::CPropertyIndex &propertyIndex, Qt::SortOrder order = Qt::AscendingOrder) = 0; + //! Allow to drag and/or drop value objects - virtual void allowDragDropValueObjects(bool allowDrag, bool allowDrop); + virtual void allowDragDrop(bool allowDrag, bool allowDrop) = 0; - //! \copydoc CDropBase::allowDrop - virtual void allowDrop(bool allow) override; + //! Drop allowed? + virtual bool isDropAllowed() const = 0; - //! \copydoc CDropBase::isDropAllowed - virtual bool isDropAllowed() const override; + //! Accept drop data? + virtual bool acceptDrop(const QMimeData *mimeData) const = 0; //! \copydoc Components::CEnableForDockWidgetInfoArea::setParentDockWidgetInfoArea virtual bool setParentDockWidgetInfoArea(BlackGui::CDockWidgetInfoArea *parentDockableWidget) override; - //! Elements in container - virtual int rowCount() const = 0; - //! Resize mode ResizeMode getResizeMode() const { return m_resizeMode; } @@ -278,23 +283,14 @@ namespace BlackGui //! \sa BlackGui::Views::CViewBaseNonTemplate::ps_customMenuRequested virtual void customMenu(QMenu &menu) const; - //! \copydoc QTableView::paintEvent + //! \name Functions from QTableView + //! @{ virtual void paintEvent(QPaintEvent *event) override; - - //! \copydoc QTableView::showEvent virtual void showEvent(QShowEvent *event) override; - - //! \copydoc QTableView::dragEnterEvent virtual void dragEnterEvent(QDragEnterEvent *event) override; - - //! \copydoc QTableView::dragMoveEvent virtual void dragMoveEvent(QDragMoveEvent *event) override; - - //! \copydoc QTableView::dragLeaveEvent virtual void dragLeaveEvent(QDragLeaveEvent *event) override; - - //! \copydoc QTableView::dropEvent - virtual void dropEvent(QDropEvent *event) override; + //! @} //! Perform resizing / non slot method for template virtual void performModeBasedResizeToContent() = 0; @@ -341,7 +337,7 @@ namespace BlackGui QWidget *m_filterWidget = nullptr; //!< filter widget or dialog Menu m_menus = MenuDefault; //!< Default menu settings BlackGui::Menus::IMenuDelegate *m_menu = nullptr; //!< custom menu if any - BlackGui::CLoadIndicator *m_loadIndicator = nullptr; //!< load indicator if neeeded + BlackGui::CLoadIndicator *m_loadIndicator = nullptr; //!< load indicator if needed protected slots: //! Helper method with template free signature serving as callback from threaded worker @@ -428,9 +424,6 @@ namespace BlackGui //! Model const ModelClass *derivedModel() const { return this->m_model; } - //! \copydoc BlackGui::Views::CViewBaseNonTemplate::clear - virtual void clear() override { Q_ASSERT(this->m_model); this->m_model->clear(); } - //! Update whole container //! \return int size after update int updateContainer(const ContainerType &container, bool sort = true, bool resize = true); @@ -459,12 +452,6 @@ namespace BlackGui //! Selected objects ContainerType selectedObjects() const; - //! \name Slot overrides from base class - //! @{ - virtual int removeSelectedRows() override; - virtual void presizeOrFullResizeToContents() override; - //! @} - //! Update selected objects int updateSelected(const BlackMisc::CVariant &variant, const BlackMisc::CPropertyIndex &index); @@ -495,15 +482,27 @@ namespace BlackGui this->updateContainerMaybeAsync(copy); } - //! \copydoc BlackGui::Views::CViewBaseNonTemplate::rowCount + //! \name Slot overrides from base class + //! @{ + virtual int removeSelectedRows() override; + virtual void presizeOrFullResizeToContents() override; + //! @} + + //! \name BlackGui::Views::CViewBaseNonTemplate implementations + //! @{ + virtual void clear() override { Q_ASSERT(this->m_model); this->m_model->clear(); } virtual int rowCount() const override; + virtual bool isEmpty() const override; + virtual bool isOrderable() const override; + virtual void allowDragDrop(bool allowDrag, bool allowDrop) override; + 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; + //! @} //! Column count int columnCount() const; - //! \copydoc BlackGui::Views::CViewBaseNonTemplate::isEmpty - virtual bool isEmpty() const override; - //! Convert to JSON QJsonObject toJson() const; @@ -522,6 +521,15 @@ namespace BlackGui //! Has filter set? bool hasFilter() const; + //! Add the object and container type as accepted drop types CDropBase::addAcceptedMetaTypeId + void addContainerTypesAsDropTypes(bool objectType = true, bool containerType = true); + + //! Init so items can be ordered + void initAsOrderable(); + + //! Drop actions + void setDropActions(Qt::DropActions dropActions) { Q_ASSERT(this->m_model); this->m_model->setDropActions(dropActions); } + protected: ModelClass *m_model = nullptr; //!< corresponding model @@ -534,14 +542,12 @@ namespace BlackGui //! Standard initialization void standardInit(ModelClass *model = nullptr); - //! \copydoc BlackGui::Views::CViewBaseNonTemplate::reachedResizeThreshold + //! \name base class implementations + //! @{ virtual bool reachedResizeThreshold(int containrerSize = -1) const override; - - //! \copydoc BlackGui::Views::CViewBaseNonTemplate::performModeBasedResizeToContent virtual void performModeBasedResizeToContent() override; - - //! \copydoc BlackGui::Views::CViewBaseNonTemplate::performUpdateContainer virtual int performUpdateContainer(const BlackMisc::CVariant &variant, bool sort, bool resize) override; + //! @} //! Modify JSON data loaded in BlackGui::Views::CViewBaseNonTemplate::ps_loadJson virtual BlackMisc::CStatusMessage modifyLoadedJsonData(ContainerType &data) const; diff --git a/src/blackgui/views/viewdbobjects.cpp b/src/blackgui/views/viewdbobjects.cpp index 8d9f17ee2..200d5874b 100644 --- a/src/blackgui/views/viewdbobjects.cpp +++ b/src/blackgui/views/viewdbobjects.cpp @@ -10,6 +10,9 @@ #include "viewdbobjects.h" #include "blackgui/models/allmodels.h" #include +#include +#include +#include using namespace BlackMisc; using namespace BlackGui; @@ -84,7 +87,7 @@ namespace BlackGui template void CViewWithDbObjects::customMenu(QMenu &menu) const { - if (this->m_menus.testFlag(CViewBase::MenuHighlightDbData)) + if (this->m_menus.testFlag(CViewBaseNonTemplate::MenuHighlightDbData)) { QAction *a = menu.addAction(CIcons::database16(), "Highlight DB data", this, &CViewWithDbObjects::ps_toggleHighlightDbData); a->setCheckable(true); @@ -100,14 +103,91 @@ namespace BlackGui this->derivedModel()->setHighlightDbData(!h); } + template + COrderableViewWithDbObjects::COrderableViewWithDbObjects(QWidget *parent) : + CViewWithDbObjects::CViewWithDbObjects(parent) + { + // void + } + + template + void COrderableViewWithDbObjects::customMenu(QMenu &menu) const + { + if (this->m_menus.testFlag(CViewBaseNonTemplate::MenuOrderable) && this->hasSelection()) + { + const int maxOrder = this->rowCount() - 1; + QMenu *menuOrder = menu.addMenu(CIcons::arrowMediumEast16(), "Order"); + + QLineEdit *leOrder = new QLineEdit(&menu); + leOrder->setPlaceholderText("New order 0-" + QString::number(maxOrder)); + const QIntValidator *v = new QIntValidator(0, maxOrder, leOrder); + leOrder->setValidator(v); + QWidgetAction *orderAction = new QWidgetAction(&menu); + orderAction->setDefaultWidget(leOrder); + menuOrder->addAction(orderAction); + QObject::connect(leOrder, &QLineEdit::returnPressed, this, &COrderableViewWithDbObjects::ps_orderToLineEdit); + + menuOrder->addAction(CIcons::arrowMediumNorth16(), "To top", this, &COrderableViewWithDbObjects::ps_orderToTop); + menuOrder->addAction(CIcons::arrowMediumSouth16(), "To bottom", this, &COrderableViewWithDbObjects::ps_orderToBottom); + menuOrder->addAction(CIcons::arrowMediumWest16(), "Freeze current order", this, &COrderableViewWithDbObjects::ps_freezeCurrentOrder); + menu.addSeparator(); + } + CViewWithDbObjects::customMenu(menu); + } + + template + void COrderableViewWithDbObjects::moveSelectedItems(int order) + { + if (this->isEmpty()) { return; } + const ContainerType objs(this->selectedObjects()); + if (objs.isEmpty()) { return; } + this->m_model->moveItems(objs, order); + } + + template + void COrderableViewWithDbObjects::ps_orderToTop() + { + this->moveSelectedItems(0); + } + + template + void COrderableViewWithDbObjects::ps_orderToBottom() + { + int c = this->model()->rowCount() - 1; + if (c >= 0) + { + this->moveSelectedItems(c); + } + } + + template + void COrderableViewWithDbObjects::ps_orderToLineEdit() + { + if (this->isEmpty()) { return; } + QLineEdit *le = qobject_cast(QObject::sender()); + if (!le || le->text().isEmpty()) { return; } + const int order = le->text().toInt(); + this->moveSelectedItems(order); + } + + template + void COrderableViewWithDbObjects::ps_freezeCurrentOrder() + { + ContainerType objects = this->container(); + objects.freezeOrder(); + this->updateContainerAsync(objects, false); + } + // see here for the reason of thess forward instantiations // http://www.parashift.com/c++-faq/separate-template-class-defn-from-decl.html template class CViewWithDbObjects; - template class CViewWithDbObjects; template class CViewWithDbObjects; template class CViewWithDbObjects; - template class CViewWithDbObjects; template class CViewWithDbObjects; + template class CViewWithDbObjects; + template class CViewWithDbObjects; + template class COrderableViewWithDbObjects; + template class COrderableViewWithDbObjects; } // namespace } // namespace diff --git a/src/blackgui/views/viewdbobjects.h b/src/blackgui/views/viewdbobjects.h index 52db07a88..20aad7fea 100644 --- a/src/blackgui/views/viewdbobjects.h +++ b/src/blackgui/views/viewdbobjects.h @@ -51,6 +51,37 @@ namespace BlackGui //! \copydoc BlackGui::Views::CViewBase::ps_toggleHighlightDbData virtual void ps_toggleHighlightDbData() override; }; + + //! Base class for views with DB objects + template class COrderableViewWithDbObjects : + public CViewWithDbObjects + { + protected: + //! Constructor + explicit COrderableViewWithDbObjects(QWidget *parent = nullptr); + + //! \copydoc BlackGui::Views::CViewBaseNonTemplate::customMenu + virtual void customMenu(QMenu &menu) const override; + + //! Move selected items + void moveSelectedItems(int order); + + protected slots: + //! Order to top + void ps_orderToTop(); + + //! Order to bottom + void ps_orderToBottom(); + + //! Order to line edit + void ps_orderToLineEdit(); + + //! Current order set as order + void ps_freezeCurrentOrder(); + + private: + QList m_menuActions; + }; } // namespace } // namespace #endif // guard