From 780ea3a41f25577e46e4f27a5c0b25cdd12192a2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 14 Nov 2013 11:39:14 +0100 Subject: [PATCH] added support for record reordering to model (only implemented in info collection) --- apps/opencs/model/world/collection.hpp | 50 +++++++++++ apps/opencs/model/world/collectionbase.hpp | 7 ++ apps/opencs/model/world/commands.cpp | 22 +++++ apps/opencs/model/world/commands.hpp | 16 ++++ apps/opencs/model/world/idtable.cpp | 24 ++++-- apps/opencs/model/world/idtable.hpp | 21 ++++- apps/opencs/model/world/infocollection.cpp | 16 ++++ apps/opencs/model/world/infocollection.hpp | 6 ++ apps/opencs/model/world/refidcollection.cpp | 5 ++ apps/opencs/model/world/refidcollection.hpp | 6 ++ apps/opencs/view/world/table.cpp | 96 +++++++++++++++++++++ apps/opencs/view/world/table.hpp | 6 ++ 12 files changed, 267 insertions(+), 8 deletions(-) diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index 316e76c5f1..3f410e3971 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -57,6 +57,12 @@ namespace CSMWorld const std::vector >& getRecords() const; + bool reorderRowsImp (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + public: Collection(); @@ -128,6 +134,12 @@ namespace CSMWorld /// If the index is invalid either generally (by being out of range) or for the particular /// record, an exception is thrown. + virtual bool reorderRows (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + void addColumn (Column *column); void setRecord (int index, const Record& record); @@ -146,6 +158,38 @@ namespace CSMWorld return mRecords; } + template + bool Collection::reorderRowsImp (int baseIndex, + const std::vector& newOrder) + { + if (!newOrder.empty()) + { + int size = static_cast (newOrder.size()); + + // check that all indices are present + std::vector test (newOrder); + std::sort (test.begin(), test.end()); + if (*test.begin()!=0 || *--test.end()!=size-1) + return false; + + // reorder records + std::vector > buffer (size); + + for (int i=0; i::iterator iter (mIndex.begin()); iter!=mIndex.end(); + ++iter) + if (iter->second>=baseIndex && iter->secondsecond = newOrder.at (iter->second-baseIndex)+baseIndex; + } + + return true; + } + template Collection::Collection() {} @@ -389,6 +433,12 @@ namespace CSMWorld mRecords.at (index) = record; } + + template + bool Collection::reorderRows (int baseIndex, const std::vector& newOrder) + { + return false; + } } #endif diff --git a/apps/opencs/model/world/collectionbase.hpp b/apps/opencs/model/world/collectionbase.hpp index 2d1b5318c7..ab7a9ebecb 100644 --- a/apps/opencs/model/world/collectionbase.hpp +++ b/apps/opencs/model/world/collectionbase.hpp @@ -2,6 +2,7 @@ #define CSM_WOLRD_COLLECTIONBASE_H #include +#include #include "universalid.hpp" #include "columns.hpp" @@ -86,6 +87,12 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + virtual bool reorderRows (int baseIndex, const std::vector& newOrder) = 0; + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + int searchColumnIndex (Columns::ColumnId id) const; ///< Return index of column with the given \a id. If no such column exists, -1 is returned. diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index f6f421c6ae..21feb14be6 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -122,4 +122,26 @@ void CSMWorld::DeleteCommand::redo() void CSMWorld::DeleteCommand::undo() { mModel.setRecord (mId, *mOld); +} + + +CSMWorld::ReorderRowsCommand::ReorderRowsCommand (IdTable& model, int baseIndex, + const std::vector& newOrder) +: mModel (model), mBaseIndex (baseIndex), mNewOrder (newOrder) +{} + +void CSMWorld::ReorderRowsCommand::redo() +{ + mModel.reorderRows (mBaseIndex, mNewOrder); +} + +void CSMWorld::ReorderRowsCommand::undo() +{ + int size = static_cast (mNewOrder.size()); + std::vector reverse (size); + + for (int i=0; i #include +#include #include #include @@ -99,6 +100,21 @@ namespace CSMWorld virtual void undo(); }; + + class ReorderRowsCommand : public QUndoCommand + { + IdTable& mModel; + int mBaseIndex; + std::vector mNewOrder; + + public: + + ReorderRowsCommand (IdTable& model, int baseIndex, const std::vector& newOrder); + + virtual void redo(); + + virtual void undo(); + }; } #endif \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index f5cd14efc4..809d64339c 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -4,15 +4,12 @@ #include "collectionbase.hpp" #include "columnbase.hpp" -CSMWorld::IdTable::IdTable (CollectionBase *idCollection) : mIdCollection (idCollection) -{ - -} +CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering) +: mIdCollection (idCollection), mReordering (reordering) +{} CSMWorld::IdTable::~IdTable() -{ - -} +{} int CSMWorld::IdTable::rowCount (const QModelIndex & parent) const { @@ -167,4 +164,17 @@ int CSMWorld::IdTable::searchColumnIndex (Columns::ColumnId id) const int CSMWorld::IdTable::findColumnIndex (Columns::ColumnId id) const { return mIdCollection->findColumnIndex (id); +} + +void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector& newOrder) +{ + if (!newOrder.empty()) + if (mIdCollection->reorderRows (baseIndex, newOrder)) + emit dataChanged (index (baseIndex, 0), + index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1)); +} + +CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const +{ + return mReordering; } \ No newline at end of file diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 00c5772366..e4ae58fd04 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -1,6 +1,8 @@ #ifndef CSM_WOLRD_IDTABLE_H #define CSM_WOLRD_IDTABLE_H +#include + #include #include "universalid.hpp" @@ -15,7 +17,18 @@ namespace CSMWorld { Q_OBJECT + public: + + enum Reordering + { + Reordering_None, + Reordering_WithinTopic + }; + + private: + CollectionBase *mIdCollection; + Reordering mReordering; // not implemented IdTable (const IdTable&); @@ -23,7 +36,7 @@ namespace CSMWorld public: - IdTable (CollectionBase *idCollection); + IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_WithinTopic); ///< The ownership of \a idCollection is not transferred. virtual ~IdTable(); @@ -63,6 +76,12 @@ namespace CSMWorld int findColumnIndex (Columns::ColumnId id) const; ///< Return index of column with the given \a id. If no such column exists, an exception is /// thrown. + + void reorderRows (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + + Reordering getReordering() const; }; } diff --git a/apps/opencs/model/world/infocollection.cpp b/apps/opencs/model/world/infocollection.cpp index b5c1872a11..87bb925c2e 100644 --- a/apps/opencs/model/world/infocollection.cpp +++ b/apps/opencs/model/world/infocollection.cpp @@ -88,6 +88,22 @@ int CSMWorld::InfoCollection::getAppendIndex (const std::string& id, UniversalId return std::distance (getRecords().begin(), range.second); } +bool CSMWorld::InfoCollection::reorderRows (int baseIndex, const std::vector& newOrder) +{ + // check if the range is valid + int lastIndex = baseIndex + newOrder.size() -1; + + if (lastIndex>=getSize()) + return false; + + // Check that topics match + if (getRecord (baseIndex).get().mTopicId!=getRecord (lastIndex).get().mTopicId) + return false; + + // reorder + return reorderRowsImp (baseIndex, newOrder); +} + void CSMWorld::InfoCollection::load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue) { std::string id = Misc::StringUtils::lowerCase (dialogue.mId) + "#" + diff --git a/apps/opencs/model/world/infocollection.hpp b/apps/opencs/model/world/infocollection.hpp index 1bccbb4aee..ae61f5d391 100644 --- a/apps/opencs/model/world/infocollection.hpp +++ b/apps/opencs/model/world/infocollection.hpp @@ -33,6 +33,12 @@ namespace CSMWorld UniversalId::Type type = UniversalId::Type_None) const; ///< \param type Will be ignored, unless the collection supports multiple record types + virtual bool reorderRows (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + void load (ESM::ESMReader& reader, bool base, const ESM::Dialogue& dialogue); Range getTopicRange (const std::string& topic) const; diff --git a/apps/opencs/model/world/refidcollection.cpp b/apps/opencs/model/world/refidcollection.cpp index 0218642e0e..86a542c5c3 100644 --- a/apps/opencs/model/world/refidcollection.cpp +++ b/apps/opencs/model/world/refidcollection.cpp @@ -540,6 +540,11 @@ std::vector CSMWorld::RefIdCollection::getIds (bool listDeleted) co return mData.getIds (listDeleted); } +bool CSMWorld::RefIdCollection::reorderRows (int baseIndex, const std::vector& newOrder) +{ + return false; +} + void CSMWorld::RefIdCollection::save (int index, ESM::ESMWriter& writer) const { mData.save (index, writer); diff --git a/apps/opencs/model/world/refidcollection.hpp b/apps/opencs/model/world/refidcollection.hpp index 0788b60237..5ff4a70bf2 100644 --- a/apps/opencs/model/world/refidcollection.hpp +++ b/apps/opencs/model/world/refidcollection.hpp @@ -100,6 +100,12 @@ namespace CSMWorld /// /// \param listDeleted include deleted record in the list + virtual bool reorderRows (int baseIndex, const std::vector& newOrder); + ///< Reorder the rows [baseIndex, baseIndex+newOrder.size()) according to the indices + /// given in \a newOrder (baseIndex+newOrder[0] specifies the new index of row baseIndex). + /// + /// \return Success? + void save (int index, ESM::ESMWriter& writer) const; }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index dfdfa6d13e..dade9c8ea3 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -12,6 +12,7 @@ #include "../../model/world/idtableproxymodel.hpp" #include "../../model/world/idtable.hpp" #include "../../model/world/record.hpp" +#include "../../model/world/columns.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" @@ -37,6 +38,35 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (listDeletableSelectedIds().size()>0) menu.addAction (mDeleteAction); + + if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) + { + /// \todo allow reordering of multiple rows + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + int column = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Topic); + + if (column==-1) + column = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Journal); + + if (column!=-1) + { + if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row-1, column))) + { + menu.addAction (mMoveUpAction); + } + + if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== + mProxyModel->data (mProxyModel->index (row+1, column))) + { + menu.addAction (mMoveDownAction); + } + } + } + } } menu.exec (event->globalPos()); @@ -176,6 +206,14 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, CSMWorld::Data& data, Q connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); addAction (mDeleteAction); + mMoveUpAction = new QAction (tr ("Move Up"), this); + connect (mMoveUpAction, SIGNAL (triggered()), this, SLOT (moveUpRecord())); + addAction (mMoveUpAction); + + mMoveDownAction = new QAction (tr ("Move Down"), this); + connect (mMoveDownAction, SIGNAL (triggered()), this, SLOT (moveDownRecord())); + addAction (mMoveDownAction); + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (tableSizeUpdate())); @@ -254,6 +292,64 @@ void CSVWorld::Table::editRecord() } } +void CSVWorld::Table::moveUpRecord() +{ + QModelIndexList selectedRows = selectionModel()->selectedRows(); + + if (selectedRows.size()==1) + { + int row2 =selectedRows.begin()->row(); + + if (row2>0) + { + int row = row2-1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; iselectedRows(); + + if (selectedRows.size()==1) + { + int row =selectedRows.begin()->row(); + + if (rowrowCount()-1) + { + int row2 = row+1; + + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + row2 = mProxyModel->mapToSource (mProxyModel->index (row2, 0)).row(); + + if (row2<=row) + throw std::runtime_error ("Inconsistent row order"); + + std::vector newOrder (row2-row+1); + newOrder[0] = row2-row; + newOrder[row2-row] = 0; + for (int i=1; icolumnCount(); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 3e42144248..889e2847ac 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -34,6 +34,8 @@ namespace CSVWorld QAction *mCreateAction; QAction *mRevertAction; QAction *mDeleteAction; + QAction *mMoveUpAction; + QAction *mMoveDownAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; bool mEditLock; @@ -80,6 +82,10 @@ namespace CSVWorld void editRecord(); + void moveUpRecord(); + + void moveDownRecord(); + public slots: void tableSizeUpdate();