From 05210d7f2133387cdd8b581c1ea30dba3b15449a Mon Sep 17 00:00:00 2001 From: cc9cii Date: Thu, 9 Apr 2015 19:29:03 +1000 Subject: [PATCH] Nested table support for Pathgrids. --- apps/opencs/model/world/collection.hpp | 8 + apps/opencs/model/world/columnbase.cpp | 1 - apps/opencs/model/world/columnbase.hpp | 2 + apps/opencs/model/world/columnimp.hpp | 99 ++++++++- apps/opencs/model/world/columns.cpp | 8 + apps/opencs/model/world/columns.hpp | 9 + apps/opencs/model/world/data.cpp | 19 +- apps/opencs/model/world/idadapter.hpp | 39 ++++ apps/opencs/model/world/idadapterimp.hpp | 202 ++++++++++++++++++ apps/opencs/model/world/subcellcollection.hpp | 122 ++++++++++- 10 files changed, 503 insertions(+), 6 deletions(-) create mode 100644 apps/opencs/model/world/idadapter.hpp create mode 100644 apps/opencs/model/world/idadapterimp.hpp diff --git a/apps/opencs/model/world/collection.hpp b/apps/opencs/model/world/collection.hpp index dc52ae663..4bc9632a8 100644 --- a/apps/opencs/model/world/collection.hpp +++ b/apps/opencs/model/world/collection.hpp @@ -120,6 +120,8 @@ namespace CSMWorld virtual const Record& getRecord (int index) const; + virtual Record& getRecord (int index); + virtual int getAppendIndex (const std::string& id, UniversalId::Type type = UniversalId::Type_None) const; ///< \param type Will be ignored, unless the collection supports multiple record types @@ -433,6 +435,12 @@ namespace CSMWorld return mRecords.at (index); } + template + Record& Collection::getRecord (int index) + { + return mRecords.at (index); + } + template void Collection::insertRecord (const RecordBase& record, int index, UniversalId::Type type) diff --git a/apps/opencs/model/world/columnbase.cpp b/apps/opencs/model/world/columnbase.cpp index 94c5afb83..e4d2195be 100644 --- a/apps/opencs/model/world/columnbase.cpp +++ b/apps/opencs/model/world/columnbase.cpp @@ -1,4 +1,3 @@ - #include "columnbase.hpp" #include "columns.hpp" diff --git a/apps/opencs/model/world/columnbase.hpp b/apps/opencs/model/world/columnbase.hpp index da1d8f0df..aef68fdbd 100644 --- a/apps/opencs/model/world/columnbase.hpp +++ b/apps/opencs/model/world/columnbase.hpp @@ -97,6 +97,8 @@ namespace CSMWorld Display_NestedItemList, Display_NestedSpellList, Display_NestedDestinationsList, + Display_PathgridPointList, + Display_PathgridEdgeList, Display_EnchantmentType, Display_BodyPartType, diff --git a/apps/opencs/model/world/columnimp.hpp b/apps/opencs/model/world/columnimp.hpp index da14bb495..27fc3ad04 100644 --- a/apps/opencs/model/world/columnimp.hpp +++ b/apps/opencs/model/world/columnimp.hpp @@ -1277,7 +1277,6 @@ namespace CSMWorld } }; - template struct PosColumn : public Column { @@ -2265,6 +2264,104 @@ namespace CSMWorld return true; } }; + + template + struct PathgridPointListColumn : public Column + { + PathgridPointListColumn () + : Column (Columns::ColumnId_PathgridPoints, + ColumnBase::Display_PathgridPointList, ColumnBase::Flag_Dialogue) + { + } + + virtual QVariant get (const Record& record) const + { + return true; // required by IdTree::hasChildren() + } + + virtual void set (Record& record, const QVariant& data) + { + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct PathgridPointColumn : public Column + { + int mIndex; // 0=PosX, 1=PosY, 2=PosZ + + PathgridPointColumn(int index) + : Column (Columns::ColumnId_PathgridPosX+index, ColumnBase::Display_Integer), mIndex(index) + {} + + virtual QVariant get (const Record& record) const + { + return QVariant(); // FIXME + } + + virtual void set (Record& record, const QVariant& data) + { + } + + virtual bool isEditable() const + { + return true; + } + }; + + template + struct PathgridEdgeListColumn : public Column + { + PathgridEdgeListColumn () + : Column (Columns::ColumnId_PathgridEdges, + ColumnBase::Display_PathgridEdgeList, ColumnBase::Flag_Dialogue) + { + } + + virtual QVariant get (const Record& record) const + { + return true; // required by IdTree::hasChildren() + } + + virtual void set (Record& record, const QVariant& data) + { + } + + virtual bool isEditable() const + { + return true; + } + + }; + + template + struct PathgridEdgeColumn : public Column + { + int mIndex; + + PathgridEdgeColumn (int index) + : Column (Columns::ColumnId_PathgridEdge0+index, ColumnBase::Display_Integer), mIndex(index) + { + } + + virtual QVariant get (const Record& record) const + { + return QVariant(); // FIXME + } + + virtual void set (Record& record, const QVariant& data) + { + } + + virtual bool isEditable() const + { + return true; + } + }; } #endif diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp index 1d3bc7641..db0ebfd86 100644 --- a/apps/opencs/model/world/columns.cpp +++ b/apps/opencs/model/world/columns.cpp @@ -223,6 +223,14 @@ namespace CSMWorld { ColumnId_AreaSound, "Area Sound" }, { ColumnId_BoltSound, "Bolt Sound" }, + { ColumnId_PathgridPoints, "Points"}, + { ColumnId_PathgridPosX, "X"}, + { ColumnId_PathgridPosY, "Y"}, + { ColumnId_PathgridPosZ, "Z"}, + { ColumnId_PathgridEdges, "Edges"}, + { ColumnId_PathgridEdge0, "Edge 0"}, + { ColumnId_PathgridEdge1, "Edge 1"}, + { ColumnId_UseValue1, "Use value 1" }, { ColumnId_UseValue2, "Use value 2" }, { ColumnId_UseValue3, "Use value 3" }, diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp index 1c0af217b..e36e29b3b 100644 --- a/apps/opencs/model/world/columns.hpp +++ b/apps/opencs/model/world/columns.hpp @@ -211,6 +211,15 @@ namespace CSMWorld ColumnId_HitSound = 196, ColumnId_AreaSound = 197, ColumnId_BoltSound = 198, + + ColumnId_PathgridPoints = 199, + ColumnId_PathgridPosX = 200, + ColumnId_PathgridPosY = 201, + ColumnId_PathgridPosZ = 202, + ColumnId_PathgridEdges = 203, + ColumnId_PathgridEdge0 = 204, + ColumnId_PathgridEdge1 = 205, + // 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/data.cpp b/apps/opencs/model/world/data.cpp index 0cfc890ea..ea94fe905 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -18,6 +18,7 @@ #include "columns.hpp" #include "resourcesmanager.hpp" #include "resourcetable.hpp" +#include "idadapterimp.hpp" void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type, bool update) { @@ -255,6 +256,22 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc mPathgrids.addColumn (new RecordStateColumn); mPathgrids.addColumn (new FixedRecordTypeColumn (UniversalId::Type_Pathgrid)); + // new object deleted in dtor of Collection + PathgridPointListColumn *pointList = new PathgridPointListColumn (); + mPathgrids.addColumn (pointList); + // new object deleted in dtor of SubCellCollection + mPathgrids.addAdapter (std::make_pair(pointList, new PathgridPointListAdapter ())); + // new objects deleted in dtor of NestableColumn + mPathgrids.getNestableColumn(mPathgrids.getColumns()-1)->addColumn(new PathgridPointColumn (0)); + mPathgrids.getNestableColumn(mPathgrids.getColumns()-1)->addColumn(new PathgridPointColumn (1)); + mPathgrids.getNestableColumn(mPathgrids.getColumns()-1)->addColumn(new PathgridPointColumn (2)); + + PathgridEdgeListColumn *edgeList = new PathgridEdgeListColumn (); + mPathgrids.addColumn (edgeList); + mPathgrids.addAdapter (std::make_pair(edgeList, new PathgridEdgeListAdapter ())); + mPathgrids.getNestableColumn(mPathgrids.getColumns()-1)->addColumn(new PathgridEdgeColumn (0)); + mPathgrids.getNestableColumn(mPathgrids.getColumns()-1)->addColumn(new PathgridEdgeColumn (1)); + mStartScripts.addColumn (new StringIdColumn); mStartScripts.addColumn (new RecordStateColumn); mStartScripts.addColumn (new FixedRecordTypeColumn (UniversalId::Type_StartScript)); @@ -331,7 +348,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart); addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen); addModel (new IdTable (&mMagicEffects), UniversalId::Type_MagicEffect); - addModel (new IdTable (&mPathgrids), UniversalId::Type_Pathgrid); + addModel (new IdTree (&mPathgrids, &mPathgrids), UniversalId::Type_Pathgrid); addModel (new IdTable (&mStartScripts), UniversalId::Type_StartScript); addModel (new IdTree (&mReferenceables, &mReferenceables, IdTable::Feature_Preview), UniversalId::Type_Referenceable); diff --git a/apps/opencs/model/world/idadapter.hpp b/apps/opencs/model/world/idadapter.hpp new file mode 100644 index 000000000..bec21a0f3 --- /dev/null +++ b/apps/opencs/model/world/idadapter.hpp @@ -0,0 +1,39 @@ +#ifndef CSM_WOLRD_IDADAPTER_H +#define CSM_WOLRD_IDADAPTER_H + +#include "record.hpp" + +class QVariant; + +namespace CSMWorld +{ + class NestedTableWrapperBase; + + template + class NestedIdAdapter + { + public: + + NestedIdAdapter() {} + + virtual ~NestedIdAdapter() {} + + virtual void addNestedRow(Record& record, int position) const = 0; + + virtual void removeNestedRow(Record& record, int rowToRemove) const = 0; + + virtual void setNestedTable(Record& record, const NestedTableWrapperBase& nestedTable) = 0; + + virtual NestedTableWrapperBase* nestedTable(const Record& record) const = 0; + + virtual QVariant getNestedData(const Record& record, int subRowIndex, int subColIndex) const = 0; + + virtual void setNestedData(Record& record, const QVariant& value, int subRowIndex, int subColIndex) const = 0; + + virtual int getNestedColumnsCount(const Record& record) const = 0; + + virtual int getNestedRowsCount(const Record& record) const = 0; + }; +} + +#endif // CSM_WOLRD_IDADAPTER_H diff --git a/apps/opencs/model/world/idadapterimp.hpp b/apps/opencs/model/world/idadapterimp.hpp new file mode 100644 index 000000000..fa7c55bd3 --- /dev/null +++ b/apps/opencs/model/world/idadapterimp.hpp @@ -0,0 +1,202 @@ +#ifndef CSM_WOLRD_IDADAPTERIMP_H +#define CSM_WOLRD_IDADAPTERIMP_H + +#include + +#include + +#include "idadapter.hpp" + +namespace CSMWorld +{ + class NestedTableWrapperBase; + + template + class PathgridPointListAdapter : public NestedIdAdapter + { + public: + PathgridPointListAdapter () {} + + virtual void addNestedRow(Record& record, int position) const + { + ESXRecordT pathgrid = record.get(); + + ESXRecordT::PointList& points = pathgrid.mPoints; + + // blank row + ESM::Pathgrid::Point point; + point.mX = 0; + point.mY = 0; + point.mZ = 0; + point.mAutogenerated = 0; + point.mConnectionNum = 0; + point.mUnknown = 0; + + // FIXME: inserting a point should trigger re-indexing of the edges + points.insert(points.begin()+position, point); + pathgrid.mData.mS2 += 1; // increment the number of points + + record.setModified (pathgrid); + } + + virtual void removeNestedRow(Record& record, int rowToRemove) const + { + ESXRecordT pathgrid = record.get(); + + ESXRecordT::PointList& points = pathgrid.mPoints; + + if (rowToRemove < 0 || rowToRemove >= static_cast (points.size())) + throw std::runtime_error ("index out of range"); + + // FIXME: deleting a point should trigger re-indexing of the edges + points.erase(points.begin()+rowToRemove); + pathgrid.mData.mS2 -= 1; // decrement the number of points + + record.setModified (pathgrid); + } + + virtual void setNestedTable(Record& record, const NestedTableWrapperBase& nestedTable) + { + record.get().mPoints = + static_cast &>(nestedTable).mNestedTable; + } + + virtual NestedTableWrapperBase* nestedTable(const Record& record) const + { + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper(record.get().mPoints); + } + + virtual QVariant getNestedData(const Record& record, int subRowIndex, int subColIndex) const + { + ESM::Pathgrid::Point point = record.get().mPoints[subRowIndex]; + switch (subColIndex) + { + case 0: return point.mX; + case 1: return point.mY; + case 2: return point.mZ; + default: throw std::logic_error("Pathgrid point subcolumn index out of range"); + } + } + + virtual void setNestedData(Record& record, const QVariant& value, + int subRowIndex, int subColIndex) const + { + ESXRecordT pathgrid = record.get(); + ESM::Pathgrid::Point point = pathgrid.mPoints[subRowIndex]; + switch (subColIndex) + { + case 0: point.mX = value.toInt(); break; + case 1: point.mY = value.toInt(); break; + case 2: point.mZ = value.toInt(); break; + default: throw std::logic_error("Pathgrid point subcolumn index out of range"); + } + + pathgrid.mPoints[subRowIndex] = point; + + record.setModified (pathgrid); + } + + virtual int getNestedColumnsCount(const Record& record) const + { + return 3; + } + + virtual int getNestedRowsCount(const Record& record) const + { + return static_cast(record.get().mPoints.size()); + } + }; + + template + class PathgridEdgeListAdapter : public NestedIdAdapter + { + public: + PathgridEdgeListAdapter () {} + + // FIXME: seems to be auto-sorted in the dialog table display after insertion + virtual void addNestedRow(Record& record, int position) const + { + ESXRecordT pathgrid = record.get(); + + ESXRecordT::EdgeList& edges = pathgrid.mEdges; + + // blank row + ESM::Pathgrid::Edge edge; + edge.mV0 = 0; + edge.mV1 = 0; + + // FIXME: inserting a blank edge does not really make sense, perhaps this should be a + // logic_error exception + edges.insert(edges.begin()+position, edge); + + record.setModified (pathgrid); + } + + virtual void removeNestedRow(Record& record, int rowToRemove) const + { + ESXRecordT pathgrid = record.get(); + + ESXRecordT::EdgeList& edges = pathgrid.mEdges; + + if (rowToRemove < 0 || rowToRemove >= static_cast (edges.size())) + throw std::runtime_error ("index out of range"); + + edges.erase(edges.begin()+rowToRemove); + + record.setModified (pathgrid); + } + + virtual void setNestedTable(Record& record, const NestedTableWrapperBase& nestedTable) + { + record.get().mEdges = + static_cast &>(nestedTable).mNestedTable; + } + + virtual NestedTableWrapperBase* nestedTable(const Record& record) const + { + // deleted by dtor of NestedTableStoring + return new NestedTableWrapper(record.get().mEdges); + } + + virtual QVariant getNestedData(const Record& record, int subRowIndex, int subColIndex) const + { + ESM::Pathgrid::Edge edge = record.get().mEdges[subRowIndex]; + switch (subColIndex) + { + case 0: return edge.mV0; + case 1: return edge.mV1; + default: throw std::logic_error("Pathgrid edge subcolumn index out of range"); + } + } + + virtual void setNestedData(Record& record, const QVariant& value, + int subRowIndex, int subColIndex) const + { + ESXRecordT pathgrid = record.get(); + ESM::Pathgrid::Edge edge = pathgrid.mEdges[subRowIndex]; + switch (subColIndex) + { + case 0: edge.mV0 = value.toInt(); break; + case 1: edge.mV1 = value.toInt(); break; + default: throw std::logic_error("Pathgrid edge subcolumn index out of range"); + } + + pathgrid.mEdges[subRowIndex] = edge; + + record.setModified (pathgrid); + } + + virtual int getNestedColumnsCount(const Record& record) const + { + return 2; + } + + virtual int getNestedRowsCount(const Record& record) const + { + return static_cast(record.get().mEdges.size()); + } + }; +} + +#endif // CSM_WOLRD_IDADAPTERIMP_H diff --git a/apps/opencs/model/world/subcellcollection.hpp b/apps/opencs/model/world/subcellcollection.hpp index 74bb6c955..f2aeb5aaf 100644 --- a/apps/opencs/model/world/subcellcollection.hpp +++ b/apps/opencs/model/world/subcellcollection.hpp @@ -1,6 +1,17 @@ #ifndef CSM_WOLRD_SUBCOLLECTION_H #define CSM_WOLRD_SUBCOLLECTION_H +#include +#include + +#include + +#include "columnimp.hpp" +#include "idcollection.hpp" +#include "nestedcollection.hpp" +#include "nestedtablewrapper.hpp" +#include "idadapterimp.hpp" + namespace ESM { class ESMReader; @@ -14,17 +25,40 @@ namespace CSMWorld /// \brief Single type collection of top level records that are associated with cells template > - class SubCellCollection : public IdCollection + class SubCellCollection : public IdCollection, public NestedCollection { const IdCollection& mCells; + std::map* > mAdapters; virtual void loadRecord (ESXRecordT& record, ESM::ESMReader& reader); + NestedIdAdapter* getAdapter(const ColumnBase &column) const; + public: SubCellCollection (const IdCollection& cells); + ~SubCellCollection(); + virtual void addNestedRow(int row, int column, int position); + virtual void removeNestedRows(int row, int column, int subRow); + + virtual QVariant getNestedData(int row, int column, int subRow, int subColumn) const; + + virtual void setNestedData(int row, int column, const QVariant& data, int subRow, int subColumn); + + virtual NestedTableWrapperBase* nestedTable(int row, int column) const; + + virtual void setNestedTable(int row, int column, const NestedTableWrapperBase& nestedTable); + + virtual int getNestedRowsCount(int row, int column) const; + + virtual int getNestedColumnsCount(int row, int column) const; + + // this method is inherited from NestedCollection, not from Collection + virtual NestableColumn *getNestableColumn(int column); + + void addAdapter(std::pair* > adapter); }; template @@ -35,11 +69,93 @@ namespace CSMWorld } template - SubCellCollection::SubCellCollection ( - const IdCollection& cells) + SubCellCollection::SubCellCollection (const IdCollection& cells) : mCells (cells) {} + template + SubCellCollection::~SubCellCollection() + { + for (std::map* >::iterator iter (mAdapters.begin()); + iter!=mAdapters.end(); ++iter) + delete (*iter).second; + } + + template + void SubCellCollection::addAdapter(std::pair* > adapter) + { + mAdapters.insert(adapter); + } + + template + NestedIdAdapter* SubCellCollection::getAdapter(const ColumnBase &column) const + { + std::map* >::const_iterator iter = + mAdapters.find (&column); + + if (iter==mAdapters.end()) + throw std::logic_error("No such column in the nestedidadapter"); + + return iter->second; + } + + template + void SubCellCollection::addNestedRow(int row, int column, int position) + { + getAdapter(getColumn(column))->addNestedRow(getRecord(row), position); + } + + template + void SubCellCollection::removeNestedRows(int row, int column, int subRow) + { + getAdapter(getColumn(column))->removeNestedRow(getRecord(row), subRow); + } + + template + QVariant SubCellCollection::getNestedData (int row, + int column, int subRow, int subColumn) const + { + return getAdapter(getColumn(column))->getNestedData(getRecord(row), subRow, subColumn); + } + + template + void SubCellCollection::setNestedData(int row, + int column, const QVariant& data, int subRow, int subColumn) + { + getAdapter(getColumn(column))->setNestedData(getRecord(row), data, subRow, subColumn); + } + + template + CSMWorld::NestedTableWrapperBase* SubCellCollection::nestedTable(int row, + int column) const + { + return getAdapter(getColumn(column))->nestedTable(getRecord(row)); + } + + template + void SubCellCollection::setNestedTable(int row, + int column, const CSMWorld::NestedTableWrapperBase& nestedTable) + { + getAdapter(getColumn(column))->setNestedTable(getRecord(row), nestedTable); + } + + template + int SubCellCollection::getNestedRowsCount(int row, int column) const + { + return getAdapter(getColumn(column))->getNestedRowsCount(getRecord(row)); + } + + template + int SubCellCollection::getNestedColumnsCount(int row, int column) const + { + return getAdapter(getColumn(column))->getNestedColumnsCount(getRecord(row)); + } + + template + CSMWorld::NestableColumn *SubCellCollection::getNestableColumn(int column) + { + return Collection::getNestableColumn(column); + } } #endif