From a28a2bc2fe256ef5626701eab7c205a1ea720c9e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 5 Jun 2014 10:28:10 +0200 Subject: [PATCH 01/11] simplified feature management for record tables --- apps/opencs/model/world/data.cpp | 10 +++--- apps/opencs/model/world/idtable.cpp | 23 ++++---------- apps/opencs/model/world/idtable.hpp | 36 ++++++++++------------ apps/opencs/view/world/dialoguesubview.cpp | 7 +++-- apps/opencs/view/world/table.cpp | 8 ++--- 5 files changed, 35 insertions(+), 49 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 442e73e51..f448f5bfa 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -248,12 +248,12 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding) addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); - addModel (new IdTable (&mTopicInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); - addModel (new IdTable (&mJournalInfos, IdTable::Reordering_WithinTopic), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); - addModel (new IdTable (&mCells, IdTable::Reordering_None, IdTable::Viewing_Id), UniversalId::Type_Cells, UniversalId::Type_Cell); - addModel (new IdTable (&mReferenceables, IdTable::Reordering_None, IdTable::Viewing_None, true), + addModel (new IdTable (&mTopicInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); + addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); + addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cells, UniversalId::Type_Cell); + addModel (new IdTable (&mReferenceables, IdTable::Feature_Preview), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); - addModel (new IdTable (&mRefs, IdTable::Reordering_None, IdTable::Viewing_Cell, true), UniversalId::Type_References, UniversalId::Type_Reference, false); + addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_References, UniversalId::Type_Reference, false); addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false); } diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 50998c36f..a481ce143 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -4,9 +4,8 @@ #include "collectionbase.hpp" #include "columnbase.hpp" -CSMWorld::IdTable::IdTable (CollectionBase *idCollection, Reordering reordering, - Viewing viewing, bool preview) -: mIdCollection (idCollection), mReordering (reordering), mViewing (viewing), mPreview (preview) +CSMWorld::IdTable::IdTable (CollectionBase *idCollection, unsigned int features) +: mIdCollection (idCollection), mFeatures (features) {} CSMWorld::IdTable::~IdTable() @@ -186,19 +185,9 @@ void CSMWorld::IdTable::reorderRows (int baseIndex, const std::vector& newO index (baseIndex+newOrder.size()-1, mIdCollection->getColumns()-1)); } -CSMWorld::IdTable::Reordering CSMWorld::IdTable::getReordering() const +unsigned int CSMWorld::IdTable::getFeatures() const { - return mReordering; -} - -CSMWorld::IdTable::Viewing CSMWorld::IdTable::getViewing() const -{ - return mViewing; -} - -bool CSMWorld::IdTable::hasPreview() const -{ - return mPreview; + return mFeatures; } std::pair CSMWorld::IdTable::view (int row) const @@ -206,7 +195,7 @@ std::pair CSMWorld::IdTable::view (int row) std::string id; std::string hint; - if (mViewing==Viewing_Cell) + if (mFeatures & Feature_ViewCell) { int cellColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Cell); int idColumn = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); @@ -217,7 +206,7 @@ std::pair CSMWorld::IdTable::view (int row) hint = "r:" + std::string (mIdCollection->getData (row, idColumn).toString().toUtf8().constData()); } } - else if (mViewing==Viewing_Id) + else if (mFeatures & Feature_ViewId) { int column = mIdCollection->searchColumnIndex (Columns::ColumnId_Id); diff --git a/apps/opencs/model/world/idtable.hpp b/apps/opencs/model/world/idtable.hpp index 8b5462825..5500d40b8 100644 --- a/apps/opencs/model/world/idtable.hpp +++ b/apps/opencs/model/world/idtable.hpp @@ -19,26 +19,27 @@ namespace CSMWorld public: - enum Reordering + enum Features { - Reordering_None, - Reordering_WithinTopic - }; + Feature_ReorderWithinTopic = 1, - enum Viewing - { - Viewing_None, - Viewing_Id, // use ID column to generate view request (ID is transformed into - // worldspace and original ID is passed as hint with c: prefix) - Viewing_Cell // use cell column to generate view request (cell ID is transformed - // into worldspace and record ID is passed as hint with r: prefix) + /// Use ID column to generate view request (ID is transformed into + /// worldspace and original ID is passed as hint with c: prefix). + Feature_ViewId = 2, + + /// Use cell column to generate view request (cell ID is transformed + /// into worldspace and record ID is passed as hint with r: prefix). + Feature_ViewCell = 4, + + Feature_View = Feature_ViewId | Feature_ViewCell, + + Feature_Preview = 8 }; private: CollectionBase *mIdCollection; - Reordering mReordering; - Viewing mViewing; + unsigned int mFeatures; bool mPreview; // not implemented @@ -47,8 +48,7 @@ namespace CSMWorld public: - IdTable (CollectionBase *idCollection, Reordering reordering = Reordering_None, - Viewing viewing = Viewing_None, bool preview = false); + IdTable (CollectionBase *idCollection, unsigned int features = 0); ///< The ownership of \a idCollection is not transferred. virtual ~IdTable(); @@ -97,11 +97,7 @@ namespace CSMWorld ///< 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; - - Viewing getViewing() const; - - bool hasPreview() const; + unsigned int getFeatures() const; std::pair view (int row) const; ///< Return the UniversalId and the hint for viewing \a row. If viewing is not diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index d03bf3f80..6b57f2685 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -440,7 +440,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM QToolButton* revertButton = new QToolButton(mainWidget); revertButton->setIcon(QIcon(":/edit-undo.png")); - if (mTable->hasPreview()) + if (mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview) { QToolButton* previewButton = new QToolButton(mainWidget); previewButton->setIcon(QIcon(":/edit-preview.png")); @@ -448,7 +448,7 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM connect(previewButton, SIGNAL(clicked()), this, SLOT(showPreview())); } - if (mTable->getViewing()!=CSMWorld::IdTable::Viewing_None) + if (mTable->getFeatures() & CSMWorld::IdTable::Feature_View) { QToolButton* viewButton = new QToolButton(mainWidget); viewButton->setIcon(QIcon(":/cell.png")); @@ -671,7 +671,8 @@ void CSVWorld::DialogueSubView::cloneRequest () void CSVWorld::DialogueSubView::showPreview () { - if (mTable->hasPreview() && mRow < mTable->rowCount()) + if ((mTable->getFeatures() & CSMWorld::IdTable::Feature_Preview) && + mRow < mTable->rowCount()) { emit focusId(CSMWorld::UniversalId(CSMWorld::UniversalId::Type_Preview, mTable->data(mTable->index (mRow, 0)).toString().toUtf8().constData()), ""); } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 3d4b02c9c..912cd2183 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -46,14 +46,14 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) /// \todo Reverting temporarily disabled on tables that support reordering, because /// revert logic currently can not handle reordering. - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_None) + if (!(mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic)) if (listRevertableSelectedIds().size()>0) menu.addAction (mRevertAction); if (listDeletableSelectedIds().size()>0) menu.addAction (mDeleteAction); - if (mModel->getReordering()==CSMWorld::IdTable::Reordering_WithinTopic) + if (mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic) { /// \todo allow reordering of multiple rows if (selectedRows.size()==1) @@ -85,7 +85,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (selectedRows.size()==1) { - if (mModel->getViewing()!=CSMWorld::IdTable::Viewing_None) + if (mModel->getFeatures() & CSMWorld::IdTable::Feature_View) { int row = selectedRows.begin()->row(); @@ -101,7 +101,7 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) menu.addAction (mViewAction); } - if (mModel->hasPreview()) + if (mModel->getFeatures() & CSMWorld::IdTable::Feature_Preview) menu.addAction (mPreviewAction); } From 83d5f2aaebebac558a45f68fdf71b8646cfc56f0 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Jun 2014 12:43:21 +0200 Subject: [PATCH 02/11] factored out command creation from table view into a separate class --- apps/opencs/CMakeLists.txt | 2 +- apps/opencs/model/world/commanddispatcher.cpp | 157 ++++++++++++++++++ apps/opencs/model/world/commanddispatcher.hpp | 53 ++++++ apps/opencs/view/world/table.cpp | 148 +++-------------- apps/opencs/view/world/table.hpp | 10 +- 5 files changed, 235 insertions(+), 135 deletions(-) create mode 100644 apps/opencs/model/world/commanddispatcher.cpp create mode 100644 apps/opencs/model/world/commanddispatcher.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index f18ac0bca..d9cac95f3 100644 --- a/apps/opencs/CMakeLists.txt +++ b/apps/opencs/CMakeLists.txt @@ -18,7 +18,7 @@ opencs_hdrs_noqt (model/doc opencs_units (model/world - idtable idtableproxymodel regionmap data + idtable idtableproxymodel regionmap data commanddispatcher ) diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp new file mode 100644 index 000000000..1e2715744 --- /dev/null +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -0,0 +1,157 @@ + +#include "commanddispatcher.hpp" + +#include "../doc/document.hpp" + +#include "idtable.hpp" +#include "record.hpp" +#include "commands.hpp" + +std::vector CSMWorld::CommandDispatcher::getDeletableRecords() const +{ + std::vector result; + + IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); + + for (std::vector::const_iterator iter (mSelection.begin()); + iter!=mSelection.end(); ++iter) + { + // check record state + RecordBase::State state = + static_cast (model.data (model.index (*iter, 1)).toInt()); + + if (state==RecordBase::State_Deleted) + continue; + + // check other columns (only relevant for a subset of the tables) + int dialogueTypeIndex = model.searchColumnIndex (Columns::ColumnId_DialogueType); + + if (dialogueTypeIndex!=-1) + { + int type = model.data (model.index (*iter, dialogueTypeIndex)).toInt(); + + if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) + continue; + } + + result.push_back (*iter); + } + + return result; +} + +std::vector CSMWorld::CommandDispatcher::getRevertableRecords() const +{ + std::vector result; + + IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); + + /// \todo Reverting temporarily disabled on tables that support reordering, because + /// revert logic currently can not handle reordering. + if (model.getFeatures() & IdTable::Feature_ReorderWithinTopic) + return result; + + for (std::vector::const_iterator iter (mSelection.begin()); + iter!=mSelection.end(); ++iter) + { + // check record state + RecordBase::State state = + static_cast (model.data (model.index (*iter, 1)).toInt()); + + if (state==RecordBase::State_BaseOnly) + continue; + + result.push_back (*iter); + } + + return result; +} + +CSMWorld::CommandDispatcher::CommandDispatcher (CSMDoc::Document& document, + const CSMWorld::UniversalId& id, QObject *parent) +: QObject (parent), mDocument (document), mId (id), mLocked (false) +{} + +void CSMWorld::CommandDispatcher::setEditLock (bool locked) +{ + mLocked = locked; +} + +void CSMWorld::CommandDispatcher::setSelection (const std::vector& selection) +{ + mSelection = selection; +} + +bool CSMWorld::CommandDispatcher::canDelete() const +{ + if (mLocked) + return false; + + return getDeletableRecords().size()!=0; +} + +bool CSMWorld::CommandDispatcher::canRevert() const +{ + if (mLocked) + return false; + + return getRevertableRecords().size()!=0; +} + +void CSMWorld::CommandDispatcher::executeDelete() +{ + if (mLocked) + return; + + std::vector rows = getDeletableRecords(); + + if (rows.empty()) + return; + + IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); + + int columnIndex = model.findColumnIndex (Columns::ColumnId_Id); + + if (rows.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Delete multiple records")); + + for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) + { + std::string id = model.data (model.index (*iter, columnIndex)). + toString().toUtf8().constData(); + + mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id)); + } + + if (rows.size()>1) + mDocument.getUndoStack().endMacro(); +} + +void CSMWorld::CommandDispatcher::executeRevert() +{ + if (mLocked) + return; + + std::vector rows = getRevertableRecords(); + + if (rows.empty()) + return; + + IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); + + int columnIndex = model.findColumnIndex (Columns::ColumnId_Id); + + if (rows.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Revert multiple records")); + + for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) + { + std::string id = model.data (model.index (*iter, columnIndex)). + toString().toUtf8().constData(); + + mDocument.getUndoStack().push (new CSMWorld::RevertCommand (model, id)); + } + + if (rows.size()>1) + mDocument.getUndoStack().endMacro(); +} diff --git a/apps/opencs/model/world/commanddispatcher.hpp b/apps/opencs/model/world/commanddispatcher.hpp new file mode 100644 index 000000000..a976df46b --- /dev/null +++ b/apps/opencs/model/world/commanddispatcher.hpp @@ -0,0 +1,53 @@ +#ifndef CSM_WOLRD_COMMANDDISPATCHER_H +#define CSM_WOLRD_COMMANDDISPATCHER_H + +#include + +#include + +#include "universalid.hpp" + +namespace CSMDoc +{ + class Document; +} + +namespace CSMWorld +{ + class CommandDispatcher : public QObject + { + Q_OBJECT + + bool mLocked; + CSMDoc::Document& mDocument; + UniversalId mId; + std::vector mSelection; + + std::vector getDeletableRecords() const; + + std::vector getRevertableRecords() const; + + public: + + CommandDispatcher (CSMDoc::Document& document, const CSMWorld::UniversalId& id, + QObject *parent = 0); + ///< \param id ID of the table the commands should operate on primarily. + + void setEditLock (bool locked); + + void setSelection (const std::vector& selection); + + bool canDelete() const; + + bool canRevert() const; + + public slots: + + void executeDelete(); + + void executeRevert(); + + }; +} + +#endif diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 912cd2183..5a2d2e536 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -19,14 +19,28 @@ #include "../../model/world/columns.hpp" #include "../../model/world/tablemimedata.hpp" #include "../../model/world/tablemimedata.hpp" +#include "../../model/world/commanddispatcher.hpp" #include "recordstatusdelegate.hpp" #include "util.hpp" void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) { + // configure dispatcher QModelIndexList selectedRows = selectionModel()->selectedRows(); + std::vector rows; + + for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); + ++iter) + { + QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); + rows.push_back (index.row()); + } + + mDispatcher->setSelection (rows); + + // create context menu QMenu menu (this); /// \todo add menu items for select all and clear selection @@ -44,13 +58,10 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (mCreateAction) menu.addAction (mCreateAction); - /// \todo Reverting temporarily disabled on tables that support reordering, because - /// revert logic currently can not handle reordering. - if (!(mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic)) - if (listRevertableSelectedIds().size()>0) - menu.addAction (mRevertAction); + if (mDispatcher->canRevert()) + menu.addAction (mRevertAction); - if (listDeletableSelectedIds().size()>0) + if (mDispatcher->canDelete()) menu.addAction (mDeleteAction); if (mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic) @@ -108,84 +119,6 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) menu.exec (event->globalPos()); } -std::vector CSVWorld::Table::listRevertableSelectedIds() const -{ - std::vector revertableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state!=CSMWorld::RecordBase::State_BaseOnly) - { - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - revertableIds.push_back (id); - } - } - } - - return revertableIds; -} - -std::vector CSVWorld::Table::listDeletableSelectedIds() const -{ - std::vector deletableIds; - - if (mProxyModel->columnCount()>0) - { - QModelIndexList selectedRows = selectionModel()->selectedRows(); - - for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); - ++iter) - { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - - // check record state - CSMWorld::RecordBase::State state = - static_cast ( - mModel->data (mModel->index (index.row(), 1)).toInt()); - - if (state==CSMWorld::RecordBase::State_Deleted) - continue; - - // check other columns (only relevant for a subset of the tables) - int dialogueTypeIndex = - mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_DialogueType); - - if (dialogueTypeIndex!=-1) - { - int type = mModel->data (mModel->index (index.row(), dialogueTypeIndex)).toInt(); - - if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) - continue; - } - - // add the id to the collection - int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); - - std::string id = mModel->data (mModel->index (index.row(), columnIndex)). - toString().toUtf8().constData(); - - deletableIds.push_back (id); - } - } - - return deletableIds; -} - CSVWorld::Table::Table (const CSMWorld::UniversalId& id, bool createAndDelete, bool sorting, CSMDoc::Document& document) : mCreateAction (0), mCloneAction(0), mRecordStatusDisplay (0), @@ -196,6 +129,8 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, mProxyModel = new CSMWorld::IdTableProxyModel (this); mProxyModel->setSourceModel (mModel); + mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); + setModel (mProxyModel); horizontalHeader()->setResizeMode (QHeaderView::Interactive); verticalHeader()->hide(); @@ -240,11 +175,11 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, } mRevertAction = new QAction (tr ("Revert Record"), this); - connect (mRevertAction, SIGNAL (triggered()), this, SLOT (revertRecord())); + connect (mRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeRevert())); addAction (mRevertAction); mDeleteAction = new QAction (tr ("Delete Record"), this); - connect (mDeleteAction, SIGNAL (triggered()), this, SLOT (deleteRecord())); + connect (mDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeDelete())); addAction (mDeleteAction); mMoveUpAction = new QAction (tr ("Move Up"), this); @@ -283,6 +218,7 @@ void CSVWorld::Table::setEditLock (bool locked) (*iter)->setEditLock (locked); DragRecordTable::setEditLock(locked); + mDispatcher->setEditLock (locked); } CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const @@ -292,46 +228,6 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const mProxyModel->data (mProxyModel->index (row, 0)).toString().toUtf8().constData()); } -void CSVWorld::Table::revertRecord() -{ - if (!mEditLock) - { - std::vector revertableIds = listRevertableSelectedIds(); - - if (!revertableIds.empty()) - { - if (revertableIds.size()>1) - mDocument.getUndoStack().beginMacro (tr ("Revert multiple records")); - - for (std::vector::const_iterator iter (revertableIds.begin()); iter!=revertableIds.end(); ++iter) - mDocument.getUndoStack().push (new CSMWorld::RevertCommand (*mModel, *iter)); - - if (revertableIds.size()>1) - mDocument.getUndoStack().endMacro(); - } - } -} - -void CSVWorld::Table::deleteRecord() -{ - if (!mEditLock) - { - std::vector deletableIds = listDeletableSelectedIds(); - - if (!deletableIds.empty()) - { - if (deletableIds.size()>1) - mDocument.getUndoStack().beginMacro (tr ("Delete multiple records")); - - for (std::vector::const_iterator iter (deletableIds.begin()); iter!=deletableIds.end(); ++iter) - mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (*mModel, *iter)); - - if (deletableIds.size()>1) - mDocument.getUndoStack().endMacro(); - } - } -} - void CSVWorld::Table::editRecord() { if (!mEditLock) diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 3b1d40e78..54971fb66 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -24,6 +24,7 @@ namespace CSMWorld class UniversalId; class IdTableProxyModel; class IdTable; + class CommandDispatcher; } namespace CSVWorld @@ -48,15 +49,12 @@ namespace CSVWorld CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; int mRecordStatusDisplay; + CSMWorld::CommandDispatcher *mDispatcher; private: void contextMenuEvent (QContextMenuEvent *event); - std::vector listRevertableSelectedIds() const; - - std::vector listDeletableSelectedIds() const; - void mouseMoveEvent(QMouseEvent *event); void dropEvent(QDropEvent *event); @@ -93,10 +91,6 @@ namespace CSVWorld private slots: - void revertRecord(); - - void deleteRecord(); - void editRecord(); void cloneRecord(); From 3e0c6ec732e660da413ba8e52843fbebfef637ab Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Jun 2014 13:09:17 +0200 Subject: [PATCH 03/11] some general cleanup; fixed two cases of dialogue subviews that allowed creating records where they shouldn't have --- apps/opencs/view/world/subviews.cpp | 57 +++++++++++++---------------- 1 file changed, 25 insertions(+), 32 deletions(-) diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index 75f391699..022d5d02e 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -80,49 +80,42 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Scene, new CSVDoc::SubViewFactory); - //edit subviews - manager.add (CSMWorld::UniversalId::Type_Region, - new CSVDoc::SubViewFactoryWithCreator > (false)); + // Dialogue subviews + static const CSMWorld::UniversalId::Type sTableTypes2[] = + { + CSMWorld::UniversalId::Type_Region, + CSMWorld::UniversalId::Type_Spell, + CSMWorld::UniversalId::Type_Birthsign, + CSMWorld::UniversalId::Type_Global, + CSMWorld::UniversalId::Type_Race, + CSMWorld::UniversalId::Type_Class, + CSMWorld::UniversalId::Type_Filter, + CSMWorld::UniversalId::Type_Sound, + CSMWorld::UniversalId::Type_Faction, - manager.add (CSMWorld::UniversalId::Type_Spell, - new CSVDoc::SubViewFactoryWithCreator > (false)); + CSMWorld::UniversalId::Type_None // end marker + }; + + for (int i=0; sTableTypes2[i]!=CSMWorld::UniversalId::Type_None; ++i) + manager.add (sTableTypes2[i], + new CSVDoc::SubViewFactoryWithCreator > (false)); + + manager.add (CSMWorld::UniversalId::Type_Skill, + new CSVDoc::SubViewFactoryWithCreator (false)); + + manager.add (CSMWorld::UniversalId::Type_Gmst, + new CSVDoc::SubViewFactoryWithCreator (false)); manager.add (CSMWorld::UniversalId::Type_Referenceable, new CSVDoc::SubViewFactoryWithCreator > (false)); - manager.add (CSMWorld::UniversalId::Type_Birthsign, - new CSVDoc::SubViewFactoryWithCreator > (false)); - - manager.add (CSMWorld::UniversalId::Type_Global, - new CSVDoc::SubViewFactoryWithCreator > (false)); - - manager.add (CSMWorld::UniversalId::Type_Gmst, - new CSVDoc::SubViewFactoryWithCreator > (false)); - - manager.add (CSMWorld::UniversalId::Type_Race, - new CSVDoc::SubViewFactoryWithCreator > (false)); - - manager.add (CSMWorld::UniversalId::Type_Class, - new CSVDoc::SubViewFactoryWithCreator > (false)); - manager.add (CSMWorld::UniversalId::Type_Reference, new CSVDoc::SubViewFactoryWithCreator > (false)); manager.add (CSMWorld::UniversalId::Type_Cell, new CSVDoc::SubViewFactoryWithCreator > (false)); - manager.add (CSMWorld::UniversalId::Type_Filter, - new CSVDoc::SubViewFactoryWithCreator > (false)); - - manager.add (CSMWorld::UniversalId::Type_Sound, - new CSVDoc::SubViewFactoryWithCreator > (false)); - - manager.add (CSMWorld::UniversalId::Type_Faction, - new CSVDoc::SubViewFactoryWithCreator > (false)); - - manager.add (CSMWorld::UniversalId::Type_Skill, - new CSVDoc::SubViewFactoryWithCreator > (false)); - manager.add (CSMWorld::UniversalId::Type_JournalInfo, new CSVDoc::SubViewFactoryWithCreator > (false)); From c6557e1ade35b5eddccc26ba987c15115944d137 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Jun 2014 14:25:06 +0200 Subject: [PATCH 04/11] some UniversalId cleanup and enhancements --- apps/opencs/model/world/universalid.cpp | 28 ++++++++++++++++++++++--- apps/opencs/model/world/universalid.hpp | 9 +++++++- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index 140a410c0..88e649ace 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -61,8 +61,8 @@ namespace { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Spell, "Spell", ":./spell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Topic, "Topic", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Journal, "Journal", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 }, - { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_TopicInfo, "TopicInfo", 0 }, + { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_JournalInfo, "JournalInfo", 0 }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Cell_Missing, "Cell", ":./cell.png" }, { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Referenceable, "Referenceables", 0 }, @@ -90,7 +90,7 @@ namespace { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Static, "Static", ":./static.png" }, { CSMWorld::UniversalId::Class_RefRecord, CSMWorld::UniversalId::Type_Weapon, "Weapon", ":./weapon.png" }, { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Reference, "Reference", 0 }, - { CSMWorld::UniversalId::Class_SubRecord, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, + { CSMWorld::UniversalId::Class_Record, CSMWorld::UniversalId::Type_Filter, "Filter", ":./filter.png" }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Scene, "Scene", 0 }, { CSMWorld::UniversalId::Class_Collection, CSMWorld::UniversalId::Type_Preview, "Preview", 0 }, @@ -320,6 +320,28 @@ std::vector CSMWorld::UniversalId::listReferenceabl return list; } +CSMWorld::UniversalId::Type CSMWorld::UniversalId::getParentType (Type type) +{ + for (int i=0; sIdArg[i].mType; ++i) + if (type==sIdArg[i].mType) + { + if (sIdArg[i].mClass==Class_RefRecord) + return Type_Referenceables; + + if (sIdArg[i].mClass==Class_SubRecord || sIdArg[i].mClass==Class_Record) + { + if (type==Type_Cell_Missing) + return Type_Cells; + + return static_cast (type-1); + } + + break; + } + + return Type_None; +} + bool CSMWorld::operator== (const CSMWorld::UniversalId& left, const CSMWorld::UniversalId& right) { return left.isEqual (right); diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index 22779b263..3bef71c75 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -32,6 +32,8 @@ namespace CSMWorld ArgumentType_Index }; + /// \note A record list type must always be immediately followed by the matching + /// record type, if this type is of class SubRecord or Record. enum Type { Type_None = 0, @@ -86,8 +88,8 @@ namespace CSMWorld Type_References, Type_Reference, Type_RegionMap, - Type_Filter, Type_Filters, + Type_Filter, Type_Topics, Type_Topic, Type_Journals, @@ -147,6 +149,11 @@ namespace CSMWorld ///< Will return an empty string, if no icon is available. static std::vector listReferenceableTypes(); + + /// If \a type is a SubRecord, RefRecord or Record type return the type of the table + /// that contains records of type \a type. + /// Otherwise return Type_None. + static Type getParentType (Type type); }; bool operator== (const UniversalId& left, const UniversalId& right); From f6ae967ba03bdc32b9526391e565baa3e951869a Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 6 Jun 2014 20:47:31 +0200 Subject: [PATCH 05/11] simplified Data constructor --- apps/opencs/model/world/data.cpp | 48 ++++++++++++++++---------------- apps/opencs/model/world/data.hpp | 4 +-- 2 files changed, 26 insertions(+), 26 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index f448f5bfa..7f2eac20d 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -16,11 +16,12 @@ #include "regionmap.hpp" #include "columns.hpp" -void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1, - UniversalId::Type type2, bool update) +void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update) { mModels.push_back (model); - mModelIndex.insert (std::make_pair (type1, model)); + mModelIndex.insert (std::make_pair (type, model)); + + UniversalId::Type type2 = UniversalId::getParentType (type); if (type2!=UniversalId::Type_None) mModelIndex.insert (std::make_pair (type2, model)); @@ -235,26 +236,26 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding) mFilters.addColumn (new DescriptionColumn); mFilters.addColumn (new ScopeColumn); - addModel (new IdTable (&mGlobals), UniversalId::Type_Globals, UniversalId::Type_Global); - addModel (new IdTable (&mGmsts), UniversalId::Type_Gmsts, UniversalId::Type_Gmst); - addModel (new IdTable (&mSkills), UniversalId::Type_Skills, UniversalId::Type_Skill, false); - addModel (new IdTable (&mClasses), UniversalId::Type_Classes, UniversalId::Type_Class); - addModel (new IdTable (&mFactions), UniversalId::Type_Factions, UniversalId::Type_Faction); - addModel (new IdTable (&mRaces), UniversalId::Type_Races, UniversalId::Type_Race); - addModel (new IdTable (&mSounds), UniversalId::Type_Sounds, UniversalId::Type_Sound); - addModel (new IdTable (&mScripts), UniversalId::Type_Scripts, UniversalId::Type_Script); - addModel (new IdTable (&mRegions), UniversalId::Type_Regions, UniversalId::Type_Region); - addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsigns, UniversalId::Type_Birthsign); - addModel (new IdTable (&mSpells), UniversalId::Type_Spells, UniversalId::Type_Spell); - addModel (new IdTable (&mTopics), UniversalId::Type_Topics, UniversalId::Type_Topic); - addModel (new IdTable (&mJournals), UniversalId::Type_Journals, UniversalId::Type_Journal); - addModel (new IdTable (&mTopicInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_TopicInfos, UniversalId::Type_TopicInfo); - addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfos, UniversalId::Type_JournalInfo); - addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cells, UniversalId::Type_Cell); + addModel (new IdTable (&mGlobals), UniversalId::Type_Global); + addModel (new IdTable (&mGmsts), UniversalId::Type_Gmst); + addModel (new IdTable (&mSkills), UniversalId::Type_Skill); + addModel (new IdTable (&mClasses), UniversalId::Type_Class); + addModel (new IdTable (&mFactions), UniversalId::Type_Faction); + addModel (new IdTable (&mRaces), UniversalId::Type_Race); + addModel (new IdTable (&mSounds), UniversalId::Type_Sound); + addModel (new IdTable (&mScripts), UniversalId::Type_Script); + addModel (new IdTable (&mRegions), UniversalId::Type_Region); + addModel (new IdTable (&mBirthsigns), UniversalId::Type_Birthsign); + addModel (new IdTable (&mSpells), UniversalId::Type_Spell); + addModel (new IdTable (&mTopics), UniversalId::Type_Topic); + addModel (new IdTable (&mJournals), UniversalId::Type_Journal); + addModel (new IdTable (&mTopicInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_TopicInfo); + addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo); + addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell); addModel (new IdTable (&mReferenceables, IdTable::Feature_Preview), - UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); - addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_References, UniversalId::Type_Reference, false); - addModel (new IdTable (&mFilters), UniversalId::Type_Filters, UniversalId::Type_Filter, false); + UniversalId::Type_Referenceable); + addModel (new IdTable (&mRefs, IdTable::Feature_ViewCell | IdTable::Feature_Preview), UniversalId::Type_Reference); + addModel (new IdTable (&mFilters), UniversalId::Type_Filter); } CSMWorld::Data::~Data() @@ -469,8 +470,7 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const CSMWorld::UniversalId& if (id.getType()==UniversalId::Type_RegionMap) { RegionMap *table = 0; - addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, - UniversalId::Type_None, false); + addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, false); return table; } throw std::logic_error ("No table model available for " + id.toString()); diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp index 9a842878a..edca9fdbc 100644 --- a/apps/opencs/model/world/data.hpp +++ b/apps/opencs/model/world/data.hpp @@ -83,8 +83,8 @@ namespace CSMWorld Data (const Data&); Data& operator= (const Data&); - void addModel (QAbstractItemModel *model, UniversalId::Type type1, - UniversalId::Type type2 = UniversalId::Type_None, bool update = true); + void addModel (QAbstractItemModel *model, UniversalId::Type type, + bool update = true); static void appendIds (std::vector& ids, const CollectionBase& collection, bool listDeleted); From 85fca19fd940876f980387dc42a8c6708d2824dd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 7 Jun 2014 13:02:45 +0200 Subject: [PATCH 06/11] replaced selection model in CommandDispatcher with a slightly slower but more robust implementation --- apps/opencs/model/world/commanddispatcher.cpp | 36 ++++++++++--------- apps/opencs/model/world/commanddispatcher.hpp | 8 ++--- apps/opencs/view/world/table.cpp | 10 +++--- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp index 1e2715744..2b4c3109f 100644 --- a/apps/opencs/model/world/commanddispatcher.cpp +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -7,18 +7,20 @@ #include "record.hpp" #include "commands.hpp" -std::vector CSMWorld::CommandDispatcher::getDeletableRecords() const +std::vector CSMWorld::CommandDispatcher::getDeletableRecords() const { - std::vector result; + std::vector result; IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); - for (std::vector::const_iterator iter (mSelection.begin()); + for (std::vector::const_iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { + int row = model.getModelIndex (*iter, 0).row(); + // check record state RecordBase::State state = - static_cast (model.data (model.index (*iter, 1)).toInt()); + static_cast (model.data (model.index (row, 1)).toInt()); if (state==RecordBase::State_Deleted) continue; @@ -28,7 +30,7 @@ std::vector CSMWorld::CommandDispatcher::getDeletableRecords() const if (dialogueTypeIndex!=-1) { - int type = model.data (model.index (*iter, dialogueTypeIndex)).toInt(); + int type = model.data (model.index (row, dialogueTypeIndex)).toInt(); if (type!=ESM::Dialogue::Topic && type!=ESM::Dialogue::Journal) continue; @@ -40,9 +42,9 @@ std::vector CSMWorld::CommandDispatcher::getDeletableRecords() const return result; } -std::vector CSMWorld::CommandDispatcher::getRevertableRecords() const +std::vector CSMWorld::CommandDispatcher::getRevertableRecords() const { - std::vector result; + std::vector result; IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); @@ -51,12 +53,14 @@ std::vector CSMWorld::CommandDispatcher::getRevertableRecords() const if (model.getFeatures() & IdTable::Feature_ReorderWithinTopic) return result; - for (std::vector::const_iterator iter (mSelection.begin()); + for (std::vector::const_iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { + int row = model.getModelIndex (*iter, 0).row(); + // check record state RecordBase::State state = - static_cast (model.data (model.index (*iter, 1)).toInt()); + static_cast (model.data (model.index (row, 1)).toInt()); if (state==RecordBase::State_BaseOnly) continue; @@ -77,7 +81,7 @@ void CSMWorld::CommandDispatcher::setEditLock (bool locked) mLocked = locked; } -void CSMWorld::CommandDispatcher::setSelection (const std::vector& selection) +void CSMWorld::CommandDispatcher::setSelection (const std::vector& selection) { mSelection = selection; } @@ -103,7 +107,7 @@ void CSMWorld::CommandDispatcher::executeDelete() if (mLocked) return; - std::vector rows = getDeletableRecords(); + std::vector rows = getDeletableRecords(); if (rows.empty()) return; @@ -115,9 +119,9 @@ void CSMWorld::CommandDispatcher::executeDelete() if (rows.size()>1) mDocument.getUndoStack().beginMacro (tr ("Delete multiple records")); - for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) + for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) { - std::string id = model.data (model.index (*iter, columnIndex)). + std::string id = model.data (model.getModelIndex (*iter, columnIndex)). toString().toUtf8().constData(); mDocument.getUndoStack().push (new CSMWorld::DeleteCommand (model, id)); @@ -132,7 +136,7 @@ void CSMWorld::CommandDispatcher::executeRevert() if (mLocked) return; - std::vector rows = getRevertableRecords(); + std::vector rows = getRevertableRecords(); if (rows.empty()) return; @@ -144,9 +148,9 @@ void CSMWorld::CommandDispatcher::executeRevert() if (rows.size()>1) mDocument.getUndoStack().beginMacro (tr ("Revert multiple records")); - for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) + for (std::vector::const_iterator iter (rows.begin()); iter!=rows.end(); ++iter) { - std::string id = model.data (model.index (*iter, columnIndex)). + std::string id = model.data (model.getModelIndex (*iter, columnIndex)). toString().toUtf8().constData(); mDocument.getUndoStack().push (new CSMWorld::RevertCommand (model, id)); diff --git a/apps/opencs/model/world/commanddispatcher.hpp b/apps/opencs/model/world/commanddispatcher.hpp index a976df46b..3bf81f068 100644 --- a/apps/opencs/model/world/commanddispatcher.hpp +++ b/apps/opencs/model/world/commanddispatcher.hpp @@ -21,11 +21,11 @@ namespace CSMWorld bool mLocked; CSMDoc::Document& mDocument; UniversalId mId; - std::vector mSelection; + std::vector mSelection; - std::vector getDeletableRecords() const; + std::vector getDeletableRecords() const; - std::vector getRevertableRecords() const; + std::vector getRevertableRecords() const; public: @@ -35,7 +35,7 @@ namespace CSMWorld void setEditLock (bool locked); - void setSelection (const std::vector& selection); + void setSelection (const std::vector& selection); bool canDelete() const; diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 5a2d2e536..7599c8341 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -29,16 +29,18 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) // configure dispatcher QModelIndexList selectedRows = selectionModel()->selectedRows(); - std::vector rows; + std::vector records; + + int columnIndex = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) { - QModelIndex index = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)); - rows.push_back (index.row()); + records.push_back (mProxyModel->data ( + mProxyModel->index (iter->row(), columnIndex)).toString().toUtf8().constData()); } - mDispatcher->setSelection (rows); + mDispatcher->setSelection (records); // create context menu QMenu menu (this); From e4a37ef7097d22e388c5c3809052190e4cc5ecef Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 7 Jun 2014 13:06:19 +0200 Subject: [PATCH 07/11] removed some hardcoded column numbers --- apps/opencs/model/world/commanddispatcher.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp index 2b4c3109f..ce6d1e398 100644 --- a/apps/opencs/model/world/commanddispatcher.cpp +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -13,14 +13,16 @@ std::vector CSMWorld::CommandDispatcher::getDeletableRecords() cons IdTable& model = dynamic_cast (*mDocument.getData().getTableModel (mId)); + int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification); + for (std::vector::const_iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { int row = model.getModelIndex (*iter, 0).row(); // check record state - RecordBase::State state = - static_cast (model.data (model.index (row, 1)).toInt()); + RecordBase::State state = static_cast ( + model.data (model.index (row, stateColumnIndex)).toInt()); if (state==RecordBase::State_Deleted) continue; @@ -53,14 +55,16 @@ std::vector CSMWorld::CommandDispatcher::getRevertableRecords() con if (model.getFeatures() & IdTable::Feature_ReorderWithinTopic) return result; + int stateColumnIndex = model.findColumnIndex (Columns::ColumnId_Modification); + for (std::vector::const_iterator iter (mSelection.begin()); iter!=mSelection.end(); ++iter) { int row = model.getModelIndex (*iter, 0).row(); // check record state - RecordBase::State state = - static_cast (model.data (model.index (row, 1)).toInt()); + RecordBase::State state = static_cast ( + model.data (model.index (row, stateColumnIndex)).toInt()); if (state==RecordBase::State_BaseOnly) continue; From 302e2f8e9a7ad6d7deab6a0f66bb230677221a41 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 7 Jun 2014 14:32:12 +0200 Subject: [PATCH 08/11] fixed some proxy model problems related to column numbers --- apps/opencs/view/world/table.cpp | 25 +++++++++++++++---------- 1 file changed, 15 insertions(+), 10 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 7599c8341..217d52ec1 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -36,8 +36,10 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) for (QModelIndexList::const_iterator iter (selectedRows.begin()); iter!=selectedRows.end(); ++iter) { - records.push_back (mProxyModel->data ( - mProxyModel->index (iter->row(), columnIndex)).toString().toUtf8().constData()); + int row = mProxyModel->mapToSource (mProxyModel->index (iter->row(), 0)).row(); + + records.push_back (mModel->data ( + mModel->index (row, columnIndex)).toString().toUtf8().constData()); } mDispatcher->setSelection (records); @@ -71,8 +73,6 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) /// \todo allow reordering of multiple rows if (selectedRows.size()==1) { - int row =selectedRows.begin()->row(); - int column = mModel->searchColumnIndex (CSMWorld::Columns::ColumnId_Topic); if (column==-1) @@ -80,14 +80,17 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) if (column!=-1) { - if (row>0 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row-1, column))) + int row = mProxyModel->mapToSource ( + mProxyModel->index (selectedRows.begin()->row(), 0)).row(); + + if (row>0 && mModel->data (mModel->index (row, column))== + mModel->data (mModel->index (row-1, column))) { menu.addAction (mMoveUpAction); } - if (rowrowCount()-1 && mProxyModel->data (mProxyModel->index (row, column))== - mProxyModel->data (mProxyModel->index (row+1, column))) + if (rowrowCount()-1 && mModel->data (mModel->index (row, column))== + mModel->data (mModel->index (row+1, column))) { menu.addAction (mMoveDownAction); } @@ -225,9 +228,11 @@ void CSVWorld::Table::setEditLock (bool locked) CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const { + row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + return CSMWorld::UniversalId ( - static_cast (mProxyModel->data (mProxyModel->index (row, 2)).toInt()), - mProxyModel->data (mProxyModel->index (row, 0)).toString().toUtf8().constData()); + static_cast (mModel->data (mModel->index (row, 2)).toInt()), + mModel->data (mModel->index (row, 0)).toString().toUtf8().constData()); } void CSVWorld::Table::editRecord() From 7ede6573ebc825bc2a1e71c985536981fa7636e7 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 7 Jun 2014 14:39:34 +0200 Subject: [PATCH 09/11] removed more hardcoded column numbers --- apps/opencs/view/world/table.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 217d52ec1..0fc7dd4b2 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -230,9 +230,12 @@ CSMWorld::UniversalId CSVWorld::Table::getUniversalId (int row) const { row = mProxyModel->mapToSource (mProxyModel->index (row, 0)).row(); + int idColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + int typeColumn = mModel->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType); + return CSMWorld::UniversalId ( - static_cast (mModel->data (mModel->index (row, 2)).toInt()), - mModel->data (mModel->index (row, 0)).toString().toUtf8().constData()); + static_cast (mModel->data (mModel->index (row, typeColumn)).toInt()), + mModel->data (mModel->index (row, idColumn)).toString().toUtf8().constData()); } void CSVWorld::Table::editRecord() From 48468b7d0ca5db689ca0b6d1141f8a4119a5b35f Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sun, 8 Jun 2014 14:10:08 +0200 Subject: [PATCH 10/11] some dialogue subview cleanup and start of command dispatching refactoring --- apps/opencs/view/world/dialoguesubview.cpp | 30 +++++++++------------- apps/opencs/view/world/dialoguesubview.hpp | 3 +++ 2 files changed, 15 insertions(+), 18 deletions(-) diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index 6b57f2685..ad2db8723 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -332,6 +332,7 @@ void CSVWorld::EditWidget::remake(int row) if (mMainWidget) { delete mMainWidget; + mMainWidget = 0; } mMainWidget = new QWidget (this); @@ -339,6 +340,7 @@ void CSVWorld::EditWidget::remake(int row) if (mWidgetMapper) { delete mWidgetMapper; + mWidgetMapper = 0; } mWidgetMapper = new QDataWidgetMapper (this); mWidgetMapper->setModel(mTable); @@ -396,7 +398,7 @@ void CSVWorld::EditWidget::remake(int row) mWidgetMapper->setCurrentModelIndex(mTable->index(row, 0)); - this->setMinimumWidth(325); //TODO find better way to set the width or make it customizable + this->setMinimumWidth(325); /// \todo replace hardcoded value with a user setting this->setWidget(mMainWidget); this->setWidgetResizable(true); } @@ -407,7 +409,6 @@ void CSVWorld::EditWidget::remake(int row) CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSMDoc::Document& document, const CreatorFactoryBase& creatorFactory, bool sorting) : - SubView (id), mEditWidget(0), mMainLayout(NULL), @@ -415,8 +416,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mTable(dynamic_cast(document.getData().getTableModel(id))), mRow (-1), mLocked(false), - mDocument(document) - + mDocument(document), + mCommandDispatcher (document, CSMWorld::UniversalId::getParentType (id.getType())) { connect(mTable, SIGNAL(dataChanged (const QModelIndex&, const QModelIndex&)), this, SLOT(dataChanged(const QModelIndex&))); mRow = mTable->getModelIndex (id.getId(), 0).row(); @@ -560,14 +561,12 @@ void CSVWorld::DialogueSubView::nextId() void CSVWorld::DialogueSubView::setEditLock (bool locked) { mLocked = locked; + CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); - if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) - { - mEditWidget->setDisabled(true); - } else - { - mEditWidget->setDisabled(mLocked); - } + + mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || locked); + + mCommandDispatcher.setEditLock (locked); } void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index) @@ -575,13 +574,8 @@ void CSVWorld::DialogueSubView::dataChanged(const QModelIndex & index) if (index.row() == mRow) { CSMWorld::RecordBase::State state = static_cast(mTable->data (mTable->index (mRow, 1)).toInt()); - if (state == CSMWorld::RecordBase::State_Deleted || state == CSMWorld::RecordBase::State_Erased) - { - mEditWidget->setDisabled(true); - } else - { - mEditWidget->setDisabled(mLocked); - } + + mEditWidget->setDisabled (state==CSMWorld::RecordBase::State_Deleted || mLocked); } } diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 5642f46a0..cbca0159c 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -8,7 +8,9 @@ #include #include "../doc/subview.hpp" + #include "../../model/world/columnbase.hpp" +#include "../../model/world/commanddispatcher.hpp" class QDataWidgetMapper; class QSize; @@ -169,6 +171,7 @@ namespace CSVWorld bool mLocked; const CSMDoc::Document& mDocument; TableBottomBox* mBottom; + CSMWorld::CommandDispatcher mCommandDispatcher; public: From 7d1ecea20c9e3c2cac2168f961c8a78b00bf5d2b Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Wed, 11 Jun 2014 11:43:38 +0200 Subject: [PATCH 11/11] added extended versions of revert and delete --- apps/opencs/model/world/commanddispatcher.cpp | 102 ++++++++++++++++++ apps/opencs/model/world/commanddispatcher.hpp | 16 +++ apps/opencs/view/world/table.cpp | 26 +++++ apps/opencs/view/world/table.hpp | 2 + 4 files changed, 146 insertions(+) diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp index ce6d1e398..4e146d87c 100644 --- a/apps/opencs/model/world/commanddispatcher.cpp +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -1,6 +1,10 @@ #include "commanddispatcher.hpp" +#include + +#include + #include "../doc/document.hpp" #include "idtable.hpp" @@ -88,6 +92,13 @@ void CSMWorld::CommandDispatcher::setEditLock (bool locked) void CSMWorld::CommandDispatcher::setSelection (const std::vector& selection) { mSelection = selection; + std::for_each (mSelection.begin(), mSelection.end(), Misc::StringUtils::toLower); + std::sort (mSelection.begin(), mSelection.end()); +} + +void CSMWorld::CommandDispatcher::setExtendedTypes (const std::vector& types) +{ + mExtendedTypes = types; } bool CSMWorld::CommandDispatcher::canDelete() const @@ -106,6 +117,20 @@ bool CSMWorld::CommandDispatcher::canRevert() const return getRevertableRecords().size()!=0; } +std::vector CSMWorld::CommandDispatcher::getExtendedTypes() const +{ + std::vector tables; + + if (mId==UniversalId::Type_Cells) + { + tables.push_back (mId); + tables.push_back (UniversalId::Type_References); + /// \todo add other cell-specific types + } + + return tables; +} + void CSMWorld::CommandDispatcher::executeDelete() { if (mLocked) @@ -163,3 +188,80 @@ void CSMWorld::CommandDispatcher::executeRevert() if (rows.size()>1) mDocument.getUndoStack().endMacro(); } + +void CSMWorld::CommandDispatcher::executeExtendedDelete() +{ + if (mExtendedTypes.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Extended delete of multiple records")); + + for (std::vector::const_iterator iter (mExtendedTypes.begin()); + iter!=mExtendedTypes.end(); ++iter) + { + if (*iter==mId) + executeDelete(); + else if (*iter==UniversalId::Type_References) + { + IdTable& model = dynamic_cast ( + *mDocument.getData().getTableModel (*iter)); + + const RefCollection& collection = mDocument.getData().getReferences(); + + int size = collection.getSize(); + + for (int i=size-1; i>=0; --i) + { + const Record& record = collection.getRecord (i); + + if (record.mState==RecordBase::State_Deleted) + continue; + + if (!std::binary_search (mSelection.begin(), mSelection.end(), + Misc::StringUtils::lowerCase (record.get().mCell))) + continue; + + mDocument.getUndoStack().push ( + new CSMWorld::DeleteCommand (model, record.get().mId)); + } + } + } + + if (mExtendedTypes.size()>1) + mDocument.getUndoStack().endMacro(); +} + +void CSMWorld::CommandDispatcher::executeExtendedRevert() +{ + if (mExtendedTypes.size()>1) + mDocument.getUndoStack().beginMacro (tr ("Extended revert of multiple records")); + + for (std::vector::const_iterator iter (mExtendedTypes.begin()); + iter!=mExtendedTypes.end(); ++iter) + { + if (*iter==mId) + executeRevert(); + else if (*iter==UniversalId::Type_References) + { + IdTable& model = dynamic_cast ( + *mDocument.getData().getTableModel (*iter)); + + const RefCollection& collection = mDocument.getData().getReferences(); + + int size = collection.getSize(); + + for (int i=size-1; i>=0; --i) + { + const Record& record = collection.getRecord (i); + + if (!std::binary_search (mSelection.begin(), mSelection.end(), + Misc::StringUtils::lowerCase (record.get().mCell))) + continue; + + mDocument.getUndoStack().push ( + new CSMWorld::RevertCommand (model, record.get().mId)); + } + } + } + + if (mExtendedTypes.size()>1) + mDocument.getUndoStack().endMacro(); +} \ No newline at end of file diff --git a/apps/opencs/model/world/commanddispatcher.hpp b/apps/opencs/model/world/commanddispatcher.hpp index 3bf81f068..50085b1a1 100644 --- a/apps/opencs/model/world/commanddispatcher.hpp +++ b/apps/opencs/model/world/commanddispatcher.hpp @@ -22,6 +22,7 @@ namespace CSMWorld CSMDoc::Document& mDocument; UniversalId mId; std::vector mSelection; + std::vector mExtendedTypes; std::vector getDeletableRecords() const; @@ -37,16 +38,31 @@ namespace CSMWorld void setSelection (const std::vector& selection); + void setExtendedTypes (const std::vector& types); + ///< Set record lists selected by the user for extended operations. + bool canDelete() const; bool canRevert() const; + /// Return IDs of the record collection that can also be affected when + /// operating on the record collection this dispatcher is used for. + /// + /// \note The returned collection contains the ID of the record collection this + /// dispatcher is used for. However if that record collection does not support + /// the extended mode, the returned vector will be empty instead. + std::vector getExtendedTypes() const; + public slots: void executeDelete(); void executeRevert(); + void executeExtendedDelete(); + + void executeExtendedRevert(); + }; } diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 0fc7dd4b2..877fd51c0 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -44,6 +44,10 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) mDispatcher->setSelection (records); + std::vector extendedTypes = mDispatcher->getExtendedTypes(); + + mDispatcher->setExtendedTypes (extendedTypes); + // create context menu QMenu menu (this); @@ -63,11 +67,21 @@ void CSVWorld::Table::contextMenuEvent (QContextMenuEvent *event) menu.addAction (mCreateAction); if (mDispatcher->canRevert()) + { menu.addAction (mRevertAction); + if (!extendedTypes.empty()) + menu.addAction (mExtendedRevertAction); + } + if (mDispatcher->canDelete()) + { menu.addAction (mDeleteAction); + if (!extendedTypes.empty()) + menu.addAction (mExtendedDeleteAction); + } + if (mModel->getFeatures() & CSMWorld::IdTable::Feature_ReorderWithinTopic) { /// \todo allow reordering of multiple rows @@ -203,6 +217,18 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, connect (mPreviewAction, SIGNAL (triggered()), this, SLOT (previewRecord())); addAction (mPreviewAction); + /// \todo add a user option, that redirects the extended action to an input panel (in + /// the bottom bar) that lets the user select which record collections should be + /// modified. + + mExtendedDeleteAction = new QAction (tr ("Extended Delete Record"), this); + connect (mExtendedDeleteAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedDelete())); + addAction (mExtendedDeleteAction); + + mExtendedRevertAction = new QAction (tr ("Extended Revert Record"), this); + connect (mExtendedRevertAction, SIGNAL (triggered()), mDispatcher, SLOT (executeExtendedRevert())); + addAction (mExtendedRevertAction); + connect (mProxyModel, SIGNAL (rowsInserted (const QModelIndex&, int, int)), this, SLOT (tableSizeUpdate())); diff --git a/apps/opencs/view/world/table.hpp b/apps/opencs/view/world/table.hpp index 54971fb66..255c430ea 100644 --- a/apps/opencs/view/world/table.hpp +++ b/apps/opencs/view/world/table.hpp @@ -46,6 +46,8 @@ namespace CSVWorld QAction *mMoveDownAction; QAction *mViewAction; QAction *mPreviewAction; + QAction *mExtendedDeleteAction; + QAction *mExtendedRevertAction; CSMWorld::IdTableProxyModel *mProxyModel; CSMWorld::IdTable *mModel; int mRecordStatusDisplay;