diff --git a/apps/opencs/model/doc/savingstages.cpp b/apps/opencs/model/doc/savingstages.cpp index ef7d1d3af..d6258da6a 100644 --- a/apps/opencs/model/doc/savingstages.cpp +++ b/apps/opencs/model/doc/savingstages.cpp @@ -231,8 +231,29 @@ void CSMDoc::CollectionReferencesStage::perform (int stage, Messages& messages) record.mState==CSMWorld::RecordBase::State_Modified || record.mState==CSMWorld::RecordBase::State_ModifiedOnly) { - mState.getSubRecords()[Misc::StringUtils::lowerCase (record.get().mCell)] - .push_back (i); + std::string cellId = record.get().mOriginalCell.empty() ? + record.get().mCell : record.get().mOriginalCell; + + std::deque& indices = + mState.getSubRecords()[Misc::StringUtils::lowerCase (cellId)]; + + // collect moved references at the end of the container + bool interior = cellId.substr (0, 1)!="#"; + std::ostringstream stream; + if (!interior) + { + // recalculate the ref's cell location + std::pair index = record.get().getCellIndex(); + stream << "#" << index.first << " " << index.second; + } + + // An empty mOriginalCell is meant to indicate that it is the same as + // the current cell. It is possible that a moved ref is moved again. + if ((record.get().mOriginalCell.empty() ? + record.get().mCell : record.get().mOriginalCell) != stream.str() && !interior) + indices.push_back (i); + else + indices.push_front (i); } } } @@ -253,7 +274,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) const CSMWorld::Record& cell = mDocument.getData().getCells().getRecord (stage); - std::map >::const_iterator references = + std::map >::const_iterator references = mState.getSubRecords().find (Misc::StringUtils::lowerCase (cell.get().mId)); if (cell.mState==CSMWorld::RecordBase::State_Modified || @@ -284,7 +305,7 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) // write references if (references!=mState.getSubRecords().end()) { - for (std::vector::const_iterator iter (references->second.begin()); + for (std::deque::const_iterator iter (references->second.begin()); iter!=references->second.end(); ++iter) { const CSMWorld::Record& ref = @@ -293,6 +314,32 @@ void CSMDoc::WriteCellCollectionStage::perform (int stage, Messages& messages) if (ref.mState==CSMWorld::RecordBase::State_Modified || ref.mState==CSMWorld::RecordBase::State_ModifiedOnly) { + // recalculate the ref's cell location + std::ostringstream stream; + if (!interior) + { + std::pair index = ref.get().getCellIndex(); + stream << "#" << index.first << " " << index.second; + } + + // An empty mOriginalCell is meant to indicate that it is the same as + // the current cell. It is possible that a moved ref is moved again. + if ((ref.get().mOriginalCell.empty() ? ref.get().mCell : ref.get().mOriginalCell) + != stream.str() && !interior) + { + ESM::MovedCellRef moved; + moved.mRefNum = ref.get().mRefNum; + + // Need to fill mTarget with the ref's new position. + std::istringstream istream (stream.str().c_str()); + + char ignore; + istream >> ignore >> moved.mTarget[0] >> moved.mTarget[1]; + + ref.get().mRefNum.save (mState.getWriter(), false, "MVRF"); + mState.getWriter().writeHNT ("CNDT", moved.mTarget, 8); + } + ref.get().save (mState.getWriter()); } else if (ref.mState==CSMWorld::RecordBase::State_Deleted) diff --git a/apps/opencs/model/doc/savingstate.cpp b/apps/opencs/model/doc/savingstate.cpp index 84bca1e95..e7ad551b2 100644 --- a/apps/opencs/model/doc/savingstate.cpp +++ b/apps/opencs/model/doc/savingstate.cpp @@ -64,7 +64,7 @@ bool CSMDoc::SavingState::isProjectFile() const return mProjectFile; } -std::map >& CSMDoc::SavingState::getSubRecords() +std::map >& CSMDoc::SavingState::getSubRecords() { return mSubRecords; } diff --git a/apps/opencs/model/doc/savingstate.hpp b/apps/opencs/model/doc/savingstate.hpp index 577fc734d..e6c8c545a 100644 --- a/apps/opencs/model/doc/savingstate.hpp +++ b/apps/opencs/model/doc/savingstate.hpp @@ -3,6 +3,7 @@ #include #include +#include #include #include @@ -26,7 +27,7 @@ namespace CSMDoc ESM::ESMWriter mWriter; boost::filesystem::path mProjectPath; bool mProjectFile; - std::map > mSubRecords; // record ID, list of subrecords + std::map > mSubRecords; // record ID, list of subrecords public: @@ -49,7 +50,7 @@ namespace CSMDoc bool isProjectFile() const; ///< Currently saving project file? (instead of content file) - std::map >& getSubRecords(); + std::map >& getSubRecords(); }; diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index 0e954ecca..71c22a9f0 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -17,7 +17,8 @@ namespace CSMWorld enum Roles { Role_Flags = Qt::UserRole, - Role_Display = Qt::UserRole+1 + Role_Display = Qt::UserRole+1, + Role_ColumnId = Qt::UserRole+2 }; enum Flags diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index 300b57217..cbe0d74c4 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -870,7 +870,13 @@ namespace CSMWorld template struct CellColumn : public Column { - CellColumn() : Column (Columns::ColumnId_Cell, ColumnBase::Display_Cell) {} + bool mBlocked; + + /// \param blocked Do not allow user-modification + CellColumn (bool blocked = false) + : Column (Columns::ColumnId_Cell, ColumnBase::Display_Cell), + mBlocked (blocked) + {} virtual QVariant get (const Record& record) const { @@ -892,9 +898,41 @@ namespace CSMWorld } virtual bool isUserEditable() const + { + return !mBlocked; + } + }; + + template + struct OriginalCellColumn : public Column + { + OriginalCellColumn() + : Column (Columns::ColumnId_OriginalCell, ColumnBase::Display_Cell) + {} + + virtual QVariant get (const Record& record) const + { + return QString::fromUtf8 (record.get().mOriginalCell.c_str()); + } + + virtual void set (Record& record, const QVariant& data) + { + ESXRecordT record2 = record.get(); + + record2.mOriginalCell = data.toString().toUtf8().constData(); + + record.setModified (record2); + } + + virtual bool isEditable() const { return true; } + + virtual bool isUserEditable() const + { + return false; + } }; template diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 1ea7da647..3d735ddca 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -222,6 +222,7 @@ namespace CSMWorld { ColumnId_HitSound, "Hit Sound" }, { ColumnId_AreaSound, "Area Sound" }, { ColumnId_BoltSound, "Bolt Sound" }, + { ColumnId_OriginalCell, "Original Cell" }, { ColumnId_PathgridPoints, "Points" }, { ColumnId_PathgridIndex, "Index" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 5a1577ddb..0c525fd11 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -264,6 +264,8 @@ namespace CSMWorld ColumnId_InfoList = 240, + ColumnId_OriginalCell = 241, + // Allocated to a separate value range, so we don't get a collision should we ever need // to extend the number of use values. ColumnId_UseValue1 = 0x10000, diff --git a/apps/opencs/model/world/commanddispatcher.cpp b/apps/opencs/model/world/commanddispatcher.cpp index 199f91e29..2d3262487 100644 --- a/apps/opencs/model/world/commanddispatcher.cpp +++ b/apps/opencs/model/world/commanddispatcher.cpp @@ -2,6 +2,7 @@ #include "commanddispatcher.hpp" #include +#include #include @@ -10,6 +11,7 @@ #include "idtable.hpp" #include "record.hpp" #include "commands.hpp" +#include "idtableproxymodel.hpp" std::vector CSMWorld::CommandDispatcher::getDeletableRecords() const { @@ -131,6 +133,54 @@ std::vector CSMWorld::CommandDispatcher::getExtendedTypes return tables; } +void CSMWorld::CommandDispatcher::executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_) +{ + if (mLocked) + return; + + std::auto_ptr modifyCell; + + int columnId = model->data (index, ColumnBase::Role_ColumnId).toInt(); + + if (columnId==Columns::ColumnId_PositionXPos || columnId==Columns::ColumnId_PositionYPos) + { + IdTableProxyModel *proxy = dynamic_cast (model); + + int row = proxy ? proxy->mapToSource (index).row() : index.row(); + + // This is not guaranteed to be the same as \a model, since a proxy could be used. + IdTable& model2 = dynamic_cast (*mDocument.getData().getTableModel (mId)); + + int cellColumn = model2.searchColumnIndex (Columns::ColumnId_Cell); + + if (cellColumn!=-1) + { + QModelIndex cellIndex = model2.index (row, cellColumn); + + std::string cellId = model2.data (cellIndex).toString().toUtf8().data(); + + if (cellId.find ('#')!=std::string::npos) + { + // Need to recalculate the cell + modifyCell.reset (new UpdateCellCommand (model2, row)); + } + } + } + + std::auto_ptr modifyData ( + new CSMWorld::ModifyCommand (*model, index, new_)); + + if (modifyCell.get()) + { + mDocument.getUndoStack().beginMacro (modifyData->text()); + mDocument.getUndoStack().push (modifyData.release()); + mDocument.getUndoStack().push (modifyCell.release()); + mDocument.getUndoStack().endMacro(); + } + else + mDocument.getUndoStack().push (modifyData.release()); +} + void CSMWorld::CommandDispatcher::executeDelete() { if (mLocked) diff --git a/apps/opencs/model/world/commanddispatcher.hpp b/apps/opencs/model/world/commanddispatcher.hpp index 50085b1a1..1d29e48c1 100644 --- a/apps/opencs/model/world/commanddispatcher.hpp +++ b/apps/opencs/model/world/commanddispatcher.hpp @@ -7,6 +7,9 @@ #include "universalid.hpp" +class QModelIndex; +class QAbstractItemModel; + namespace CSMDoc { class Document; @@ -53,6 +56,12 @@ namespace CSMWorld /// the extended mode, the returned vector will be empty instead. std::vector getExtendedTypes() const; + /// Add a modify command to the undo stack. + /// + /// \attention model must either be a model for the table operated on by this + /// dispatcher or a proxy of it. + void executeModify (QAbstractItemModel *model, const QModelIndex& index, const QVariant& new_); + public slots: void executeDelete(); diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp index 5a3190846..232959727 100644 --- a/apps/opencs/model/world/commands.cpp +++ b/apps/opencs/model/world/commands.cpp @@ -1,5 +1,8 @@ #include "commands.hpp" +#include +#include + #include #include @@ -183,6 +186,47 @@ void CSMWorld::CloneCommand::undo() mModel.removeRow (mModel.getModelIndex (mId, 0).row()); } + +CSMWorld::UpdateCellCommand::UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent) +: QUndoCommand (parent), mModel (model), mRow (row) +{ + setText ("Update cell ID"); +} + +void CSMWorld::UpdateCellCommand::redo() +{ + if (!mNew.isValid()) + { + int cellColumn = mModel.searchColumnIndex (Columns::ColumnId_Cell); + mIndex = mModel.index (mRow, cellColumn); + + const int cellSize = 8192; + + QModelIndex xIndex = mModel.index ( + mRow, mModel.findColumnIndex (Columns::ColumnId_PositionXPos)); + + QModelIndex yIndex = mModel.index ( + mRow, mModel.findColumnIndex (Columns::ColumnId_PositionYPos)); + + int x = std::floor (mModel.data (xIndex).toFloat() / cellSize); + int y = std::floor (mModel.data (yIndex).toFloat() / cellSize); + + std::ostringstream stream; + + stream << "#" << x << " " << y; + + mNew = QString::fromUtf8 (stream.str().c_str()); + } + + mModel.setData (mIndex, mNew); +} + +void CSMWorld::UpdateCellCommand::undo() +{ + mModel.setData (mIndex, mOld); +} + + CSMWorld::DeleteNestedCommand::DeleteNestedCommand (IdTree& model, const std::string& id, int nestedRow, diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp index 89ca5d1ff..f13376145 100644 --- a/apps/opencs/model/world/commands.hpp +++ b/apps/opencs/model/world/commands.hpp @@ -144,6 +144,29 @@ namespace CSMWorld virtual void undo(); }; + /// \brief Update cell ID according to x/y-coordinates + /// + /// \note The new value will be calculated in the first call to redo instead of the + /// constructor to accommodate multiple coordinate-affecting commands being executed + /// in a macro. + class UpdateCellCommand : public QUndoCommand + { + IdTable& mModel; + int mRow; + QModelIndex mIndex; + QVariant mNew; // invalid, if new cell ID has not been calculated yet + QVariant mOld; + + public: + + UpdateCellCommand (IdTable& model, int row, QUndoCommand *parent = 0); + + virtual void redo(); + + virtual void undo(); + }; + + class NestedTableStoring { NestedTableWrapperBase* mOld; diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 873d8b78a..fc4532fb0 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -368,7 +368,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mRefs.addColumn (new StringIdColumn (true)); mRefs.addColumn (new RecordStateColumn); mRefs.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Reference)); - mRefs.addColumn (new CellColumn); + mRefs.addColumn (new CellColumn (true)); + mRefs.addColumn (new OriginalCellColumn); mRefs.addColumn (new IdColumn); mRefs.addColumn (new PosColumn (&CellRef::mPos, 0, false)); mRefs.addColumn (new PosColumn (&CellRef::mPos, 1, false)); @@ -457,6 +458,8 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc UniversalId::Type_Texture); addModel (new ResourceTable (&mResourcesManager.get (UniversalId::Type_Videos)), UniversalId::Type_Video); + + mRefLoadCache.clear(); // clear here rather than startLoading() and continueLoading() for multiple content files } CSMWorld::Data::~Data() @@ -778,7 +781,6 @@ int CSMWorld::Data::startLoading (const boost::filesystem::path& path, bool base mReader = 0; mDialogue = 0; - mRefLoadCache.clear(); mReader = new ESM::ESMReader; mReader->setEncoder (&mEncoder); @@ -815,7 +817,6 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) mReader = 0; mDialogue = 0; - mRefLoadCache.clear(); return true; } @@ -860,9 +861,16 @@ bool CSMWorld::Data::continueLoading (CSMDoc::Messages& messages) case ESM::REC_CELL: { - mCells.load (*mReader, mBase); - std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (mCells.getSize()-1)); - mRefs.load (*mReader, mCells.getSize()-1, mBase, mRefLoadCache[cellId], messages); + int index = mCells.load (*mReader, mBase); + if (index < 0 || index >= mCells.getSize()) + { + // log an error and continue loading the refs to the last loaded cell + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_None); + messages.add (id, "Logic error: cell index out of bounds"); + index = mCells.getSize()-1; + } + std::string cellId = Misc::StringUtils::lowerCase (mCells.getId (index)); + mRefs.load (*mReader, index, mBase, mRefLoadCache[cellId], messages); break; } diff --git a/apps/opencs/model/world/idcollection.hpp b/apps/opencs/model/world/idcollection.hpp index f00ea447a..4eafc59bd 100644 --- a/apps/opencs/model/world/idcollection.hpp +++ b/apps/opencs/model/world/idcollection.hpp @@ -74,6 +74,15 @@ namespace CSMWorld { ESXRecordT record; + // Sometimes id (i.e. NAME of the cell) may be different to the id we stored + // earlier. e.g. NAME == "Vivec, Arena" but id == "#-4 11". Sometime NAME is + // missing altogether for scripts or cells. + // + // In such cases the returned index will be -1. We then try updating the + // IdAccessor's id manually (e.g. set mId of the record to "Vivec, Arena") + // and try getting the index once more after loading the record. The mId of the + // record would have changed to "#-4 11" after the load, and searchId() should find + // it (if this is a modify) int index = this->searchId (id); if (index==-1) diff --git a/apps/opencs/model/world/idtable.cpp b/apps/opencs/model/world/idtable.cpp index 2f0164c2d..a0dd0b47b 100644 --- a/apps/opencs/model/world/idtable.cpp +++ b/apps/opencs/model/world/idtable.cpp @@ -28,9 +28,15 @@ int CSMWorld::IdTable::columnCount (const QModelIndex & parent) const return mIdCollection->getColumns(); } -QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const +QVariant CSMWorld::IdTable::data (const QModelIndex & index, int role) const { - if ((role!=Qt::DisplayRole && role!=Qt::EditRole) || index.row() < 0 || index.column() < 0) + if (index.row() < 0 || index.column() < 0) + return QVariant(); + + if (role==ColumnBase::Role_ColumnId) + return QVariant (getColumnId (index.column())); + + if ((role!=Qt::DisplayRole && role!=Qt::EditRole)) return QVariant(); if (role==Qt::EditRole && !mIdCollection->getColumn (index.column()).isEditable()) @@ -56,6 +62,9 @@ QVariant CSMWorld::IdTable::headerData (int section, Qt::Orientation orientation if (role==ColumnBase::Role_Display) return mIdCollection->getColumn (section).mDisplayType; + if (role==ColumnBase::Role_ColumnId) + return getColumnId (section); + return QVariant(); } diff --git a/apps/opencs/model/world/ref.cpp b/apps/opencs/model/world/ref.cpp index eee454a16..13706c950 100644 --- a/apps/opencs/model/world/ref.cpp +++ b/apps/opencs/model/world/ref.cpp @@ -1,8 +1,18 @@ #include "ref.hpp" +#include + CSMWorld::CellRef::CellRef() { mRefNum.mIndex = 0; mRefNum.mContentFile = 0; } + +std::pair CSMWorld::CellRef::getCellIndex() const +{ + const int cellSize = 8192; + + return std::make_pair ( + std::floor (mPos.pos[0]/cellSize), std::floor (mPos.pos[1]/cellSize)); +} diff --git a/apps/opencs/model/world/ref.hpp b/apps/opencs/model/world/ref.hpp index 8ab901a6f..c60392221 100644 --- a/apps/opencs/model/world/ref.hpp +++ b/apps/opencs/model/world/ref.hpp @@ -1,6 +1,8 @@ #ifndef CSM_WOLRD_REF_H #define CSM_WOLRD_REF_H +#include + #include namespace CSMWorld @@ -10,8 +12,12 @@ namespace CSMWorld { std::string mId; std::string mCell; + std::string mOriginalCell; CellRef(); + + /// Calculate cell index based on coordinates (x and y) + std::pair getCellIndex() const; }; } diff --git a/apps/opencs/model/world/refcollection.cpp b/apps/opencs/model/world/refcollection.cpp index 47f0276c6..ff30dafae 100644 --- a/apps/opencs/model/world/refcollection.cpp +++ b/apps/opencs/model/world/refcollection.cpp @@ -2,8 +2,10 @@ #include "refcollection.hpp" #include +#include #include +#include #include "ref.hpp" #include "cell.hpp" @@ -20,14 +22,75 @@ void CSMWorld::RefCollection::load (ESM::ESMReader& reader, int cellIndex, bool CellRef ref; bool deleted = false; + ESM::MovedCellRef mref; - while (ESM::Cell::getNextRef (reader, ref, deleted)) + // hack to initialise mindex + while (!(mref.mRefNum.mIndex = 0) && ESM::Cell::getNextRef(reader, ref, deleted, true, &mref)) { - ref.mCell = cell2.mId; + // Keep mOriginalCell empty when in modified (as an indicator that the + // original cell will always be equal the current cell). + ref.mOriginalCell = base ? cell2.mId : ""; - /// \todo handle moved references + if (cell.get().isExterior()) + { + // ignoring moved references sub-record; instead calculate cell from coordinates + std::pair index = ref.getCellIndex(); + + std::ostringstream stream; + stream << "#" << index.first << " " << index.second; + + ref.mCell = stream.str(); + + if (!base && // don't try to update base records + mref.mRefNum.mIndex != 0) // MVRF tag found + { + // there is a requirement for a placeholder where the original object was + // + // see the forum discussions here for more details: + // https://forum.openmw.org/viewtopic.php?f=6&t=577&start=30 + ref.mOriginalCell = cell2.mId; + + if (deleted) + { + // FIXME: how to mark the record deleted? + CSMWorld::UniversalId id (CSMWorld::UniversalId::Type_Cell, + mCells.getId (cellIndex)); + + messages.add (id, "Moved reference "+ref.mRefID+" is in DELE state"); + + continue; + } + + // It is not always possibe to ignore moved references sub-record and + // calculate from coordinates. Some mods may place the ref in positions + // outside normal bounds, resulting in non sensical cell id's. This often + // happens if the moved ref was deleted. + // + // Use the target cell from the MVRF tag but if different output an error + // message + if (index.first != mref.mTarget[0] || index.second != mref.mTarget[1]) + { + std::cerr << "The Position of moved ref " + << ref.mRefID << " does not match the target cell" << std::endl; + std::cerr << "Position: #" << index.first << " " << index.second + <<", Target #"<< mref.mTarget[0] << " " << mref.mTarget[1] << std::endl; + + std::ostringstream stream; + stream << "#" << mref.mTarget[0] << " " << mref.mTarget[1]; + ref.mCell = stream.str(); // overwrite + } + } + } + else + ref.mCell = cell2.mId; - std::map::iterator iter = cache.find (ref.mRefNum); + // ignore content file number + std::map::iterator iter = cache.begin(); + for (; iter != cache.end(); ++iter) + { + if (ref.mRefNum.mIndex == iter->first.mIndex) + break; + } if (deleted) { diff --git a/apps/opencs/model/world/refcollection.hpp b/apps/opencs/model/world/refcollection.hpp index 46572752e..d031398d3 100644 --- a/apps/opencs/model/world/refcollection.hpp +++ b/apps/opencs/model/world/refcollection.hpp @@ -27,8 +27,7 @@ namespace CSMWorld {} void load (ESM::ESMReader& reader, int cellIndex, bool base, - std::map& cache, - CSMDoc::Messages& messages); + std::map& cache, CSMDoc::Messages& messages); ///< Load a sequence of references. std::string getNewId(); diff --git a/apps/opencs/view/tools/reporttable.cpp b/apps/opencs/view/tools/reporttable.cpp index 1b07d3c0f..7cfe8e4f0 100644 --- a/apps/opencs/view/tools/reporttable.cpp +++ b/apps/opencs/view/tools/reporttable.cpp @@ -131,8 +131,8 @@ CSVTools::ReportTable::ReportTable (CSMDoc::Document& document, setModel (mModel); setColumnHidden (2, true); - mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate ( - document, this); + mIdTypeDelegate = CSVWorld::IdTypeDelegateFactory().makeDelegate (0, + mDocument, this); setItemDelegateForColumn (0, mIdTypeDelegate); diff --git a/apps/opencs/view/world/datadisplaydelegate.cpp b/apps/opencs/view/world/datadisplaydelegate.cpp index 46ca17a29..b9df52bf7 100644 --- a/apps/opencs/view/world/datadisplaydelegate.cpp +++ b/apps/opencs/view/world/datadisplaydelegate.cpp @@ -6,11 +6,12 @@ CSVWorld::DataDisplayDelegate::DataDisplayDelegate(const ValueList &values, const IconList &icons, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, const QString &pageName, const QString &settingName, QObject *parent) - : EnumDelegate (values, document, parent), mDisplayMode (Mode_TextOnly), + : EnumDelegate (values, dispatcher, document, parent), mDisplayMode (Mode_TextOnly), mIcons (icons), mIconSize (QSize(16, 16)), mIconLeftOffset(3), mTextLeftOffset(8), mSettingKey (pageName + '/' + settingName) { @@ -136,9 +137,9 @@ void CSVWorld::DataDisplayDelegateFactory::add (int enumValue, QString enumName, } CSVWorld::CommandDelegate *CSVWorld::DataDisplayDelegateFactory::makeDelegate ( - CSMDoc::Document& document, QObject *parent) const + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { - return new DataDisplayDelegate (mValues, mIcons, document, "", "", parent); + return new DataDisplayDelegate (mValues, mIcons, dispatcher, document, "", "", parent); } diff --git a/apps/opencs/view/world/datadisplaydelegate.hpp b/apps/opencs/view/world/datadisplaydelegate.hpp index 73790e3c6..f6e4c2688 100755 --- a/apps/opencs/view/world/datadisplaydelegate.hpp +++ b/apps/opencs/view/world/datadisplaydelegate.hpp @@ -38,12 +38,9 @@ namespace CSVWorld QString mSettingKey; public: - explicit DataDisplayDelegate (const ValueList & values, - const IconList & icons, - CSMDoc::Document& document, - const QString &pageName, - const QString &settingName, - QObject *parent); + DataDisplayDelegate (const ValueList & values, const IconList & icons, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, + const QString &pageName, const QString &settingName, QObject *parent); ~DataDisplayDelegate(); @@ -82,7 +79,7 @@ namespace CSVWorld public: - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. protected: diff --git a/apps/opencs/view/world/dialoguesubview.cpp b/apps/opencs/view/world/dialoguesubview.cpp index f067c3225..cf3653c1b 100644 --- a/apps/opencs/view/world/dialoguesubview.cpp +++ b/apps/opencs/view/world/dialoguesubview.cpp @@ -177,10 +177,11 @@ void CSVWorld::DialogueDelegateDispatcherProxy::tableMimeDataDropped(const std:: */ CSVWorld::DialogueDelegateDispatcher::DialogueDelegateDispatcher(QObject* parent, - CSMWorld::IdTable* table, CSMDoc::Document& document, QAbstractItemModel *model) : + CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, + CSMDoc::Document& document, QAbstractItemModel *model) : mParent(parent), mTable(model ? model : table), -mDocument (document), +mCommandDispatcher (commandDispatcher), mDocument (document), mNotEditableDelegate(table, parent) { } @@ -192,7 +193,7 @@ CSVWorld::CommandDelegate* CSVWorld::DialogueDelegateDispatcher::makeDelegate(CS if (delegateIt == mDelegates.end()) { delegate = CommandDelegateFactoryCollection::get().makeDelegate ( - display, mDocument, mParent); + display, &mCommandDispatcher, mDocument, mParent); mDelegates.insert(std::make_pair(display, delegate)); } else { @@ -352,13 +353,16 @@ CSVWorld::EditWidget::~EditWidget() delete mNestedTableDispatcher; } -CSVWorld::EditWidget::EditWidget(QWidget *parent, int row, CSMWorld::IdTable* table, CSMDoc::Document& document, bool createAndDelete) : -mDispatcher(this, table, document), +CSVWorld::EditWidget::EditWidget(QWidget *parent, + int row, CSMWorld::IdTable* table, CSMWorld::CommandDispatcher& commandDispatcher, + CSMDoc::Document& document, bool createAndDelete) : +mDispatcher(this, table, commandDispatcher, document), mNestedTableDispatcher(NULL), QScrollArea(parent), mWidgetMapper(NULL), mNestedTableMapper(NULL), mMainWidget(NULL), +mCommandDispatcher (commandDispatcher), mDocument (document), mTable(table) { @@ -442,7 +446,14 @@ void CSVWorld::EditWidget::remake(int row) { mNestedModels.push_back(new CSMWorld::NestedTableProxyModel (mTable->index(row, i), display, dynamic_cast(mTable))); - NestedTable* table = new NestedTable(mDocument, mNestedModels.back(), this); + int idColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_Id); + int typeColumn = mTable->findColumnIndex (CSMWorld::Columns::ColumnId_RecordType); + + CSMWorld::UniversalId id = CSMWorld::UniversalId( + static_cast (mTable->data (mTable->index (row, typeColumn)).toInt()), + mTable->data (mTable->index (row, idColumn)).toString().toUtf8().constData()); + + NestedTable* table = new NestedTable(mDocument, id, mNestedModels.back(), this); // FIXME: does not work well when enum delegates are used //table->resizeColumnsToContents(); table->setEditTriggers(QAbstractItemView::SelectedClicked | QAbstractItemView::CurrentChanged); @@ -499,7 +510,7 @@ void CSVWorld::EditWidget::remake(int row) mNestedTableMapper->setModel(mNestedModels.back()); // FIXME: lack MIME support? mNestedTableDispatcher = - new DialogueDelegateDispatcher (this, mTable, mDocument, mNestedModels.back()); + new DialogueDelegateDispatcher (this, mTable, mCommandDispatcher, mDocument, mNestedModels.back()); mNestedTableMapper->setItemDelegate(mNestedTableDispatcher); int columnCount = @@ -626,7 +637,8 @@ CSVWorld::DialogueSubView::DialogueSubView (const CSMWorld::UniversalId& id, CSM mMainLayout = new QVBoxLayout(mainWidget); - mEditWidget = new EditWidget(mainWidget, mTable->getModelIndex(mCurrentId, 0).row(), mTable, document, false); + mEditWidget = new EditWidget(mainWidget, + mTable->getModelIndex(mCurrentId, 0).row(), mTable, mCommandDispatcher, document, false); connect(mEditWidget, SIGNAL(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*)), this, SLOT(tableMimeDataDropped(QWidget*, const QModelIndex&, const CSMWorld::UniversalId&, const CSMDoc::Document*))); diff --git a/apps/opencs/view/world/dialoguesubview.hpp b/apps/opencs/view/world/dialoguesubview.hpp index 067ff95f5..b5a44d266 100644 --- a/apps/opencs/view/world/dialoguesubview.hpp +++ b/apps/opencs/view/world/dialoguesubview.hpp @@ -109,6 +109,7 @@ namespace CSVWorld QAbstractItemModel* mTable; + CSMWorld::CommandDispatcher& mCommandDispatcher; CSMDoc::Document& mDocument; NotEditableSubDelegate mNotEditableDelegate; @@ -119,6 +120,7 @@ namespace CSVWorld public: DialogueDelegateDispatcher(QObject* parent, CSMWorld::IdTable* table, + CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, QAbstractItemModel* model = 0); @@ -167,12 +169,14 @@ namespace CSVWorld DialogueDelegateDispatcher *mNestedTableDispatcher; QWidget* mMainWidget; CSMWorld::IdTable* mTable; + CSMWorld::CommandDispatcher& mCommandDispatcher; CSMDoc::Document& mDocument; std::vector mNestedModels; //Plain, raw C pointers, deleted in the dtor public: EditWidget (QWidget *parent, int row, CSMWorld::IdTable* table, + CSMWorld::CommandDispatcher& commandDispatcher, CSMDoc::Document& document, bool createAndDelete = false); virtual ~EditWidget(); diff --git a/apps/opencs/view/world/enumdelegate.cpp b/apps/opencs/view/world/enumdelegate.cpp index eb523f781..4b76bf9d6 100644 --- a/apps/opencs/view/world/enumdelegate.cpp +++ b/apps/opencs/view/world/enumdelegate.cpp @@ -37,8 +37,8 @@ void CSVWorld::EnumDelegate::addCommands (QAbstractItemModel *model, CSVWorld::EnumDelegate::EnumDelegate (const std::vector >& values, - CSMDoc::Document& document, QObject *parent) -: CommandDelegate (document, parent), mValues (values) + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) +: CommandDelegate (dispatcher, document, parent), mValues (values) { } @@ -142,9 +142,9 @@ CSVWorld::EnumDelegateFactory::EnumDelegateFactory (const std::vector >& values, - CSMDoc::Document& document, QObject *parent); + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); virtual QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem& option, @@ -64,7 +64,7 @@ namespace CSVWorld EnumDelegateFactory (const std::vector& names, bool allowNone = false); /// \param allowNone Use value of -1 for "none selected" (empty string) - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. void add (int value, const QString& name); diff --git a/apps/opencs/view/world/idtypedelegate.cpp b/apps/opencs/view/world/idtypedelegate.cpp index 3b440ff71..34c8d12cd 100755 --- a/apps/opencs/view/world/idtypedelegate.cpp +++ b/apps/opencs/view/world/idtypedelegate.cpp @@ -3,8 +3,8 @@ #include "../../model/world/universalid.hpp" CSVWorld::IdTypeDelegate::IdTypeDelegate - (const ValueList &values, const IconList &icons, CSMDoc::Document& document, QObject *parent) - : DataDisplayDelegate (values, icons, document, + (const ValueList &values, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) + : DataDisplayDelegate (values, icons, dispatcher, document, "records", "type-format", parent) {} @@ -21,7 +21,7 @@ CSVWorld::IdTypeDelegateFactory::IdTypeDelegateFactory() } CSVWorld::CommandDelegate *CSVWorld::IdTypeDelegateFactory::makeDelegate ( - CSMDoc::Document& document, QObject *parent) const + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { - return new IdTypeDelegate (mValues, mIcons, document, parent); + return new IdTypeDelegate (mValues, mIcons, dispatcher, document, parent); } diff --git a/apps/opencs/view/world/idtypedelegate.hpp b/apps/opencs/view/world/idtypedelegate.hpp index e9a0af68c..d0ed6997b 100755 --- a/apps/opencs/view/world/idtypedelegate.hpp +++ b/apps/opencs/view/world/idtypedelegate.hpp @@ -11,7 +11,7 @@ namespace CSVWorld class IdTypeDelegate : public DataDisplayDelegate { public: - IdTypeDelegate (const ValueList &mValues, const IconList &icons, CSMDoc::Document& document, QObject *parent); + IdTypeDelegate (const ValueList &mValues, const IconList &icons, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); }; class IdTypeDelegateFactory : public DataDisplayDelegateFactory @@ -20,7 +20,7 @@ namespace CSVWorld IdTypeDelegateFactory(); - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; } diff --git a/apps/opencs/view/world/nestedtable.cpp b/apps/opencs/view/world/nestedtable.cpp index 1597c81a3..5c8762020 100644 --- a/apps/opencs/view/world/nestedtable.cpp +++ b/apps/opencs/view/world/nestedtable.cpp @@ -2,6 +2,7 @@ #include "../../model/world/nestedtableproxymodel.hpp" #include "../../model/world/universalid.hpp" #include "../../model/world/commands.hpp" +#include "../../model/world/commanddispatcher.hpp" #include "util.hpp" #include @@ -10,12 +11,14 @@ #include CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, + CSMWorld::UniversalId id, CSMWorld::NestedTableProxyModel* model, QWidget* parent) : QTableView(parent), mUndoStack(document.getUndoStack()), mModel(model) { + mDispatcher = new CSMWorld::CommandDispatcher (document, id, this); setSelectionBehavior (QAbstractItemView::SelectRows); setSelectionMode (QAbstractItemView::ExtendedSelection); @@ -31,6 +34,7 @@ CSVWorld::NestedTable::NestedTable(CSMDoc::Document& document, model->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate(display, + mDispatcher, document, this); diff --git a/apps/opencs/view/world/nestedtable.hpp b/apps/opencs/view/world/nestedtable.hpp index f41ba4345..b8e91844c 100644 --- a/apps/opencs/view/world/nestedtable.hpp +++ b/apps/opencs/view/world/nestedtable.hpp @@ -12,6 +12,7 @@ namespace CSMWorld { class NestedTableProxyModel; class UniversalId; + class CommandDispatcher; } namespace CSMDoc @@ -29,9 +30,11 @@ namespace CSVWorld QAction *mRemoveRowAction; QUndoStack& mUndoStack; CSMWorld::NestedTableProxyModel* mModel; + CSMWorld::CommandDispatcher *mDispatcher; public: NestedTable(CSMDoc::Document& document, + CSMWorld::UniversalId id, CSMWorld::NestedTableProxyModel* model, QWidget* parent = NULL); diff --git a/apps/opencs/view/world/recordstatusdelegate.cpp b/apps/opencs/view/world/recordstatusdelegate.cpp index 708a78015..58a5c0177 100644 --- a/apps/opencs/view/world/recordstatusdelegate.cpp +++ b/apps/opencs/view/world/recordstatusdelegate.cpp @@ -9,16 +9,16 @@ CSVWorld::RecordStatusDelegate::RecordStatusDelegate(const ValueList& values, const IconList & icons, - CSMDoc::Document& document, QObject *parent) - : DataDisplayDelegate (values, icons, document, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) + : DataDisplayDelegate (values, icons, dispatcher, document, "records", "status-format", parent) {} CSVWorld::CommandDelegate *CSVWorld::RecordStatusDelegateFactory::makeDelegate ( - CSMDoc::Document& document, QObject *parent) const + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { - return new RecordStatusDelegate (mValues, mIcons, document, parent); + return new RecordStatusDelegate (mValues, mIcons, dispatcher, document, parent); } CSVWorld::RecordStatusDelegateFactory::RecordStatusDelegateFactory() diff --git a/apps/opencs/view/world/recordstatusdelegate.hpp b/apps/opencs/view/world/recordstatusdelegate.hpp index fbdaed538..acaf872a6 100644 --- a/apps/opencs/view/world/recordstatusdelegate.hpp +++ b/apps/opencs/view/world/recordstatusdelegate.hpp @@ -17,9 +17,9 @@ namespace CSVWorld { public: - explicit RecordStatusDelegate(const ValueList& values, - const IconList& icons, - CSMDoc::Document& document, QObject *parent = 0); + RecordStatusDelegate (const ValueList& values, const IconList& icons, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, + QObject *parent = 0); }; class RecordStatusDelegateFactory : public DataDisplayDelegateFactory @@ -28,7 +28,7 @@ namespace CSVWorld RecordStatusDelegateFactory(); - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; diff --git a/apps/opencs/view/world/table.cpp b/apps/opencs/view/world/table.cpp index 97a3bc2e3..5a650f98a 100644 --- a/apps/opencs/view/world/table.cpp +++ b/apps/opencs/view/world/table.cpp @@ -281,7 +281,7 @@ CSVWorld::Table::Table (const CSMWorld::UniversalId& id, mModel->headerData (i, Qt::Horizontal, CSMWorld::ColumnBase::Role_Display).toInt()); CommandDelegate *delegate = CommandDelegateFactoryCollection::get().makeDelegate (display, - mDocument, this); + mDispatcher, document, this); mDelegates.push_back (delegate); setItemDelegateForColumn (i, delegate); diff --git a/apps/opencs/view/world/util.cpp b/apps/opencs/view/world/util.cpp index 5694c6e5a..a11b5bdde 100644 --- a/apps/opencs/view/world/util.cpp +++ b/apps/opencs/view/world/util.cpp @@ -18,6 +18,7 @@ #include "../../model/world/commands.hpp" #include "../../model/world/tablemimedata.hpp" +#include "../../model/world/commanddispatcher.hpp" #include "scriptedit.hpp" @@ -82,15 +83,15 @@ void CSVWorld::CommandDelegateFactoryCollection::add (CSMWorld::ColumnBase::Disp } CSVWorld::CommandDelegate *CSVWorld::CommandDelegateFactoryCollection::makeDelegate ( - CSMWorld::ColumnBase::Display display, CSMDoc::Document& document, QObject *parent) const + CSMWorld::ColumnBase::Display display, CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { std::map::const_iterator iter = mFactories.find (display); if (iter!=mFactories.end()) - return iter->second->makeDelegate (document, parent); + return iter->second->makeDelegate (dispatcher, document, parent); - return new CommandDelegate (document, parent); + return new CommandDelegate (dispatcher, document, parent); } const CSVWorld::CommandDelegateFactoryCollection& CSVWorld::CommandDelegateFactoryCollection::get() @@ -115,17 +116,22 @@ CSMDoc::Document& CSVWorld::CommandDelegate::getDocument() const void CSVWorld::CommandDelegate::setModelDataImp (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const { + if (!mCommandDispatcher) + return; + NastyTableModelHack hack (*model); QStyledItemDelegate::setModelData (editor, &hack, index); QVariant new_ = hack.getData(); if ((model->data (index)!=new_) && (model->flags(index) & Qt::ItemIsEditable)) - getUndoStack().push (new CSMWorld::ModifyCommand (*model, index, new_)); + mCommandDispatcher->executeModify (model, index, new_); } -CSVWorld::CommandDelegate::CommandDelegate (CSMDoc::Document& document, QObject *parent) -: QStyledItemDelegate (parent), mDocument (document), mEditLock (false) +CSVWorld::CommandDelegate::CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher, + CSMDoc::Document& document, QObject *parent) +: QStyledItemDelegate (parent), mEditLock (false), + mCommandDispatcher (commandDispatcher), mDocument (document) {} void CSVWorld::CommandDelegate::setModelData (QWidget *editor, QAbstractItemModel *model, diff --git a/apps/opencs/view/world/util.hpp b/apps/opencs/view/world/util.hpp index 10011798d..a12e6ae36 100644 --- a/apps/opencs/view/world/util.hpp +++ b/apps/opencs/view/world/util.hpp @@ -16,6 +16,7 @@ namespace CSMWorld { class TableMimeData; class UniversalId; + class CommandDispatcher; } namespace CSVWorld @@ -51,7 +52,8 @@ namespace CSVWorld virtual ~CommandDelegateFactory(); - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, QObject *parent) const = 0; ///< The ownership of the returned CommandDelegate is transferred to the caller. }; @@ -78,7 +80,8 @@ namespace CSVWorld /// /// This function must not be called more than once per value of \a display. - CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, CSMDoc::Document& document, + CommandDelegate *makeDelegate (CSMWorld::ColumnBase::Display display, + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. /// @@ -111,8 +114,9 @@ namespace CSVWorld { Q_OBJECT - CSMDoc::Document& mDocument; bool mEditLock; + CSMWorld::CommandDispatcher *mCommandDispatcher; + CSMDoc::Document& mDocument; protected: @@ -125,7 +129,9 @@ namespace CSVWorld public: - CommandDelegate (CSMDoc::Document& document, QObject *parent); + /// \param commandDispatcher If CommandDelegate will be only be used on read-only + /// cells, a 0-pointer can be passed here. + CommandDelegate (CSMWorld::CommandDispatcher *commandDispatcher, CSMDoc::Document& document, QObject *parent); virtual void setModelData (QWidget *editor, QAbstractItemModel *model, const QModelIndex& index) const; diff --git a/apps/opencs/view/world/vartypedelegate.cpp b/apps/opencs/view/world/vartypedelegate.cpp index c3c98b800..90a686a67 100644 --- a/apps/opencs/view/world/vartypedelegate.cpp +++ b/apps/opencs/view/world/vartypedelegate.cpp @@ -47,8 +47,8 @@ void CSVWorld::VarTypeDelegate::addCommands (QAbstractItemModel *model, const QM } CSVWorld::VarTypeDelegate::VarTypeDelegate (const std::vector >& values, - CSMDoc::Document& document, QObject *parent) -: EnumDelegate (values, document, parent) + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) +: EnumDelegate (values, dispatcher, document, parent) {} @@ -69,9 +69,9 @@ CSVWorld::VarTypeDelegateFactory::VarTypeDelegateFactory (ESM::VarType type0, } CSVWorld::CommandDelegate *CSVWorld::VarTypeDelegateFactory::makeDelegate ( - CSMDoc::Document& document, QObject *parent) const + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent) const { - return new VarTypeDelegate (mValues, document, parent); + return new VarTypeDelegate (mValues, dispatcher, document, parent); } void CSVWorld::VarTypeDelegateFactory::add (ESM::VarType type) diff --git a/apps/opencs/view/world/vartypedelegate.hpp b/apps/opencs/view/world/vartypedelegate.hpp index c86b936f6..a8f39c318 100644 --- a/apps/opencs/view/world/vartypedelegate.hpp +++ b/apps/opencs/view/world/vartypedelegate.hpp @@ -17,7 +17,7 @@ namespace CSVWorld public: VarTypeDelegate (const std::vector >& values, - CSMDoc::Document& document, QObject *parent); + CSMWorld::CommandDispatcher *dispatcher, CSMDoc::Document& document, QObject *parent); }; class VarTypeDelegateFactory : public CommandDelegateFactory @@ -30,7 +30,8 @@ namespace CSVWorld ESM::VarType type1 = ESM::VT_Unknown, ESM::VarType type2 = ESM::VT_Unknown, ESM::VarType type3 = ESM::VT_Unknown); - virtual CommandDelegate *makeDelegate (CSMDoc::Document& document, QObject *parent) const; + virtual CommandDelegate *makeDelegate (CSMWorld::CommandDispatcher *dispatcher, + CSMDoc::Document& document, QObject *parent) const; ///< The ownership of the returned CommandDelegate is transferred to the caller. void add (ESM::VarType type); diff --git a/components/esm/cellref.cpp b/components/esm/cellref.cpp index 0dd2987b5..ef33f495f 100644 --- a/components/esm/cellref.cpp +++ b/components/esm/cellref.cpp @@ -4,13 +4,34 @@ #include "esmreader.hpp" #include "esmwriter.hpp" -void ESM::CellRef::load (ESMReader& esm, bool wideRefNum) +void ESM::RefNum::load (ESMReader& esm, bool wide) { - loadId(esm, wideRefNum); + if (wide) + esm.getHNT (*this, "FRMR", 8); + else + esm.getHNT (mIndex, "FRMR"); +} + +void ESM::RefNum::save (ESMWriter &esm, bool wide, const std::string& tag) const +{ + if (wide) + esm.writeHNT (tag, *this, 8); + else + { + int refNum = (mIndex & 0xffffff) | ((hasContentFile() ? mContentFile : 0xff)<<24); + + esm.writeHNT (tag, refNum, 4); + } +} + + +void ESM::CellRef::load (ESMReader& esm, bool wideRefNum, bool ignoreRefNum) +{ + loadId(esm, wideRefNum, ignoreRefNum); loadData(esm); } -void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum) +void ESM::CellRef::loadId (ESMReader& esm, bool wideRefNum, bool ignoreRefNum) { // According to Hrnchamd, this does not belong to the actual ref. Instead, it is a marker indicating that // the following refs are part of a "temp refs" section. A temp ref is not being tracked by the moved references system. @@ -19,10 +40,8 @@ void ESM::CellRef::loadId(ESMReader &esm, bool wideRefNum) if (esm.isNextSub ("NAM0")) esm.skipHSub(); - if (wideRefNum) - esm.getHNT (mRefNum, "FRMR", 8); - else - esm.getHNT (mRefNum.mIndex, "FRMR"); + if (!ignoreRefNum) + mRefNum.load (esm, wideRefNum); mRefID = esm.getHNString ("NAME"); } @@ -83,10 +102,7 @@ void ESM::CellRef::loadData(ESMReader &esm) void ESM::CellRef::save (ESMWriter &esm, bool wideRefNum, bool inInventory) const { - if (wideRefNum) - esm.writeHNT ("FRMR", mRefNum, 8); - else - esm.writeHNT ("FRMR", mRefNum.mIndex, 4); + mRefNum.save (esm, wideRefNum); esm.writeHNCString("NAME", mRefID); diff --git a/components/esm/cellref.hpp b/components/esm/cellref.hpp index 01b6a8473..0fb449e16 100644 --- a/components/esm/cellref.hpp +++ b/components/esm/cellref.hpp @@ -16,6 +16,10 @@ namespace ESM unsigned int mIndex; int mContentFile; + void load (ESMReader& esm, bool wide = false); + + void save (ESMWriter &esm, bool wide = false, const std::string& tag = "FRMR") const; + enum { RefNum_NoContentFile = -1 }; inline bool hasContentFile() const { return mContentFile != RefNum_NoContentFile; } inline void unset() { mIndex = 0; mContentFile = RefNum_NoContentFile; } @@ -96,9 +100,9 @@ namespace ESM Position mPos; /// Calls loadId and loadData - void load (ESMReader& esm, bool wideRefNum = false); + void load (ESMReader& esm, bool wideRefNum = false, bool ignoreRefNum = false); - void loadId (ESMReader& esm, bool wideRefNum = false); + void loadId (ESMReader& esm, bool wideRefNum = false, bool ignoreRefNum = false); /// Implicitly called by load void loadData (ESMReader& esm); diff --git a/components/esm/loadcell.cpp b/components/esm/loadcell.cpp index e4f847dec..0f8897c48 100644 --- a/components/esm/loadcell.cpp +++ b/components/esm/loadcell.cpp @@ -168,7 +168,7 @@ std::string Cell::getDescription() const } } -bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) +bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted, bool ignoreMoves, MovedCellRef *mref) { // TODO: Try and document reference numbering, I don't think this has been done anywhere else. if (!esm.hasMoreSubs()) @@ -176,10 +176,20 @@ bool Cell::getNextRef(ESMReader &esm, CellRef &ref, bool& deleted) // NOTE: We should not need this check. It is a safety check until we have checked // more plugins, and how they treat these moved references. - if (esm.isNextSub("MVRF")) { - // skip rest of cell record (moved references), they are handled elsewhere - esm.skipRecord(); // skip MVRF, CNDT - return false; + if (esm.isNextSub("MVRF")) + { + if (ignoreMoves) + { + esm.getHT (mref->mRefNum.mIndex); + esm.getHNOT (mref->mTarget, "CNDT"); + adjustRefNum (mref->mRefNum, esm); + } + else + { + // skip rest of cell record (moved references), they are handled elsewhere + esm.skipRecord(); // skip MVRF, CNDT + return false; + } } ref.load (esm); diff --git a/components/esm/loadcell.hpp b/components/esm/loadcell.hpp index d982bbfd4..b78d4075a 100644 --- a/components/esm/loadcell.hpp +++ b/components/esm/loadcell.hpp @@ -156,7 +156,9 @@ struct Cell All fields of the CellRef struct are overwritten. You can safely reuse one memory location without blanking it between calls. */ - static bool getNextRef(ESMReader &esm, CellRef &ref, bool& deleted); + /// \param ignoreMoves ignore MVRF record and read reference like a regular CellRef. + static bool getNextRef(ESMReader &esm, + CellRef &ref, bool& deleted, bool ignoreMoves = false, MovedCellRef *mref = 0); /* This fetches an MVRF record, which is used to track moved references. * Since they are comparably rare, we use a separate method for this.