From 4f05f2bddfb65354f6843656ab9d145f89074145 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 8 Jul 2013 13:12:50 +0200 Subject: [PATCH 1/8] basic region map; non-interactive for now and working with dummy data instead of real cell/region records --- apps/opencs/CMakeLists.txt | 4 +- apps/opencs/model/world/data.cpp | 3 + apps/opencs/model/world/regionmap.cpp | 73 +++++++++++++++++++++ apps/opencs/model/world/regionmap.hpp | 38 +++++++++++ apps/opencs/model/world/universalid.cpp | 2 + apps/opencs/model/world/universalid.hpp | 3 +- apps/opencs/view/doc/view.cpp | 9 +++ apps/opencs/view/doc/view.hpp | 2 + apps/opencs/view/world/regionmapsubview.cpp | 29 ++++++++ apps/opencs/view/world/regionmapsubview.hpp | 27 ++++++++ apps/opencs/view/world/subviews.cpp | 3 + 11 files changed, 190 insertions(+), 3 deletions(-) create mode 100644 apps/opencs/model/world/regionmap.cpp create mode 100644 apps/opencs/model/world/regionmap.hpp create mode 100644 apps/opencs/view/world/regionmapsubview.cpp create mode 100644 apps/opencs/view/world/regionmapsubview.hpp diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt index cec3756a3..c7006c4d7 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 + idtable idtableproxymodel regionmap ) @@ -57,7 +57,7 @@ opencs_hdrs_noqt (view/doc opencs_units (view/world - table tablesubview scriptsubview util + table tablesubview scriptsubview util regionmapsubview ) opencs_units_noqt (view/world diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 392175442..4a38604b1 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -11,6 +11,7 @@ #include "idtable.hpp" #include "columns.hpp" +#include "regionmap.hpp" void CSMWorld::Data::addModel (QAbstractItemModel *model, UniversalId::Type type1, UniversalId::Type type2) @@ -161,6 +162,8 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference); + + addModel (new RegionMap, UniversalId::Type_RegionMap, UniversalId::Type_None); } CSMWorld::Data::~Data() diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp new file mode 100644 index 000000000..8fbed9c5a --- /dev/null +++ b/apps/opencs/model/world/regionmap.cpp @@ -0,0 +1,73 @@ + +#include "regionmap.hpp" + +#include + +std::pair CSMWorld::RegionMap::getIndex (const QModelIndex& index) const +{ + return std::make_pair (index.column()+mMin.first, index.row()+mMin.second); +} + +CSMWorld::RegionMap::RegionMap() +{ + // setting up some placeholder regions + mMap.insert (std::make_pair (std::make_pair (0, 0), "a")); + mMap.insert (std::make_pair (std::make_pair (1, 1), "b")); + mMap.insert (std::make_pair (std::make_pair (1, 0), "a")); + mMin = std::make_pair (0, 0); + mMax = std::make_pair (2, 2); + mColours.insert (std::make_pair ("a", 0xff0000ff)); + mColours.insert (std::make_pair ("b", 0x00ff00ff)); +} + +int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const +{ + if (parent.isValid()) + return 0; + + return mMax.second-mMin.second; +} + +int CSMWorld::RegionMap::columnCount (const QModelIndex& parent) const +{ + if (parent.isValid()) + return 0; + + return mMax.first-mMin.first; +} + +QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const +{ + if (role==Qt::SizeHintRole) + return QSize (16, 16); + + if (role==Qt::BackgroundRole) + { + /// \todo GUI class in non-GUI code. Needs to be addressed eventually. + + std::map, std::string>::const_iterator cell = + mMap.find (getIndex (index)); + + if (cell!=mMap.end()) + { + std::map::const_iterator iter = mColours.find (cell->second); + + if (iter!=mColours.end()) + return QBrush ( + QColor (iter->second>>24, (iter->second>>16) & 255, (iter->second>>8) & 255, + iter->second & 255)); + } + + return QBrush (Qt::DiagCrossPattern); + } + + return QVariant(); +} + +Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const +{ + if (mMap.find (getIndex (index))!=mMap.end()) + return Qt::ItemIsSelectable | Qt::ItemIsEnabled; + + return 0; +} \ No newline at end of file diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp new file mode 100644 index 000000000..78792fad1 --- /dev/null +++ b/apps/opencs/model/world/regionmap.hpp @@ -0,0 +1,38 @@ +#ifndef CSM_WOLRD_REGIONMAP_H +#define CSM_WOLRD_REGIONMAP_H + +#include +#include + +#include + +namespace CSMWorld +{ + /// \brief Model for the region map + /// + /// This class does not holds any record data (other than for the purpose of buffering). + class RegionMap : public QAbstractTableModel + { + std::map, std::string> mMap; ///< cell index, region + std::pair mMin; ///< inclusive + std::pair mMax; ///< exclusive + std::map mColours; ///< region ID, colour (RGBA) + + std::pair getIndex (const QModelIndex& index) const; + ///< Translates a Qt model index into a cell index (which can contain negative components) + + public: + + RegionMap(); + + virtual int rowCount (const QModelIndex& parent = QModelIndex()) const; + + virtual int columnCount (const QModelIndex& parent = QModelIndex()) const; + + virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const; + + virtual Qt::ItemFlags flags (const QModelIndex& index) const; + }; +} + +#endif diff --git a/apps/opencs/model/world/universalid.cpp b/apps/opencs/model/world/universalid.cpp index b5efe6e22..7e07a2989 100644 --- a/apps/opencs/model/world/universalid.cpp +++ b/apps/opencs/model/world/universalid.cpp @@ -33,6 +33,8 @@ namespace "Referenceables" }, { CSMWorld::UniversalId::Class_RecordList, CSMWorld::UniversalId::Type_References, "References" }, + { CSMWorld::UniversalId::Class_NonRecord, CSMWorld::UniversalId::Type_RegionMap, + "Region Map" }, { CSMWorld::UniversalId::Class_None, CSMWorld::UniversalId::Type_None, 0 } // end marker }; diff --git a/apps/opencs/model/world/universalid.hpp b/apps/opencs/model/world/universalid.hpp index bd6fdeb00..8ca8765e7 100644 --- a/apps/opencs/model/world/universalid.hpp +++ b/apps/opencs/model/world/universalid.hpp @@ -81,7 +81,8 @@ namespace CSMWorld Type_Static, Type_Weapon, Type_References, - Type_Reference + Type_Reference, + Type_RegionMap }; private: diff --git a/apps/opencs/view/doc/view.cpp b/apps/opencs/view/doc/view.cpp index bcc020350..cdc161bd8 100644 --- a/apps/opencs/view/doc/view.cpp +++ b/apps/opencs/view/doc/view.cpp @@ -139,6 +139,10 @@ void CSVDoc::View::setupWorldMenu() QAction *references = new QAction (tr ("References"), this); connect (references, SIGNAL (triggered()), this, SLOT (addReferencesSubView())); world->addAction (references); + + QAction *regionMap = new QAction (tr ("Region Map"), this); + connect (regionMap, SIGNAL (triggered()), this, SLOT (addRegionMapSubView())); + world->addAction (regionMap); } void CSVDoc::View::setupUi() @@ -363,6 +367,11 @@ void CSVDoc::View::addReferencesSubView() addSubView (CSMWorld::UniversalId::Type_References); } +void CSVDoc::View::addRegionMapSubView() +{ + addSubView (CSMWorld::UniversalId::Type_RegionMap); +} + void CSVDoc::View::abortOperation (int type) { mDocument->abortOperation (type); diff --git a/apps/opencs/view/doc/view.hpp b/apps/opencs/view/doc/view.hpp index b3c4ae22a..ff81f8223 100644 --- a/apps/opencs/view/doc/view.hpp +++ b/apps/opencs/view/doc/view.hpp @@ -151,6 +151,8 @@ namespace CSVDoc void addReferencesSubView(); + void addRegionMapSubView(); + void showUserSettings(); }; } diff --git a/apps/opencs/view/world/regionmapsubview.cpp b/apps/opencs/view/world/regionmapsubview.cpp new file mode 100644 index 000000000..b82c1afb5 --- /dev/null +++ b/apps/opencs/view/world/regionmapsubview.cpp @@ -0,0 +1,29 @@ + +#include "regionmapsubview.hpp" + +#include +#include + +CSVWorld::RegionMapSubView::RegionMapSubView (CSMWorld::UniversalId universalId, + CSMDoc::Document& document) +: CSVDoc::SubView (universalId) +{ + mTable = new QTableView (this); + + mTable->verticalHeader()->hide(); + mTable->horizontalHeader()->hide(); + + mTable->setSelectionMode (QAbstractItemView::ExtendedSelection); + + mTable->setModel (document.getData().getTableModel (universalId)); + + mTable->resizeColumnsToContents(); + mTable->resizeRowsToContents(); + + setWidget (mTable); +} + +void CSVWorld::RegionMapSubView::setEditLock (bool locked) +{ + +} \ No newline at end of file diff --git a/apps/opencs/view/world/regionmapsubview.hpp b/apps/opencs/view/world/regionmapsubview.hpp new file mode 100644 index 000000000..1655107af --- /dev/null +++ b/apps/opencs/view/world/regionmapsubview.hpp @@ -0,0 +1,27 @@ +#ifndef CSV_WORLD_REGIONMAPSUBVIEW_H +#define CSV_WORLD_REGIONMAPSUBVIEW_H + +#include "../doc/subview.hpp" + +class QTableView; + +namespace CSMDoc +{ + class Document; +} + +namespace CSVWorld +{ + class RegionMapSubView : public CSVDoc::SubView + { + QTableView *mTable; + + public: + + RegionMapSubView (CSMWorld::UniversalId universalId, CSMDoc::Document& document); + + virtual void setEditLock (bool locked); + }; +} + +#endif diff --git a/apps/opencs/view/world/subviews.cpp b/apps/opencs/view/world/subviews.cpp index cd98ed4e0..1c06ea2f6 100644 --- a/apps/opencs/view/world/subviews.cpp +++ b/apps/opencs/view/world/subviews.cpp @@ -6,6 +6,7 @@ #include "tablesubview.hpp" #include "dialoguesubview.hpp" #include "scriptsubview.hpp" +#include "regionmapsubview.hpp" void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) { @@ -38,6 +39,8 @@ void CSVWorld::addSubViewFactories (CSVDoc::SubViewFactoryManager& manager) manager.add (CSMWorld::UniversalId::Type_Script, new CSVDoc::SubViewFactory); + manager.add (CSMWorld::UniversalId::Type_RegionMap, new CSVDoc::SubViewFactory); + // manager.add (CSMWorld::UniversalId::Type_Global, // new CSVDoc::SubViewFactoryWithCreateFlag (true)); } \ No newline at end of file From 6159724b04e72809ca55c350d21251716cb37ab8 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 8 Jul 2013 16:53:08 +0200 Subject: [PATCH 2/8] build region map from records (only initial build; updates not implemented yet) --- apps/opencs/model/world/data.cpp | 15 ++- apps/opencs/model/world/regionmap.cpp | 127 ++++++++++++++++++++++++-- apps/opencs/model/world/regionmap.hpp | 24 ++++- 3 files changed, 154 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp index 4a38604b1..d54b3ac16 100644 --- a/apps/opencs/model/world/data.cpp +++ b/apps/opencs/model/world/data.cpp @@ -162,8 +162,6 @@ CSMWorld::Data::Data() : mRefs (mCells) addModel (new IdTable (&mReferenceables), UniversalId::Type_Referenceables, UniversalId::Type_Referenceable); addModel (new IdTable (&mRefs), UniversalId::Type_References, UniversalId::Type_Reference); - - addModel (new RegionMap, UniversalId::Type_RegionMap, UniversalId::Type_None); } CSMWorld::Data::~Data() @@ -307,7 +305,20 @@ QAbstractItemModel *CSMWorld::Data::getTableModel (const UniversalId& id) std::map::iterator iter = mModelIndex.find (id.getType()); if (iter==mModelIndex.end()) + { + // try creating missing (secondary) tables on the fly + // + // Note: We create these tables here so we don't have to deal with them during load/initial + // construction of the ESX data where no update signals are available. + if (id.getType()==UniversalId::Type_RegionMap) + { + RegionMap *table = 0; + addModel (table = new RegionMap (*this), UniversalId::Type_RegionMap, + UniversalId::Type_None); + return table; + } throw std::logic_error ("No table model available for " + id.toString()); + } return iter->second; } diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 8fbed9c5a..01ec979d2 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -3,21 +3,95 @@ #include +#include "data.hpp" +#include "universalid.hpp" + std::pair CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { return std::make_pair (index.column()+mMin.first, index.row()+mMin.second); } -CSMWorld::RegionMap::RegionMap() +void CSMWorld::RegionMap::buildRegions (Data& data) { - // setting up some placeholder regions - mMap.insert (std::make_pair (std::make_pair (0, 0), "a")); - mMap.insert (std::make_pair (std::make_pair (1, 1), "b")); - mMap.insert (std::make_pair (std::make_pair (1, 0), "a")); - mMin = std::make_pair (0, 0); - mMax = std::make_pair (2, 2); - mColours.insert (std::make_pair ("a", 0xff0000ff)); - mColours.insert (std::make_pair ("b", 0x00ff00ff)); + const IdCollection& regions = data.getRegions(); + + int size = regions.getSize(); + + for (int i=0; i& region = regions.getRecord (i); + + if (!region.isDeleted()) + mColours.insert (std::make_pair (region.get().mId, region.get().mMapColor)); + } +} + +void CSMWorld::RegionMap::buildMap (Data& data) +{ + const IdCollection& cells = data.getCells(); + + int size = cells.getSize(); + + mMin = mMax = std::make_pair (0, 0); + + for (int i=0; i& cell = cells.getRecord (i); + + if (!cell.isDeleted()) + { + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + std::pair index (cell2.mData.mX, cell2.mData.mY); + + if (mMap.empty()) + { + mMin = index; + mMax = std::make_pair (mMin.first+1, mMin.second+1); + } + else + { + if (index.first=mMax.first) + mMax.first = index.first + 1; + + if (index.second=mMax.second) + mMax.second = index.second + 1; + } + + mMap.insert (std::make_pair (index, cell2.mRegion)); + } + } + } +} + +CSMWorld::RegionMap::RegionMap (Data& data) +{ + buildRegions (data); + buildMap (data); + + QAbstractItemModel *regions = data.getTableModel (UniversalId (UniversalId::Type_Regions)); + + connect (regions, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (regionsAboutToBeRemoved (const QModelIndex&, int, int))); + connect (regions, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (regionsInserted (const QModelIndex&, int, int))); + connect (regions, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (regionsChanged (const QModelIndex&, const QModelIndex&))); + + QAbstractItemModel *cells = data.getTableModel (UniversalId (UniversalId::Type_Cells)); + + connect (cells, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), + this, SLOT (regionsAboutToBeRemoved (const QModelIndex&, int, int))); + connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), + this, SLOT (regionsInserted (const QModelIndex&, int, int))); + connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), + this, SLOT (regionsChanged (const QModelIndex&, const QModelIndex&))); } int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const @@ -70,4 +144,39 @@ Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const return Qt::ItemIsSelectable | Qt::ItemIsEnabled; return 0; +} + +void CSMWorld::RegionMap::regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + + +} + +void CSMWorld::RegionMap::regionsInserted (const QModelIndex& parent, int start, int end) +{ + + +} + +void CSMWorld::RegionMap::regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ + + +} + +void CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end) +{ + + +} + +void CSMWorld::RegionMap::cellsInserted (const QModelIndex& parent, int start, int end) +{ + + +} + +void CSMWorld::RegionMap::cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) +{ +; } \ No newline at end of file diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index 78792fad1..a61875c78 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -8,11 +8,15 @@ namespace CSMWorld { + class Data; + /// \brief Model for the region map /// /// This class does not holds any record data (other than for the purpose of buffering). class RegionMap : public QAbstractTableModel { + Q_OBJECT + std::map, std::string> mMap; ///< cell index, region std::pair mMin; ///< inclusive std::pair mMax; ///< exclusive @@ -21,9 +25,13 @@ namespace CSMWorld std::pair getIndex (const QModelIndex& index) const; ///< Translates a Qt model index into a cell index (which can contain negative components) + void buildRegions (Data& data); + + void buildMap (Data& data); + public: - RegionMap(); + RegionMap (Data& data); virtual int rowCount (const QModelIndex& parent = QModelIndex()) const; @@ -32,6 +40,20 @@ namespace CSMWorld virtual QVariant data (const QModelIndex& index, int role = Qt::DisplayRole) const; virtual Qt::ItemFlags flags (const QModelIndex& index) const; + + private slots: + + void regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void regionsInserted (const QModelIndex& parent, int start, int end); + + void regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); + + void cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end); + + void cellsInserted (const QModelIndex& parent, int start, int end); + + void cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight); }; } From 116320cc0dc508d2759c422f1ba96c0c48a00ccd Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Mon, 8 Jul 2013 17:13:58 +0200 Subject: [PATCH 3/8] dealing with invalid regions and cells without regions --- apps/opencs/model/world/regionmap.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 01ec979d2..6dd3148d5 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -130,6 +130,11 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const return QBrush ( QColor (iter->second>>24, (iter->second>>16) & 255, (iter->second>>8) & 255, iter->second & 255)); + + if (cell->second.empty()) + return QBrush (Qt::Dense6Pattern); // no region + + return QBrush (Qt::red, Qt::Dense6Pattern); } return QBrush (Qt::DiagCrossPattern); From 121978a69e23ebf4d5d58513970d5a608a1de62e Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 9 Jul 2013 14:20:28 +0200 Subject: [PATCH 4/8] some cleanup; added handling of deleted cells (now displayed instead of ignored) --- apps/opencs/model/world/regionmap.cpp | 68 +++++++++++++++------------ apps/opencs/model/world/regionmap.hpp | 22 +++++++-- 2 files changed, 57 insertions(+), 33 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 6dd3148d5..b45c45a54 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -6,9 +6,11 @@ #include "data.hpp" #include "universalid.hpp" +CSMWorld::RegionMap::CellDescription::CellDescription() : mDeleted (false) {} + std::pair CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { - return std::make_pair (index.column()+mMin.first, index.row()+mMin.second); + return CellIndex (index.column()+mMin.first, index.row()+mMin.second); } void CSMWorld::RegionMap::buildRegions (Data& data) @@ -38,34 +40,38 @@ void CSMWorld::RegionMap::buildMap (Data& data) { const Record& cell = cells.getRecord (i); - if (!cell.isDeleted()) + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) { - const Cell& cell2 = cell.get(); + CellDescription description; - if (cell2.isExterior()) + if (cell.isDeleted()) + description.mDeleted = true; + else + description.mRegion = cell2.mRegion; + + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + if (mMap.empty()) { - std::pair index (cell2.mData.mX, cell2.mData.mY); - - if (mMap.empty()) - { - mMin = index; - mMax = std::make_pair (mMin.first+1, mMin.second+1); - } - else - { - if (index.first=mMax.first) - mMax.first = index.first + 1; - - if (index.second=mMax.second) - mMax.second = index.second + 1; - } - - mMap.insert (std::make_pair (index, cell2.mRegion)); + mMin = index; + mMax = std::make_pair (mMin.first+1, mMin.second+1); } + else + { + if (index.first=mMax.first) + mMax.first = index.first + 1; + + if (index.second=mMax.second) + mMax.second = index.second + 1; + } + + mMap.insert (std::make_pair (index, description)); } } } @@ -119,22 +125,26 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const { /// \todo GUI class in non-GUI code. Needs to be addressed eventually. - std::map, std::string>::const_iterator cell = + std::map::const_iterator cell = mMap.find (getIndex (index)); if (cell!=mMap.end()) { - std::map::const_iterator iter = mColours.find (cell->second); + if (cell->second.mDeleted) + return QBrush (Qt::red, Qt::DiagCrossPattern); + + std::map::const_iterator iter = + mColours.find (cell->second.mRegion); if (iter!=mColours.end()) return QBrush ( QColor (iter->second>>24, (iter->second>>16) & 255, (iter->second>>8) & 255, iter->second & 255)); - if (cell->second.empty()) + if (cell->second.mRegion.empty()) return QBrush (Qt::Dense6Pattern); // no region - return QBrush (Qt::red, Qt::Dense6Pattern); + return QBrush (Qt::red, Qt::Dense6Pattern); // invalid region } return QBrush (Qt::DiagCrossPattern); diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index a61875c78..c5f70b490 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -17,12 +17,26 @@ namespace CSMWorld { Q_OBJECT - std::map, std::string> mMap; ///< cell index, region - std::pair mMin; ///< inclusive - std::pair mMax; ///< exclusive + public: + + typedef std::pair CellIndex; + + private: + + struct CellDescription + { + bool mDeleted; + std::string mRegion; + + CellDescription(); + }; + + std::map mMap; + CellIndex mMin; ///< inclusive + CellIndex mMax; ///< exclusive std::map mColours; ///< region ID, colour (RGBA) - std::pair getIndex (const QModelIndex& index) const; + CellIndex getIndex (const QModelIndex& index) const; ///< Translates a Qt model index into a cell index (which can contain negative components) void buildRegions (Data& data); From d389b70ec4cf56b4d272fd53f5d1768e2db65010 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Tue, 9 Jul 2013 14:22:58 +0200 Subject: [PATCH 5/8] added missing case folding --- apps/opencs/model/world/regionmap.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index b45c45a54..5b28097f9 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -3,6 +3,8 @@ #include +#include + #include "data.hpp" #include "universalid.hpp" @@ -24,7 +26,8 @@ void CSMWorld::RegionMap::buildRegions (Data& data) const Record& region = regions.getRecord (i); if (!region.isDeleted()) - mColours.insert (std::make_pair (region.get().mId, region.get().mMapColor)); + mColours.insert (std::make_pair (Misc::StringUtils::lowerCase (region.get().mId), + region.get().mMapColor)); } } @@ -134,7 +137,7 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const return QBrush (Qt::red, Qt::DiagCrossPattern); std::map::const_iterator iter = - mColours.find (cell->second.mRegion); + mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion)); if (iter!=mColours.end()) return QBrush ( From c808bf2b23a8297f6582c3eba82557dfa4a7eb74 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Thu, 11 Jul 2013 15:05:28 +0200 Subject: [PATCH 6/8] updating region map on changes to region and cell records --- apps/opencs/model/world/regionmap.cpp | 338 +++++++++++++++++++++++--- apps/opencs/model/world/regionmap.hpp | 35 ++- 2 files changed, 337 insertions(+), 36 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index 5b28097f9..fd278d2a6 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -1,6 +1,8 @@ #include "regionmap.hpp" +#include + #include #include @@ -10,14 +12,20 @@ CSMWorld::RegionMap::CellDescription::CellDescription() : mDeleted (false) {} -std::pair CSMWorld::RegionMap::getIndex (const QModelIndex& index) const +CSMWorld::RegionMap::CellIndex CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { return CellIndex (index.column()+mMin.first, index.row()+mMin.second); } -void CSMWorld::RegionMap::buildRegions (Data& data) +QModelIndex CSMWorld::RegionMap::getIndex (const CellIndex& index) const { - const IdCollection& regions = data.getRegions(); + // I hate you, Qt API naming scheme! + return QAbstractTableModel::index (index.second-mMin.second, index.first-mMin.first); +} + +void CSMWorld::RegionMap::buildRegions() +{ + const IdCollection& regions = mData.getRegions(); int size = regions.getSize(); @@ -31,14 +39,12 @@ void CSMWorld::RegionMap::buildRegions (Data& data) } } -void CSMWorld::RegionMap::buildMap (Data& data) +void CSMWorld::RegionMap::buildMap() { - const IdCollection& cells = data.getCells(); + const IdCollection& cells = mData.getCells(); int size = cells.getSize(); - mMin = mMax = std::make_pair (0, 0); - for (int i=0; i& cell = cells.getRecord (i); @@ -56,33 +62,237 @@ void CSMWorld::RegionMap::buildMap (Data& data) CellIndex index (cell2.mData.mX, cell2.mData.mY); - if (mMap.empty()) - { - mMin = index; - mMax = std::make_pair (mMin.first+1, mMin.second+1); - } - else - { - if (index.first=mMax.first) - mMax.first = index.first + 1; - - if (index.second=mMax.second) - mMax.second = index.second + 1; - } - mMap.insert (std::make_pair (index, description)); } } + + std::pair mapSize = getSize(); + + mMin = mapSize.first; + mMax = mapSize.second; +} + +void CSMWorld::RegionMap::addCell (const CellIndex& index, const CellDescription& description) +{ + std::map::iterator cell = mMap.find (index); + + if (cell!=mMap.end()) + { + cell->second = description; + } + else + { + updateSize(); + + mMap.insert (std::make_pair (index, description)); + } + + QModelIndex index2 = getIndex (index); + + dataChanged (index2, index2); +} + +void CSMWorld::RegionMap::addCells (int start, int end) +{ + const IdCollection& cells = mData.getCells(); + + for (int i=start; i<=end; ++i) + { + const Record& cell = cells.getRecord (i); + + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + CellDescription description; + + if (cell.isDeleted()) + description.mDeleted = true; + else + description.mRegion = cell2.mRegion; + + addCell (index, description); + } + } } -CSMWorld::RegionMap::RegionMap (Data& data) +void CSMWorld::RegionMap::removeCell (const CellIndex& index) { - buildRegions (data); - buildMap (data); + std::map::iterator cell = mMap.find (index); + + if (cell!=mMap.end()) + { + mMap.erase (cell); + + QModelIndex index2 = getIndex (index); + + dataChanged (index2, index2); + + updateSize(); + } +} + +void CSMWorld::RegionMap::addRegion (const std::string& region, unsigned int colour) +{ + mColours[Misc::StringUtils::lowerCase (region)] = colour; +} + +void CSMWorld::RegionMap::removeRegion (const std::string& region) +{ + std::map::iterator iter ( + mColours.find (Misc::StringUtils::lowerCase (region))); + + if (iter!=mColours.end()) + mColours.erase (iter); +} + +void CSMWorld::RegionMap::updateRegions (const std::vector& regions) +{ + std::vector regions2 (regions); + + std::for_each (regions2.begin(), regions2.end(), &Misc::StringUtils::lowerCase); + std::sort (regions2.begin(), regions2.end()); + + for (std::map::const_iterator iter (mMap.begin()); + iter!=mMap.end(); ++iter) + { + if (!iter->second.mRegion.empty() && + std::find (regions2.begin(), regions2.end(), + Misc::StringUtils::lowerCase (iter->second.mRegion))!=regions2.end()) + { + QModelIndex index = getIndex (iter->first); + + dataChanged (index, index); + } + } +} + +void CSMWorld::RegionMap::updateSize() +{ + std::pair size = getSize(); + + { + int diff = size.first.first - mMin.first; + + if (diff<0) + { + beginInsertColumns (QModelIndex(), 0, -diff-1); + mMin.first = size.first.first; + endInsertColumns(); + } + else if (diff>0) + { + beginRemoveColumns (QModelIndex(), 0, diff-1); + mMin.first = size.first.first; + endRemoveColumns(); + } + } + + { + int diff = size.first.second - mMin.second; + + if (diff<0) + { + beginInsertRows (QModelIndex(), 0, -diff-1); + mMin.second = size.first.second; + endInsertRows(); + } + else if (diff>0) + { + beginRemoveRows (QModelIndex(), 0, diff-1); + mMin.second = size.first.second; + endRemoveRows(); + } + } + + { + int diff = size.second.first - mMax.first; + + if (diff>0) + { + int columns = columnCount(); + beginInsertColumns (QModelIndex(), columns, columns+diff-1); + mMax.first = size.second.first; + endInsertColumns(); + } + else if (diff<0) + { + int columns = columnCount(); + beginRemoveColumns (QModelIndex(), columns+diff, columns-1); + mMax.first = size.second.first; + endRemoveColumns(); + } + } + + { + int diff = size.second.second - mMax.second; + + if (diff>0) + { + int rows = rowCount(); + beginInsertRows (QModelIndex(), rows, rows+diff-1); + mMax.second = size.second.second; + endInsertRows(); + } + else if (diff<0) + { + int rows = rowCount(); + beginRemoveRows (QModelIndex(), rows+diff, rows-1); + mMax.second = size.second.second; + endRemoveRows(); + } + } +} + +std::pair CSMWorld::RegionMap::getSize() + const +{ + const IdCollection& cells = mData.getCells(); + + int size = cells.getSize(); + + CellIndex min (0, 0); + CellIndex max (0, 0); + + for (int i=0; i& cell = cells.getRecord (i); + + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + if (min==max) + { + min = index; + max = std::make_pair (min.first+1, min.second+1); + } + else + { + if (index.first=max.first) + max.first = index.first + 1; + + if (index.second=max.second) + max.second = index.second + 1; + } + } + } + + return std::make_pair (min, max); +} + +CSMWorld::RegionMap::RegionMap (Data& data) : mData (data) +{ + buildRegions(); + buildMap(); QAbstractItemModel *regions = data.getTableModel (UniversalId (UniversalId::Type_Regions)); @@ -96,11 +306,11 @@ CSMWorld::RegionMap::RegionMap (Data& data) QAbstractItemModel *cells = data.getTableModel (UniversalId (UniversalId::Type_Cells)); connect (cells, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)), - this, SLOT (regionsAboutToBeRemoved (const QModelIndex&, int, int))); + this, SLOT (cellsAboutToBeRemoved (const QModelIndex&, int, int))); connect (cells, SIGNAL (rowsInserted (const QModelIndex&, int, int)), - this, SLOT (regionsInserted (const QModelIndex&, int, int))); + this, SLOT (cellsInserted (const QModelIndex&, int, int))); connect (cells, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)), - this, SLOT (regionsChanged (const QModelIndex&, const QModelIndex&))); + this, SLOT (cellsChanged (const QModelIndex&, const QModelIndex&))); } int CSMWorld::RegionMap::rowCount (const QModelIndex& parent) const @@ -166,35 +376,95 @@ Qt::ItemFlags CSMWorld::RegionMap::flags (const QModelIndex& index) const void CSMWorld::RegionMap::regionsAboutToBeRemoved (const QModelIndex& parent, int start, int end) { + std::vector update; + const IdCollection& regions = mData.getRegions(); + for (int i=start; i<=end; ++i) + { + const Record& region = regions.getRecord (i); + + update.push_back (region.get().mId); + + removeRegion (region.get().mId); + } + + updateRegions (update); } void CSMWorld::RegionMap::regionsInserted (const QModelIndex& parent, int start, int end) { + std::vector update; + const IdCollection& regions = mData.getRegions(); + for (int i=start; i<=end; ++i) + { + const Record& region = regions.getRecord (i); + + if (!region.isDeleted()) + { + update.push_back (region.get().mId); + + addRegion (region.get().mId, region.get().mMapColor); + } + } + + updateRegions (update); } void CSMWorld::RegionMap::regionsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { + // Note: At this point an additional check could be inserted to see if there is any change to the + // columns we are interested in. If not we can exit the function here and avoid all updating. + std::vector update; + const IdCollection& regions = mData.getRegions(); + + for (int i=topLeft.row(); i<=bottomRight.column(); ++i) + { + const Record& region = regions.getRecord (i); + + update.push_back (region.get().mId); + + if (!region.isDeleted()) + addRegion (region.get().mId, region.get().mMapColor); + else + removeRegion (region.get().mId); + } + + updateRegions (update); } void CSMWorld::RegionMap::cellsAboutToBeRemoved (const QModelIndex& parent, int start, int end) { + const IdCollection& cells = mData.getCells(); + for (int i=start; i<=end; ++i) + { + const Record& cell = cells.getRecord (i); + const Cell& cell2 = cell.get(); + + if (cell2.isExterior()) + { + CellIndex index (cell2.mData.mX, cell2.mData.mY); + + removeCell (index); + } + } } void CSMWorld::RegionMap::cellsInserted (const QModelIndex& parent, int start, int end) { - - + addCells (start, end); } void CSMWorld::RegionMap::cellsChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) { -; + // Note: At this point an additional check could be inserted to see if there is any change to the + // columns we are interested in. If not we can exit the function here and avoid all updating. + + addCells (topLeft.row(), bottomRight.row()); } \ No newline at end of file diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index c5f70b490..0a20324c4 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -3,6 +3,7 @@ #include #include +#include #include @@ -31,6 +32,7 @@ namespace CSMWorld CellDescription(); }; + Data& mData; std::map mMap; CellIndex mMin; ///< inclusive CellIndex mMax; ///< exclusive @@ -39,9 +41,38 @@ namespace CSMWorld CellIndex getIndex (const QModelIndex& index) const; ///< Translates a Qt model index into a cell index (which can contain negative components) - void buildRegions (Data& data); + QModelIndex getIndex (const CellIndex& index) const; - void buildMap (Data& data); + void buildRegions(); + + void buildMap(); + + void addCell (const CellIndex& index, const CellDescription& description); + ///< May be called on a cell that is already in the map (in which case an update is + // performed) + + void addCells (int start, int end); + + void removeCell (const CellIndex& index); + ///< May be called on a cell that is not in the map (in which case the call is ignored) + + void addRegion (const std::string& region, unsigned int colour); + ///< May be called on a region that is already listed (in which case an update is + /// performed) + /// + /// \note This function does not update the region map. + + void removeRegion (const std::string& region); + ///< May be called on a region that is not listed (in which case the call is ignored) + /// + /// \note This function does not update the region map. + + void updateRegions (const std::vector& regions); + ///< Update cells affected by the listed regions + + void updateSize(); + + std::pair getSize() const; public: From c26a6f884f38ab75edaf692bf3eb75bce705fff9 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Fri, 12 Jul 2013 12:55:14 +0200 Subject: [PATCH 7/8] added region map tooltips --- apps/opencs/model/world/regionmap.cpp | 65 ++++++++++++++++++++++----- apps/opencs/model/world/regionmap.hpp | 6 +++ 2 files changed, 59 insertions(+), 12 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index fd278d2a6..a071bded8 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -12,6 +12,19 @@ CSMWorld::RegionMap::CellDescription::CellDescription() : mDeleted (false) {} +CSMWorld::RegionMap::CellDescription::CellDescription (const Record& cell) +{ + const Cell& cell2 = cell.get(); + + if (!cell2.isExterior()) + throw std::logic_error ("Interior cell in region map"); + + mDeleted = cell.isDeleted(); + + mRegion = cell2.mRegion; + mName = cell2.mName; +} + CSMWorld::RegionMap::CellIndex CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { return CellIndex (index.column()+mMin.first, index.row()+mMin.second); @@ -53,12 +66,7 @@ void CSMWorld::RegionMap::buildMap() if (cell2.isExterior()) { - CellDescription description; - - if (cell.isDeleted()) - description.mDeleted = true; - else - description.mRegion = cell2.mRegion; + CellDescription description (cell); CellIndex index (cell2.mData.mX, cell2.mData.mY); @@ -106,12 +114,7 @@ void CSMWorld::RegionMap::addCells (int start, int end) { CellIndex index (cell2.mData.mX, cell2.mData.mY); - CellDescription description; - - if (cell.isDeleted()) - description.mDeleted = true; - else - description.mRegion = cell2.mRegion; + CellDescription description (cell); addCell (index, description); } @@ -363,6 +366,44 @@ QVariant CSMWorld::RegionMap::data (const QModelIndex& index, int role) const return QBrush (Qt::DiagCrossPattern); } + if (role==Qt::ToolTipRole) + { + CellIndex cellIndex = getIndex (index); + + std::ostringstream stream; + + stream << cellIndex.first << ", " << cellIndex.second; + + std::map::const_iterator cell = + mMap.find (cellIndex); + + if (cell!=mMap.end()) + { + if (!cell->second.mName.empty()) + stream << " " << cell->second.mName; + + if (cell->second.mDeleted) + stream << " (deleted)"; + + if (!cell->second.mRegion.empty()) + { + stream << "
"; + + std::map::const_iterator iter = + mColours.find (Misc::StringUtils::lowerCase (cell->second.mRegion)); + + if (iter!=mColours.end()) + stream << cell->second.mRegion; + else + stream << "" << cell->second.mRegion << ""; + } + } + else + stream << " (no cell)"; + + return QString::fromUtf8 (stream.str().c_str()); + } + return QVariant(); } diff --git a/apps/opencs/model/world/regionmap.hpp b/apps/opencs/model/world/regionmap.hpp index 0a20324c4..7fb89f20a 100644 --- a/apps/opencs/model/world/regionmap.hpp +++ b/apps/opencs/model/world/regionmap.hpp @@ -7,6 +7,9 @@ #include +#include "record.hpp" +#include "cell.hpp" + namespace CSMWorld { class Data; @@ -28,8 +31,11 @@ namespace CSMWorld { bool mDeleted; std::string mRegion; + std::string mName; CellDescription(); + + CellDescription (const Record& cell); }; Data& mData; From 4cce466dc76feea1ae9a3e85e8da1db00bc803a2 Mon Sep 17 00:00:00 2001 From: Marc Zinnschlag Date: Sat, 13 Jul 2013 16:34:10 +0200 Subject: [PATCH 8/8] inverted region map y-axis --- apps/opencs/model/world/regionmap.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/apps/opencs/model/world/regionmap.cpp b/apps/opencs/model/world/regionmap.cpp index a071bded8..697c5f450 100644 --- a/apps/opencs/model/world/regionmap.cpp +++ b/apps/opencs/model/world/regionmap.cpp @@ -27,13 +27,15 @@ CSMWorld::RegionMap::CellDescription::CellDescription (const Record& cell) CSMWorld::RegionMap::CellIndex CSMWorld::RegionMap::getIndex (const QModelIndex& index) const { - return CellIndex (index.column()+mMin.first, index.row()+mMin.second); + return CellIndex (index.column()+mMin.first, + (mMax.second-mMin.second - index.row()-1)+mMin.second); } QModelIndex CSMWorld::RegionMap::getIndex (const CellIndex& index) const { // I hate you, Qt API naming scheme! - return QAbstractTableModel::index (index.second-mMin.second, index.first-mMin.first); + return QAbstractTableModel::index (mMax.second-mMin.second - (index.second-mMin.second)-1, + index.first-mMin.first); } void CSMWorld::RegionMap::buildRegions()