From f2ff2f298888d32d5dcdb7ae5a18e47f2b07fe6b Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sat, 8 Nov 2014 15:51:45 +1100
Subject: [PATCH 01/29] Render pathgrid points and edges.  Can move the points
 but the edges are not updated.  Visibility mask does not work properly
 because pathgrid points are considered as objects by the physics engine.

---
 apps/opencs/CMakeLists.txt                |   1 +
 apps/opencs/editor.cpp                    |   3 +
 apps/opencs/view/render/cell.cpp          | 120 ++++++++++++++++++++++
 apps/opencs/view/render/cell.hpp          |  14 +++
 apps/opencs/view/render/mousestate.cpp    |  85 ++++++++++-----
 apps/opencs/view/render/mousestate.hpp    |   2 +
 apps/opencs/view/render/pathgridpoint.cpp |  38 +++++++
 apps/opencs/view/render/pathgridpoint.hpp |  35 +++++++
 files/materials/pathgrid_pt.nif           | Bin 0 -> 1683 bytes
 9 files changed, 272 insertions(+), 26 deletions(-)
 create mode 100644 apps/opencs/view/render/pathgridpoint.cpp
 create mode 100644 apps/opencs/view/render/pathgridpoint.hpp
 create mode 100644 files/materials/pathgrid_pt.nif

diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt
index ec6f802cfa..29f93e5666 100644
--- a/apps/opencs/CMakeLists.txt
+++ b/apps/opencs/CMakeLists.txt
@@ -83,6 +83,7 @@ opencs_units (view/render
 opencs_units_noqt (view/render
     navigation navigation1st navigationfree navigationorbit lighting lightingday lightingnight
     lightingbright object cell terrainstorage textoverlay overlaymask overlaysystem mousestate
+    pathgridpoint
     )
 
 opencs_hdrs_noqt (view/render
diff --git a/apps/opencs/editor.cpp b/apps/opencs/editor.cpp
index 591667ebb7..beab0b6072 100644
--- a/apps/opencs/editor.cpp
+++ b/apps/opencs/editor.cpp
@@ -308,6 +308,9 @@ std::auto_ptr<sh::Factory> CS::Editor::setupGraphics()
     // for font used in overlays
     Ogre::Root::getSingleton().addResourceLocation ((mResources / "mygui").string(),
             "FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
+    // for pathgrid point nif
+    Ogre::Root::getSingleton().addResourceLocation ((mResources / "materials").string(),
+            "FileSystem", Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME, true);
 
     if (!boost::filesystem::exists (mCfgMgr.getCachePath()))
         boost::filesystem::create_directories (mCfgMgr.getCachePath());
diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 75e11cc10a..6979a4042b 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -3,6 +3,7 @@
 
 #include <OgreSceneManager.h>
 #include <OgreSceneNode.h>
+#include <OgreManualObject.h>
 
 #include <components/misc/stringops.hpp>
 #include <components/esm/loadland.hpp>
@@ -10,10 +11,60 @@
 #include "../../model/world/idtable.hpp"
 #include "../../model/world/columns.hpp"
 #include "../../model/world/data.hpp"
+#include "../../model/world/pathgrid.hpp"
 #include "../world/physicssystem.hpp"
 
 #include "elements.hpp"
 #include "terrainstorage.hpp"
+#include "pathgridpoint.hpp"
+
+// FIXME: redraw edges when pathgrid points are moved, added and deleted
+namespace CSVRender
+{
+    // PLEASE NOTE: pathgrid edge code copied and adapted from mwrender/debugging
+    static const std::string PG_LINE_MATERIAL = "pathgridLineMaterial";
+    static const int POINT_MESH_BASE = 35;
+    static const std::string DEBUGGING_GROUP = "debugging";
+}
+
+void CSVRender::Cell::createGridMaterials()
+{
+    if(Ogre::MaterialManager::getSingleton().getByName(PG_LINE_MATERIAL, DEBUGGING_GROUP).isNull())
+    {
+        Ogre::MaterialPtr lineMatPtr =
+            Ogre::MaterialManager::getSingleton().create(PG_LINE_MATERIAL, DEBUGGING_GROUP);
+        lineMatPtr->setReceiveShadows(false);
+        lineMatPtr->getTechnique(0)->setLightingEnabled(true);
+        lineMatPtr->getTechnique(0)->getPass(0)->setDiffuse(1,1,0,0);
+        lineMatPtr->getTechnique(0)->getPass(0)->setAmbient(1,1,0);
+        lineMatPtr->getTechnique(0)->getPass(0)->setSelfIllumination(1,1,0);
+    }
+}
+
+void CSVRender::Cell::destroyGridMaterials()
+{
+    if(!Ogre::MaterialManager::getSingleton().getByName(PG_LINE_MATERIAL, DEBUGGING_GROUP).isNull())
+        Ogre::MaterialManager::getSingleton().remove(PG_LINE_MATERIAL);
+}
+
+Ogre::ManualObject *CSVRender::Cell::createPathgridEdge(const std::string &name,
+        const Ogre::Vector3 &start, const Ogre::Vector3 &end)
+{
+    Ogre::ManualObject *result = mSceneMgr->createManualObject(name);
+
+    result->begin(PG_LINE_MATERIAL, Ogre::RenderOperation::OT_LINE_LIST);
+
+    Ogre::Vector3 direction = (end - start);
+    Ogre::Vector3 lineDisplacement = direction.crossProduct(Ogre::Vector3::UNIT_Z).normalisedCopy();
+    // move lines up a little, so they will be less covered by meshes/landscape
+    lineDisplacement = lineDisplacement * POINT_MESH_BASE + Ogre::Vector3(0, 0, 10);
+    result->position(start + lineDisplacement);
+    result->position(end + lineDisplacement);
+
+    result->end();
+
+    return result;
+}
 
 bool CSVRender::Cell::removeObject (const std::string& id)
 {
@@ -94,10 +145,28 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
                     esmLand->mLandData->mHeights, mX, mY, 0, worldsize / (verts-1), verts);
         }
     }
+
+    addPathgrid();
 }
 
 CSVRender::Cell::~Cell()
 {
+    // destroy manual objects
+    for(std::map<std::pair<int, int>, std::string>::iterator iter = mPgEdges.begin();
+        iter != mPgEdges.end(); ++iter)
+    {
+        if(mSceneMgr->hasManualObject((*iter).second))
+            mSceneMgr->destroyManualObject((*iter).second);
+    }
+    destroyGridMaterials();
+    Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(DEBUGGING_GROUP);
+
+    for(std::map<std::string, PathgridPoint *>::iterator iter (mPgPoints.begin());
+        iter!=mPgPoints.end(); ++iter)
+    {
+        delete iter->second;
+    }
+
     mPhysics->removeHeightField(mSceneMgr, mX, mY);
 
     for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
@@ -236,3 +305,54 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
     else
         return -std::numeric_limits<float>::max();
 }
+
+void CSVRender::Cell::addPathgrid()
+{
+    if(!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists(DEBUGGING_GROUP))
+        Ogre::ResourceGroupManager::getSingleton().createResourceGroup(DEBUGGING_GROUP);
+    createGridMaterials();
+
+    float worldsize = ESM::Land::REAL_SIZE;
+
+    CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgridCollection = mData.getPathgrids();
+    int index = pathgridCollection.searchId(mId);
+    if(index != -1)
+    {
+        const CSMWorld::Pathgrid pathgrid = pathgridCollection.getRecord(index).get();
+
+        std::vector<ESM::Pathgrid::Point>::const_iterator iter = pathgrid.mPoints.begin();
+        for(index = 0; iter != pathgrid.mPoints.end(); ++iter, ++index)
+        {
+            std::ostringstream stream;
+            stream << "Pathgrid_" << mId << "_" << index;
+            std::string name = stream.str();
+
+            Ogre::Vector3 pos =
+                Ogre::Vector3(worldsize*mX+(*iter).mX, worldsize*mY+(*iter).mY, (*iter).mZ);
+
+            mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics)));
+        }
+
+        for(ESM::Pathgrid::EdgeList::const_iterator it = pathgrid.mEdges.begin();
+            it != pathgrid.mEdges.end();
+            ++it)
+        {
+            Ogre::SceneNode *node = mCellNode->createChildSceneNode();
+            const ESM::Pathgrid::Edge &edge = *it;
+            const ESM::Pathgrid::Point &p0 = pathgrid.mPoints[edge.mV0];
+            const ESM::Pathgrid::Point &p1 = pathgrid.mPoints[edge.mV1];
+
+            std::ostringstream stream;
+            stream << mId << "_" << edge.mV0 << " " << edge.mV1;
+            std::string name = stream.str();
+
+            Ogre::ManualObject *line = createPathgridEdge(name,
+                Ogre::Vector3(worldsize*mX+p0.mX, worldsize*mY+p0.mY, p0.mZ),
+                Ogre::Vector3(worldsize*mX+p1.mX, worldsize*mY+p1.mY, p1.mZ));
+            line->setVisibilityFlags(Element_Pathgrid);
+            node->attachObject(line);
+
+            mPgEdges.insert(std::make_pair(std::make_pair(edge.mV0, edge.mV1), name));
+        }
+    }
+}
diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp
index d38f0c68d1..c88b593532 100644
--- a/apps/opencs/view/render/cell.hpp
+++ b/apps/opencs/view/render/cell.hpp
@@ -17,6 +17,7 @@ namespace Ogre
 {
     class SceneManager;
     class SceneNode;
+    class ManualObject;
 }
 
 namespace CSMWorld
@@ -31,12 +32,16 @@ namespace CSVWorld
 
 namespace CSVRender
 {
+    class PathgridPoint;
+
     class Cell
     {
             CSMWorld::Data& mData;
             std::string mId;
             Ogre::SceneNode *mCellNode;
             std::map<std::string, Object *> mObjects;
+            std::map<std::string, PathgridPoint *> mPgPoints;
+            std::map<std::pair<int, int>, std::string> mPgEdges;
             std::auto_ptr<Terrain::TerrainGrid> mTerrain;
             CSVWorld::PhysicsSystem *mPhysics;
             Ogre::SceneManager *mSceneMgr;
@@ -82,6 +87,15 @@ namespace CSVRender
             bool referenceAdded (const QModelIndex& parent, int start, int end);
 
             float getTerrainHeightAt(const Ogre::Vector3 &pos) const;
+
+        private:
+
+            // for drawing pathgrid points & lines
+            void createGridMaterials();
+            void destroyGridMaterials();
+            void addPathgrid();
+            Ogre::ManualObject *createPathgridEdge(const std::string &name,
+                    const Ogre::Vector3 &start, const Ogre::Vector3 &end);
     };
 }
 
diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index 45e846f74a..aa8f1a785b 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -206,6 +206,15 @@ namespace CSVRender
                         mCurrentObj = result.first;
 
                     }
+                    // print some debug info
+                    std::string referenceId = mPhysics->sceneNodeToRefId(result.first);
+                    if(referenceId != "")
+                        std::cout << "result: " << referenceId << std::endl;
+                    else
+                        std::cout << "result: " << result.first << std::endl;
+                    std::cout << "  hit pos "+ QString::number(result.second.x).toStdString()
+                            + ", " + QString::number(result.second.y).toStdString()
+                            + ", " + QString::number(result.second.z).toStdString() << std::endl;
                 }
                 break;
             }
@@ -213,15 +222,22 @@ namespace CSVRender
             {
                 // final placement
                 std::pair<bool, Ogre::Vector3> planeResult = mousePositionOnPlane(event->pos(), *mPlane);
-                if(planeResult.first)
+                if(planeResult.first && mGrabbedSceneNode != "")
                 {
-                    if(mGrabbedSceneNode != "")
-                    {
-                        std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
-                        Ogre::Vector3 pos = mOrigObjPos+planeRes.first*mOffset+planeResult.second-mOrigMousePos;
-                        // use the saved scene node name since the physics model has not moved yet
-                        std::string referenceId = mPhysics->sceneNodeToRefId(mGrabbedSceneNode);
+                    std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
+                    Ogre::Vector3 pos = mOrigObjPos+planeRes.first*mOffset+planeResult.second-mOrigMousePos;
+                    // use the saved scene node name since the physics model has not moved yet
+                    std::string referenceId = mPhysics->sceneNodeToRefId(mGrabbedSceneNode);
 
+                    if(QString(referenceId.c_str()).contains(QRegExp("^Pathgrid")))
+                    {
+                        // move pathgrid point, but don't save yet (need pathgrid
+                        // table feature & its data structure to be completed)
+                        // FIXME: need to signal PathgridPoint object of change
+                        placeObject(mGrabbedSceneNode, pos);
+                    }
+                    else
+                    {
                         mParent->mDocument.getUndoStack().beginMacro (QObject::tr("Move Object"));
                         mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel,
                             mIdTableModel->getModelIndex(referenceId, mColIndexPosX), pos.x));
@@ -230,22 +246,22 @@ namespace CSVRender
                         mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel,
                             mIdTableModel->getModelIndex(referenceId, mColIndexPosZ), pos.z));
                         mParent->mDocument.getUndoStack().endMacro();
-
-                        // FIXME: highlight current object?
-                        //mCurrentObj = mGrabbedSceneNode; // FIXME: doesn't work?
-                        mCurrentObj = "";                   // whether the object is selected
-
-                        mMouseState = Mouse_Edit;
-
-                        // reset states
-                        mCurrentMousePos = Ogre::Vector3(); // mouse pos to use in wheel event
-                        mOrigMousePos = Ogre::Vector3();    // starting pos of mouse in world space
-                        mOrigObjPos = Ogre::Vector3();      // starting pos of object in world space
-                        mGrabbedSceneNode = "";             // id of the object
-                        mOffset = 0.0f;                     // used for z-axis movement
-                        mOldPos = QPoint(0, 0);             // to calculate relative movement of mouse
                     }
-                }
+
+                    // FIXME: highlight current object?
+                    //mCurrentObj = mGrabbedSceneNode; // FIXME: doesn't work?
+                    mCurrentObj = "";                   // whether the object is selected
+
+                    mMouseState = Mouse_Edit;
+
+                    // reset states
+                    mCurrentMousePos = Ogre::Vector3(); // mouse pos to use in wheel event
+                    mOrigMousePos = Ogre::Vector3();    // starting pos of mouse in world space
+                    mOrigObjPos = Ogre::Vector3();      // starting pos of object in world space
+                    mGrabbedSceneNode = "";             // id of the object
+                    mOffset = 0.0f;                     // used for z-axis movement
+                    mOldPos = QPoint(0, 0);             // to calculate relative movement of mouse
+            }
                 break;
             }
             case Mouse_Edit:
@@ -266,9 +282,9 @@ namespace CSVRender
 
     void MouseState::mouseDoubleClickEvent (QMouseEvent *event)
     {
-        event->ignore();
-        //mPhysics->toggleDebugRendering(mSceneManager);
-        //mParent->flagAsModified();
+        //event->ignore();
+        mPhysics->toggleDebugRendering(mSceneManager);
+        mParent->flagAsModified();
     }
 
     bool MouseState::wheelEvent (QWheelEvent *event)
@@ -401,6 +417,10 @@ namespace CSVRender
         std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
         if(result.first != "")
         {
+                    std::cout << "result: " << result.first << std::endl;
+                    std::cout << "  hit pos "+ QString::number(result.second.x).toStdString()
+                            + ", " + QString::number(result.second.y).toStdString()
+                            + ", " + QString::number(result.second.z).toStdString() << std::endl;
             // FIXME: is there  a better way to distinguish terrain from objects?
             QString name  = QString(result.first.c_str());
             if(name.contains(QRegExp("^HeightField")))
@@ -423,7 +443,7 @@ namespace CSVRender
         std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
         if(result.first != "")
         {
-            // NOTE: anything not terrain is assumed to be an object
+            // NOTE: anything not terrain is assumed to be an object, e.g pathgrid points
             QString name  = QString(result.first.c_str());
             if(!name.contains(QRegExp("^HeightField")))
             {
@@ -460,4 +480,17 @@ namespace CSVRender
     {
         return mParent->getCamera()->getViewport();
     }
+
+    void MouseState::placeObject(const std::string sceneNode, const Ogre::Vector3 &pos)
+    {
+        mSceneManager->getSceneNode(sceneNode)->setPosition(pos);
+
+        // update physics
+        std::string refId = mPhysics->sceneNodeToRefId(sceneNode);
+
+        mPhysics->replaceObject(sceneNode, 1, pos, Ogre::Quaternion::IDENTITY);
+
+        // update all SceneWidgets and their SceneManagers
+        updateSceneWidgets();
+    }
 }
diff --git a/apps/opencs/view/render/mousestate.hpp b/apps/opencs/view/render/mousestate.hpp
index 27907bb331..36d04ef6ba 100644
--- a/apps/opencs/view/render/mousestate.hpp
+++ b/apps/opencs/view/render/mousestate.hpp
@@ -82,6 +82,8 @@ namespace CSVRender
             std::pair<Ogre::Vector3, Ogre::Vector3> planeAxis();
             void updateSceneWidgets();
 
+            void placeObject(const std::string sceneNode, const Ogre::Vector3 &pos); // FIXME
+
             Ogre::Camera *getCamera();     // friend access
             Ogre::Viewport *getViewport(); // friend access
     };
diff --git a/apps/opencs/view/render/pathgridpoint.cpp b/apps/opencs/view/render/pathgridpoint.cpp
new file mode 100644
index 0000000000..2455ea1a83
--- /dev/null
+++ b/apps/opencs/view/render/pathgridpoint.cpp
@@ -0,0 +1,38 @@
+#include "pathgridpoint.hpp"
+
+#include <iostream> // FIXME
+
+#include <OgreSceneManager.h>
+#include <OgreSceneNode.h>
+
+//#include "../../model/world/pathgrid.hpp"
+#include "../world/physicssystem.hpp"
+
+#include "elements.hpp"
+
+namespace CSVRender
+{
+    PathgridPoint::PathgridPoint(const std::string &name,
+            Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, CSVWorld::PhysicsSystem *physics)
+        : mBase(cellNode), mPhysics(physics)
+    {
+        mBase = cellNode->createChildSceneNode();
+
+        mPgPoint = NifOgre::Loader::createObjects(mBase, "pathgrid_pt.nif");
+        mPgPoint->setVisibilityFlags(Element_Pathgrid);
+        mBase->setPosition(pos);
+
+        physics->addObject("pathgrid_pt.nif",
+                mBase->getName(), name, 1, pos, Ogre::Quaternion::IDENTITY);
+    }
+
+    PathgridPoint::~PathgridPoint()
+    {
+        mPgPoint.setNull();
+
+        mPhysics->removeObject(mBase->getName());
+
+        if (mBase)
+            mBase->getCreator()->destroySceneNode(mBase);
+    }
+}
diff --git a/apps/opencs/view/render/pathgridpoint.hpp b/apps/opencs/view/render/pathgridpoint.hpp
new file mode 100644
index 0000000000..f4fa42790d
--- /dev/null
+++ b/apps/opencs/view/render/pathgridpoint.hpp
@@ -0,0 +1,35 @@
+#ifndef OPENCS_VIEW_PATHGRIDPOINT_H
+#define OPENCS_VIEW_PATHGRIDPOINT_H
+
+#include <components/nifogre/ogrenifloader.hpp>
+
+namespace Ogre
+{
+    class Vector3;
+    class SceneNode;
+    class SceneManager;
+}
+
+namespace CSVWorld
+{
+    class PhysicsSystem;
+}
+
+namespace CSVRender
+{
+    class PathgridPoint
+    {
+            CSVWorld::PhysicsSystem *mPhysics; // local copy
+            NifOgre::ObjectScenePtr mPgPoint;
+            Ogre::SceneNode *mBase;
+
+        public:
+
+            PathgridPoint(const std::string &name,
+                Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, CSVWorld::PhysicsSystem *physics);
+
+            ~PathgridPoint();
+    };
+}
+
+#endif // OPENCS_VIEW_PATHGRIDPOINT_H
diff --git a/files/materials/pathgrid_pt.nif b/files/materials/pathgrid_pt.nif
new file mode 100644
index 0000000000000000000000000000000000000000..579b03036c05d5deefe564880dbe312ebbb671f2
GIT binary patch
literal 1683
zcmb_c&2G~`5T2w?TS{o5<)=VFsl*Xg0afBs5fvdM4p>M<NE}dPwVTFLLadEBRN~+V
z;02o3sJOOLU!vj-xbO;u?_00qpSUy!##-+;GvCgP*Sq7Y@NK`Z^iVb4_&vM^y6=Tg
z8y{iv2VWZ7TTMLAm#QMpqKM36xV}3$P%A*eVLJ%hS{<}|o<3A#D|NWC61B3LNSR)=
z#Ve3jDi};lS;BDr58B`BdVyMlmX;87kB0vKu+dD)aJV8P#S~?F-ZqETEP-<ptp>~t
z{C?Np?FC)$y&eQghexm&7u~;CWz{hD0=54Y+fzoWSk5~yRND7?35Oe)*tVL@XADTG
z$msazOQxZWjz48KHiA7Fu|IzA>~=bx)-sfI<6e8A_t<Q#Nc~kj$Ts*KAOgnBF~Q(X
zB>M49L(g%nZumu9vX(p%#5uQlEoZ8R#7-VYypD5R1EVB3_buexwNtrloXhIZ@iTF9
z{Hz^${+)}5IF;yZR(x^t`RvblZ2ox}hRvJ-a%RA%8BP(Knrxg>jG7~^xAk0x1sv9V
z%5N;g-(S>Q9HU>GX1)qD24gYiESTIxGG_#)x;!zCin-NQ+-pZ_Xf6w~DA&+ju7hzy
zmSsh5$}QZ@HMuQ!<gVP4b-dT*zC4hJvVr%8JVK;d+R^0lI!R4-&bJ!ut@)8DiF(l%
zdlxXV9ZX;9`TuaKvxh&ehUn_etRg>;I=7Xbdk!1z;9(1`L8re49DCSSsnHYnc}(&b
DaQ$}<

literal 0
HcmV?d00001


From 2c09b9ba212c699e53045e81d5d8ba936a67ce9a Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sun, 9 Nov 2014 12:34:26 +1100
Subject: [PATCH 02/29] Fix scene node being erased in the object reference
 map.

---
 apps/opencs/view/world/physicssystem.cpp | 15 +++++++++------
 1 file changed, 9 insertions(+), 6 deletions(-)

diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp
index 57909f4d38..13b29ba5a5 100644
--- a/apps/opencs/view/world/physicssystem.cpp
+++ b/apps/opencs/view/world/physicssystem.cpp
@@ -70,9 +70,6 @@ namespace CSVWorld
 
         if(referenceId != "")
         {
-            mSceneNodeToRefId.erase(sceneNodeName);
-            mSceneNodeToMesh.erase(sceneNodeName);
-
             // find which SceneManager has this object
             Ogre::SceneManager *sceneManager = findSceneManager(sceneNodeName);
             if(!sceneManager)
@@ -97,7 +94,8 @@ namespace CSVWorld
                 mRefIdToSceneNode.begin();
             for(; itRef != mRefIdToSceneNode.end(); ++itRef)
             {
-                if((*itRef).second.find(sceneManager) != (*itRef).second.end())
+                if((*itRef).first == referenceId &&
+                        (*itRef).second.find(sceneManager) != (*itRef).second.end())
                 {
                     (*itRef).second.erase(sceneManager);
                     break;
@@ -110,6 +108,9 @@ namespace CSVWorld
                 mEngine->removeRigidBody(referenceId);
                 mEngine->deleteRigidBody(referenceId);
             }
+
+            mSceneNodeToRefId.erase(sceneNodeName);
+            mSceneNodeToMesh.erase(sceneNodeName);
         }
     }
 
@@ -252,11 +253,13 @@ namespace CSVWorld
             return std::make_pair("", Ogre::Vector3(0,0,0));
         else
         {
+            // FIXME: maybe below logic belongs in the caller, i.e. terrainUnderCursor or
+            // objectUnderCursor
             std::string name = refIdToSceneNode(result.first, sceneMgr);
             if(name == "")
-                name = result.first;
+                name = result.first; // prob terrain
             else
-                name = refIdToSceneNode(result.first, sceneMgr);
+                name = refIdToSceneNode(result.first, sceneMgr); // prob object
 
             return std::make_pair(name, ray.getPoint(farClipDist*result.second));
         }

From a5a76cadca8f5cc4002eec0f6d2cf2c0ffeb997a Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sun, 9 Nov 2014 14:36:58 +1100
Subject: [PATCH 03/29] Adding, deleting and moving pathgrid points work. 
 However still not saving to the document.

---
 apps/opencs/view/render/cell.cpp              | 204 +++++++++++++++++-
 apps/opencs/view/render/cell.hpp              |  16 +-
 apps/opencs/view/render/mousestate.cpp        |  38 +++-
 apps/opencs/view/render/mousestate.hpp        |   3 +-
 .../view/render/pagedworldspacewidget.cpp     |  94 ++++++++
 .../view/render/pagedworldspacewidget.hpp     |  13 ++
 apps/opencs/view/render/pathgridpoint.cpp     |  35 +++
 apps/opencs/view/render/pathgridpoint.hpp     |   4 +
 .../view/render/unpagedworldspacewidget.cpp   |  18 ++
 .../view/render/unpagedworldspacewidget.hpp   |   6 +
 apps/opencs/view/render/worldspacewidget.cpp  |  50 +++++
 apps/opencs/view/render/worldspacewidget.hpp  |  10 +
 12 files changed, 474 insertions(+), 17 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 6979a4042b..e703c8a9dc 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -112,7 +112,8 @@ bool CSVRender::Cell::addObjects (int start, int end)
 
 CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
     const std::string& id, CSVWorld::PhysicsSystem *physics, const Ogre::Vector3& origin)
-: mData (data), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager), mPhysics(physics)
+: mData (data), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager), mPhysics(physics),
+    mPathgridId("")
 {
     mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
     mCellNode->setPosition (origin);
@@ -146,7 +147,7 @@ CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
         }
     }
 
-    addPathgrid();
+    loadPathgrid();
 }
 
 CSVRender::Cell::~Cell()
@@ -156,7 +157,13 @@ CSVRender::Cell::~Cell()
         iter != mPgEdges.end(); ++iter)
     {
         if(mSceneMgr->hasManualObject((*iter).second))
+        {
+            Ogre::ManualObject *manual = mSceneMgr->getManualObject((*iter).second);
+            Ogre::SceneNode *node = manual->getParentSceneNode();
             mSceneMgr->destroyManualObject((*iter).second);
+            if(mSceneMgr->hasSceneNode(node->getName()))
+                mSceneMgr->destroySceneNode(node);
+        }
     }
     destroyGridMaterials();
     Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(DEBUGGING_GROUP);
@@ -306,7 +313,14 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
         return -std::numeric_limits<float>::max();
 }
 
-void CSVRender::Cell::addPathgrid()
+// FIXME:
+//  - updating indicies
+//  - collision mask
+//  - add pathgrid point above an object
+//  - adding edges
+//  - save to document & signals
+//  - repainting edges while moving
+void CSVRender::Cell::loadPathgrid()
 {
     if(!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists(DEBUGGING_GROUP))
         Ogre::ResourceGroupManager::getSingleton().createResourceGroup(DEBUGGING_GROUP);
@@ -318,19 +332,20 @@ void CSVRender::Cell::addPathgrid()
     int index = pathgridCollection.searchId(mId);
     if(index != -1)
     {
-        const CSMWorld::Pathgrid pathgrid = pathgridCollection.getRecord(index).get();
+        const CSMWorld::Pathgrid &pathgrid = pathgridCollection.getRecord(index).get();
+        mPathgridId = pathgrid.mId; // FIXME: temporary storage (should be document)
 
+        mPoints.resize(pathgrid.mPoints.size());
         std::vector<ESM::Pathgrid::Point>::const_iterator iter = pathgrid.mPoints.begin();
         for(index = 0; iter != pathgrid.mPoints.end(); ++iter, ++index)
         {
-            std::ostringstream stream;
-            stream << "Pathgrid_" << mId << "_" << index;
-            std::string name = stream.str();
+            std::string name = PathgridPoint::getName(pathgrid.mId, index);
 
             Ogre::Vector3 pos =
                 Ogre::Vector3(worldsize*mX+(*iter).mX, worldsize*mY+(*iter).mY, (*iter).mZ);
 
             mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics)));
+            mPoints[index] = *iter; // FIXME: temporary storage (should be document)
         }
 
         for(ESM::Pathgrid::EdgeList::const_iterator it = pathgrid.mEdges.begin();
@@ -343,7 +358,7 @@ void CSVRender::Cell::addPathgrid()
             const ESM::Pathgrid::Point &p1 = pathgrid.mPoints[edge.mV1];
 
             std::ostringstream stream;
-            stream << mId << "_" << edge.mV0 << " " << edge.mV1;
+            stream << pathgrid.mId << "_" << edge.mV0 << " " << edge.mV1;
             std::string name = stream.str();
 
             Ogre::ManualObject *line = createPathgridEdge(name,
@@ -353,6 +368,179 @@ void CSVRender::Cell::addPathgrid()
             node->attachObject(line);
 
             mPgEdges.insert(std::make_pair(std::make_pair(edge.mV0, edge.mV1), name));
+            mEdges.push_back(*it); // FIXME: temporary storage (should be document)
         }
     }
 }
+
+// NOTE: pos is in world coordinates
+// FIXME: save to the document
+void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos)
+{
+    std::string name = PathgridPoint::getName(mId, mPoints.size());
+
+    mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics)));
+
+    // store to document
+    ESM::Pathgrid::Point point((int)pos.x, (int)pos.y, (int)pos.z);
+    point.mConnectionNum = 0;
+    mPoints.push_back(point);
+    mPathgridId = mId;
+    // FIXME: update other scene managers
+}
+
+// FIXME: save to the document
+void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
+{
+    std::pair<std::string, int> result = PathgridPoint::getIdAndIndex(name);
+    if(result.first == "")
+        return;
+
+    std::string pathgridId = result.first;
+    int index = result.second;
+
+    // check if the point exists
+    if(index < 0 || mPathgridId != pathgridId || index >= mPoints.size())
+        return;
+
+    int numToDelete = mPoints[index].mConnectionNum * 2; // for sanity check later
+    int edgeCount = 0;
+
+    // find edges to delete
+    std::vector<std::pair<int, int> > edges;
+    for(unsigned i = 0; i < mEdges.size(); ++i)
+    {
+        if(mEdges[i].mV0 == index || mEdges[i].mV1 == index)
+        {
+            for(std::map<std::pair<int, int>, std::string>::iterator iter = mPgEdges.begin();
+                iter != mPgEdges.end(); ++iter)
+            {
+                if((*iter).first.first == index || (*iter).first.second == index)
+                {
+                    edges.push_back(std::make_pair((*iter).first.first, (*iter).first.second));
+                }
+            }
+        }
+    }
+
+    // delete the edges
+    for(std::vector<std::pair<int, int> >::iterator iter = edges.begin();
+        iter != edges.end(); ++iter)
+    {
+        std::string name = mPgEdges[*iter];
+        if(mSceneMgr->hasManualObject(name))
+        {
+            // remove manual objects
+            Ogre::ManualObject *manual = mSceneMgr->getManualObject(name);
+            Ogre::SceneNode *node = manual->getParentSceneNode();
+            mSceneMgr->destroyManualObject(name);
+            if(mSceneMgr->hasSceneNode(node->getName()))
+                mSceneMgr->destroySceneNode(node);
+
+            edgeCount++; // for sanity check later
+
+            // update map
+            mPgEdges.erase(*iter);
+
+            // update document
+            assert(mPoints[(*iter).first].mConnectionNum > 0);
+            mPoints[(*iter).first].mConnectionNum -= 1;
+            for(unsigned i = mEdges.size() - 1; i > 0; --i)
+            {
+                if(mEdges[i].mV0 == index || mEdges[i].mV1 == index)
+                    mEdges.erase(mEdges.begin() + i);
+            }
+        }
+    }
+
+    if(edgeCount != numToDelete)
+    {
+        // WARNING: continue anyway?  Or should this be an exception?
+        std::cerr << "The no of edges del does not match the no of conn for: "
+            << mPathgridId + "_" + QString::number(index).toStdString() << std::endl;
+    }
+
+    if(edgeCount || mPoints[index].mConnectionNum == 0)
+    {
+        // remove the point
+        delete mPgPoints[name];
+        mPgPoints.erase(name);
+        // FIXME: update other scene managers
+    }
+
+    // store to document
+    //mPoints.erase(mPoints.begin() + index);  // WARNING: Can't erase because the index will change
+    // FIXME: it should be possible to refresh indicies but that means index values
+    // can't be stored in maps, names, etc
+}
+
+// NOTE: newPos is in world coordinates
+void CSVRender::Cell::pathgridPointMoved(const std::string &name, const Ogre::Vector3 &newPos)
+{
+    std::pair<std::string, int> result = PathgridPoint::getIdAndIndex(name);
+    if(result.first == "")
+        return;
+
+    std::string pathgridId = result.first;
+    int index = result.second;
+
+    // check if the point exists
+    if(index < 0 || mPathgridId != pathgridId || index >= mPoints.size())
+        return;
+
+    float worldsize = ESM::Land::REAL_SIZE;
+
+    // delete then recreate the edges
+    for(unsigned i = 0; i < mEdges.size(); ++i)
+    {
+        if(mEdges[i].mV0 == index || mEdges[i].mV1 == index)
+        {
+            std::ostringstream stream;
+            stream << pathgridId << "_" << mEdges[i].mV0 << " " << mEdges[i].mV1;
+            std::string name = stream.str();
+
+            if(mSceneMgr->hasManualObject(name))
+            {
+                // remove manual objects
+                Ogre::ManualObject *manual = mSceneMgr->getManualObject(name);
+                Ogre::SceneNode *node = manual->getParentSceneNode();
+                mSceneMgr->destroyManualObject(name);
+
+                if(mEdges[i].mV0 == index)
+                {
+                    const ESM::Pathgrid::Point &p1 = mPoints[mEdges[i].mV1];
+
+                    Ogre::ManualObject *line = createPathgridEdge(name,
+                        newPos,
+                        Ogre::Vector3(worldsize*mX+p1.mX, worldsize*mY+p1.mY, p1.mZ));
+                    line->setVisibilityFlags(Element_Pathgrid);
+                    node->attachObject(line);
+                }
+                else if(mEdges[i].mV1 == index)
+                {
+                    const ESM::Pathgrid::Point &p0 = mPoints[mEdges[i].mV0];
+
+                    Ogre::ManualObject *line = createPathgridEdge(name,
+                        Ogre::Vector3(worldsize*mX+p0.mX, worldsize*mY+p0.mY, p0.mZ),
+                        newPos);
+                    line->setVisibilityFlags(Element_Pathgrid);
+                    node->attachObject(line);
+                }
+            }
+        }
+    }
+}
+
+// FIXME: save to the document
+void CSVRender::Cell::addPathgridEdge()
+{
+    // check if the points exist
+    // update the edges
+    // store to document
+    // FIXME: update other scene managers
+}
+
+// FIXME: save to the document
+void CSVRender::Cell::removePathgridEdge()
+{
+}
diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp
index c88b593532..5d7b9088a5 100644
--- a/apps/opencs/view/render/cell.hpp
+++ b/apps/opencs/view/render/cell.hpp
@@ -8,6 +8,7 @@
 #include <OgreVector3.h>
 
 #include <components/terrain/terraingrid.hpp>
+#include <components/esm/loadpgrd.hpp>  // FIXME: temporaty storage until saving to document
 
 #include "object.hpp"
 
@@ -23,6 +24,7 @@ namespace Ogre
 namespace CSMWorld
 {
     class Data;
+    class Pathgrid;
 }
 
 namespace CSVWorld
@@ -42,6 +44,11 @@ namespace CSVRender
             std::map<std::string, Object *> mObjects;
             std::map<std::string, PathgridPoint *> mPgPoints;
             std::map<std::pair<int, int>, std::string> mPgEdges;
+
+            ESM::Pathgrid::PointList mPoints; // FIXME: temporary storage until saving to document
+            ESM::Pathgrid::EdgeList mEdges; // FIXME: temporary storage until saving to document
+            std::string mPathgridId; // FIXME: temporary storage until saving to document
+
             std::auto_ptr<Terrain::TerrainGrid> mTerrain;
             CSVWorld::PhysicsSystem *mPhysics;
             Ogre::SceneManager *mSceneMgr;
@@ -88,14 +95,21 @@ namespace CSVRender
 
             float getTerrainHeightAt(const Ogre::Vector3 &pos) const;
 
+            void pathgridPointAdded(const Ogre::Vector3 &pos);
+            void pathgridPointMoved(const std::string &name, const Ogre::Vector3 &newPos);
+            void pathgridPointRemoved(const std::string &name);
+
         private:
 
             // for drawing pathgrid points & lines
             void createGridMaterials();
             void destroyGridMaterials();
-            void addPathgrid();
+            void loadPathgrid();
             Ogre::ManualObject *createPathgridEdge(const std::string &name,
                     const Ogre::Vector3 &start, const Ogre::Vector3 &end);
+
+            void addPathgridEdge();
+            void removePathgridEdge();
     };
 }
 
diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index aa8f1a785b..bceae716cf 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -209,9 +209,9 @@ namespace CSVRender
                     // print some debug info
                     std::string referenceId = mPhysics->sceneNodeToRefId(result.first);
                     if(referenceId != "")
-                        std::cout << "result: " << referenceId << std::endl;
+                        std::cout << "result grab release: " << referenceId << std::endl;
                     else
-                        std::cout << "result: " << result.first << std::endl;
+                        std::cout << "result grab release: " << result.first << std::endl;
                     std::cout << "  hit pos "+ QString::number(result.second.x).toStdString()
                             + ", " + QString::number(result.second.y).toStdString()
                             + ", " + QString::number(result.second.z).toStdString() << std::endl;
@@ -234,7 +234,20 @@ namespace CSVRender
                         // move pathgrid point, but don't save yet (need pathgrid
                         // table feature & its data structure to be completed)
                         // FIXME: need to signal PathgridPoint object of change
-                        placeObject(mGrabbedSceneNode, pos);
+                        std::pair<std::string, Ogre::Vector3> result =
+                            terrainUnderCursor(event->x(), event->y()); // FIXME: some pathgrid points are on objects
+                        if(result.first != "")
+                        {
+                            // FIXME: rather than just updating at the end, should
+                            // consider providing visual feedback of terrain height
+                            // while dragging the pathgrid point (maybe check whether
+                            // the object is a pathgrid point at the begging and set
+                            // a flag?)
+                            // FIXME: this also disallows dragging over objects, so
+                            // may need to use ignoreObjects while raycasting
+                            placeObject(mGrabbedSceneNode, result.second);
+                            mParent->pathgridMoved(referenceId, result.second);
+                        }
                     }
                     else
                     {
@@ -389,6 +402,21 @@ namespace CSVRender
         }
     }
 
+    // FIXME: castRay converts referenceId to scene node name only to be re-converted
+    // here - investigate whether refactoring required
+    std::pair<std::string, Ogre::Vector3> MouseState::pgPointUnderCursor(const int mouseX, const int mouseY)
+    {
+        std::pair<std::string, Ogre::Vector3> result = objectUnderCursor(mouseX, mouseY);
+        std::string referenceId = mPhysics->sceneNodeToRefId(result.first);
+        if(result.first != "" &&
+                referenceId != "" && QString(referenceId.c_str()).contains(QRegExp("^Pathgrid")))
+        {
+            return std::make_pair(referenceId, result.second);
+        }
+
+        return std::make_pair("", Ogre::Vector3());
+    }
+
     std::pair<bool, Ogre::Vector3> MouseState::mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane)
     {
         // using a really small value seems to mess up with the projections
@@ -417,10 +445,6 @@ namespace CSVRender
         std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
         if(result.first != "")
         {
-                    std::cout << "result: " << result.first << std::endl;
-                    std::cout << "  hit pos "+ QString::number(result.second.x).toStdString()
-                            + ", " + QString::number(result.second.y).toStdString()
-                            + ", " + QString::number(result.second.z).toStdString() << std::endl;
             // FIXME: is there  a better way to distinguish terrain from objects?
             QString name  = QString(result.first.c_str());
             if(name.contains(QRegExp("^HeightField")))
diff --git a/apps/opencs/view/render/mousestate.hpp b/apps/opencs/view/render/mousestate.hpp
index 36d04ef6ba..b56860cace 100644
--- a/apps/opencs/view/render/mousestate.hpp
+++ b/apps/opencs/view/render/mousestate.hpp
@@ -71,13 +71,14 @@ namespace CSVRender
             void mouseReleaseEvent (QMouseEvent *event);
             void mouseDoubleClickEvent (QMouseEvent *event);
             bool wheelEvent (QWheelEvent *event);
+            std::pair<std::string, Ogre::Vector3> pgPointUnderCursor(const int mouseX, const int mouseY);
+            std::pair<std::string, Ogre::Vector3> terrainUnderCursor(const int mouseX, const int mouseY);
 
             void cancelDrag();
 
         private:
 
             std::pair<bool, Ogre::Vector3> mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane);
-            std::pair<std::string, Ogre::Vector3> terrainUnderCursor(const int mouseX, const int mouseY);
             std::pair<std::string, Ogre::Vector3> objectUnderCursor(const int mouseX, const int mouseY);
             std::pair<Ogre::Vector3, Ogre::Vector3> planeAxis();
             void updateSceneWidgets();
diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp
index 49e7e1f097..e528733c01 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.cpp
@@ -22,6 +22,7 @@
 
 #include "../widget/scenetooltoggle.hpp"
 
+#include "pathgridpoint.hpp"
 #include "elements.hpp"
 
 bool CSVRender::PagedWorldspaceWidget::adjustCells()
@@ -295,6 +296,99 @@ void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent
             flagAsModified();
 }
 
+//void CSVRender::PagedWorldspaceWidget::pathgridAdded (const QModelIndex& parent,
+//    int start, int end)
+//{
+//    // FIXME:
+//}
+//
+//void CSVRender::PagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft,
+//    const QModelIndex& bottomRight)
+//{
+//    // FIXME:
+//}
+//
+//void CSVRender::PagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelIndex& parent,
+//    int start, int end)
+//{
+//    // FIXME:
+//}
+
+CSVRender::Cell *CSVRender::PagedWorldspaceWidget::findCell(const std::string &cellId)
+{
+    const CSMWorld::IdCollection<CSMWorld::Cell>& cells = mDocument.getData().getCells();
+
+    std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
+    for(; iter!= mCells.end(); ++iter)
+    {
+        int index = cells.searchId(cellId);
+
+        if (index != -1 && cellId == iter->first.getId (mWorldspace))
+        {
+            return iter->second;
+        }
+    }
+
+    return NULL;
+}
+
+// FIXME: pathgrid may be inserted above an object, the parsing needs to change
+void CSVRender::PagedWorldspaceWidget::pathgridInserted (const std::string &terrain, const Ogre::Vector3 &pos)
+{
+    // decode name
+    QString id = QString(terrain.c_str());
+    QRegExp terrainRe("^HeightField_([\\d-]+)_([\\d-]+)$");
+
+    if (id.isEmpty() || !id.startsWith("HeightField_"))
+        return;
+
+    if (terrainRe.indexIn(id) == -1)
+        return;
+
+    int cellX = terrainRe.cap(1).toInt();
+    int cellY = terrainRe.cap(2).toInt();
+
+    std::ostringstream stream;
+    stream << "#" << cellX << " " << cellY;
+
+    Cell *cell = findCell(stream.str());
+    if(cell)
+    {
+        cell->pathgridPointAdded(pos);
+        flagAsModified();
+
+        return;
+    }
+}
+
+void CSVRender::PagedWorldspaceWidget::pathgridMoved (const std::string &pgName, const Ogre::Vector3 &pos)
+{
+    std::pair<std::string, int> result = PathgridPoint::getIdAndIndex(pgName);
+
+    Cell *cell = findCell(result.first);
+    if(cell)
+    {
+        cell->pathgridPointMoved(pgName, pos);
+        flagAsModified();
+
+        return;
+    }
+}
+
+void CSVRender::PagedWorldspaceWidget::pathgridAboutToBeRemoved (const std::string &pgName)
+{
+    std::pair<std::string, int> result = PathgridPoint::getIdAndIndex(pgName);
+
+    Cell *cell = findCell(result.first);
+    if(cell)
+    {
+        cell->pathgridPointRemoved(pgName);
+        flagAsModified();
+
+        return;
+    }
+}
+
 std::string CSVRender::PagedWorldspaceWidget::getStartupInstruction()
 {
     Ogre::Vector3 position = getCamera()->getPosition();
diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp
index fe79e761e5..b5ca91880b 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.hpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.hpp
@@ -49,8 +49,16 @@ namespace CSVRender
 
             virtual void referenceAdded (const QModelIndex& index, int start, int end);
 
+            //virtual void pathgridAdded (const QModelIndex& parent, int start, int end);
+
+            //virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
+
+            //virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end);
+
             virtual std::string getStartupInstruction();
 
+            Cell *findCell(const std::string &cellId);
+
         public:
 
             PagedWorldspaceWidget (QWidget *parent, CSMDoc::Document& document);
@@ -87,6 +95,11 @@ namespace CSVRender
 
             virtual void mouseDoubleClickEvent (QMouseEvent *event);
 
+            // FIXME: temporary only until signals from the document is implemented
+            virtual void pathgridInserted (const std::string &terrain, const Ogre::Vector3 &pos);
+            virtual void pathgridMoved (const std::string &pgName, const Ogre::Vector3 &pos);
+            virtual void pathgridAboutToBeRemoved (const std::string &pgName);
+
         signals:
 
             void cellSelectionChanged (const CSMWorld::CellSelection& selection);
diff --git a/apps/opencs/view/render/pathgridpoint.cpp b/apps/opencs/view/render/pathgridpoint.cpp
index 2455ea1a83..3e365932f9 100644
--- a/apps/opencs/view/render/pathgridpoint.cpp
+++ b/apps/opencs/view/render/pathgridpoint.cpp
@@ -2,6 +2,8 @@
 
 #include <iostream> // FIXME
 
+#include <QRegExp>
+
 #include <OgreSceneManager.h>
 #include <OgreSceneNode.h>
 
@@ -35,4 +37,37 @@ namespace CSVRender
         if (mBase)
             mBase->getCreator()->destroySceneNode(mBase);
     }
+
+    // FIXME: Is there a way to identify the pathgrid point other than via the index?
+    // ESM::Pathgrid::Edge itself uses the indicies so any change (add/delete) must be
+    // propagated everywhere.
+    std::pair<std::string, int> PathgridPoint::getIdAndIndex(const std::string &name)
+    {
+        // decode name
+        QString id = QString(name.c_str());
+        QRegExp pathgridRe("^Pathgrid_(.+)_(\\d+)$");
+
+        if (id.isEmpty() || !id.startsWith("Pathgrid_"))
+            return std::make_pair("", -1);
+
+        std::string pathgridId = "";
+        int index = -1;
+        if (pathgridRe.indexIn(id) != -1)
+        {
+            pathgridId = pathgridRe.cap(1).toStdString();
+            index = pathgridRe.cap(2).toInt();
+
+            return std::make_pair(pathgridId, index);
+        }
+
+        return std::make_pair("", -1);
+    }
+
+    std::string PathgridPoint::getName(const std::string &pathgridId, const int index)
+    {
+        std::ostringstream stream;
+        stream << "Pathgrid_" << pathgridId << "_" << index;
+
+        return stream.str();
+    }
 }
diff --git a/apps/opencs/view/render/pathgridpoint.hpp b/apps/opencs/view/render/pathgridpoint.hpp
index f4fa42790d..3419082c3a 100644
--- a/apps/opencs/view/render/pathgridpoint.hpp
+++ b/apps/opencs/view/render/pathgridpoint.hpp
@@ -29,6 +29,10 @@ namespace CSVRender
                 Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, CSVWorld::PhysicsSystem *physics);
 
             ~PathgridPoint();
+
+            static std::pair<std::string, int> getIdAndIndex(const std::string &name);
+
+            static std::string getName(const std::string &pathgridId, int index);
     };
 }
 
diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp
index 8012b1b246..a1a8a89219 100644
--- a/apps/opencs/view/render/unpagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp
@@ -160,6 +160,24 @@ void CSVRender::UnpagedWorldspaceWidget::referenceAdded (const QModelIndex& pare
             flagAsModified();
 }
 
+//void CSVRender::UnpagedWorldspaceWidget::pathgridAdded (const QModelIndex& parent,
+//    int start, int end)
+//{
+//    // FIXME:
+//}
+//
+//void CSVRender::UnpagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft,
+//    const QModelIndex& bottomRight)
+//{
+//    // FIXME:
+//}
+//
+//void CSVRender::UnpagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelIndex& parent,
+//    int start, int end)
+//{
+//    // FIXME:
+//}
+
 std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction()
 {
     Ogre::Vector3 position = getCamera()->getPosition();
diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp
index 5924abaa9e..ee397d467a 100644
--- a/apps/opencs/view/render/unpagedworldspacewidget.hpp
+++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp
@@ -62,6 +62,12 @@ namespace CSVRender
 
             virtual void referenceAdded (const QModelIndex& index, int start, int end);
 
+            //virtual void pathgridAdded (const QModelIndex& index, int start, int end);
+
+            //virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
+
+            //virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end);
+
             virtual std::string getStartupInstruction();
 
         private slots:
diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp
index 9c86760640..1fedc8aa12 100644
--- a/apps/opencs/view/render/worldspacewidget.cpp
+++ b/apps/opencs/view/render/worldspacewidget.cpp
@@ -8,6 +8,7 @@
 #include <OgreEntity.h>
 
 #include <QtGui/qevent.h>
+#include <QPoint>
 
 #include "../../model/world/universalid.hpp"
 #include "../../model/world/idtable.hpp"
@@ -54,6 +55,16 @@ CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidg
     connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
         this, SLOT (debugProfileAboutToBeRemoved (const QModelIndex&, int, int)));
 
+    //QAbstractItemModel *pathgrids =
+        //document.getData().getTableModel (CSMWorld::UniversalId::Type_Pathgrid);
+
+    //connect (pathgrids, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
+        //this, SLOT (pathgridAdded (const QModelIndex&, int, int)));
+    //connect (pathgrids, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
+        //this, SLOT (pathgridDataChanged (const QModelIndex&, const QModelIndex&)));
+    //connect (pathgrids, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
+        //this, SLOT (pathgridAboutToBeRemoved (const QModelIndex&, int, int)));
+
     // associate WorldSpaceWidgets (and their SceneManagers) with Documents
     // then create physics if there is a new document
     mPhysics = CSVWorld::PhysicsManager::instance()->addSceneWidget(document, this);
@@ -389,12 +400,51 @@ void CSVRender::WorldspaceWidget::wheelEvent (QWheelEvent *event)
         SceneWidget::wheelEvent(event);
 }
 
+// FIXME: mouse button events are processed in MouseState but key events are
+// processed here - seems inconsistent
 void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event)
 {
     if(event->key() == Qt::Key_Escape)
     {
         mMouse->cancelDrag();
     }
+    else if(event->key() == Qt::Key_Delete)
+    {
+        QPoint p = this->mapFromGlobal(QCursor::pos());
+        std::pair<std::string, Ogre::Vector3> result = mMouse->pgPointUnderCursor(p.x(), p.y());
+        if(result.first != "")
+        {
+            pathgridAboutToBeRemoved(result.first);
+        }
+        else
+            SceneWidget::keyPressEvent(event);
+    }
+    else if(event->key() == Qt::Key_Insert)
+    {
+        QPoint p = this->mapFromGlobal(QCursor::pos());
+        std::pair<std::string, Ogre::Vector3> result = mMouse->terrainUnderCursor(p.x(), p.y());
+        if(result.first != "")
+        {
+            pathgridInserted(result.first, result.second);
+        }
+        else
+            SceneWidget::keyPressEvent(event);
+    }
     else
         SceneWidget::keyPressEvent(event);
 }
+
+// FIXME: temporary until signals from the document are implemented
+void CSVRender::WorldspaceWidget::pathgridAboutToBeRemoved (const std::string &pgName)
+{
+}
+
+// FIXME: temporary until signals from the document are implemented
+void CSVRender::WorldspaceWidget::pathgridMoved (const std::string &pgName, const Ogre::Vector3 &newPos)
+{
+}
+
+// FIXME: temporary until signals from the document are implemented
+void CSVRender::WorldspaceWidget::pathgridInserted (const std::string &name, const Ogre::Vector3 &pos)
+{
+}
diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp
index 5bad3933d8..75dc0c2647 100644
--- a/apps/opencs/view/render/worldspacewidget.hpp
+++ b/apps/opencs/view/render/worldspacewidget.hpp
@@ -109,6 +109,11 @@ namespace CSVRender
             virtual void wheelEvent (QWheelEvent *event);
             virtual void keyPressEvent (QKeyEvent *event);
 
+            // FIXME: temporary only until the signals from the document are implemented
+            virtual void pathgridInserted (const std::string &terrain, const Ogre::Vector3 &pos);
+            virtual void pathgridMoved (const std::string &pgName, const Ogre::Vector3 &pos);
+            virtual void pathgridAboutToBeRemoved (const std::string &pgName);
+
         private:
 
             void dragEnterEvent(QDragEnterEvent *event);
@@ -143,6 +148,11 @@ namespace CSVRender
 
             void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end);
 
+            //virtual void pathgridAdded (const QModelIndex& index, int start, int end) = 0;
+
+            //virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0;
+
+            //virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0;
 
         protected slots:
 

From 895739d6bbc092076ac6ec6d0d9a3645bd2dc1a6 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sun, 9 Nov 2014 17:53:54 +1100
Subject: [PATCH 04/29] Visibility mask working correctly.

---
 apps/opencs/view/render/cell.cpp         |  1 -
 apps/opencs/view/render/mousestate.cpp   |  8 +++++++-
 apps/opencs/view/world/physicssystem.cpp | 23 +++++++++++++++++++----
 3 files changed, 26 insertions(+), 6 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index e703c8a9dc..a53a2939f3 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -315,7 +315,6 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
 
 // FIXME:
 //  - updating indicies
-//  - collision mask
 //  - add pathgrid point above an object
 //  - adding edges
 //  - save to document & signals
diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index bceae716cf..ee77768908 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -285,6 +285,10 @@ namespace CSVRender
                 if(result.first != "")
                 {
                     // FIXME: terrain editing goes here
+                    std::cout << "result default/edit release: " << result.first << std::endl;
+                    std::cout << "  hit pos "+ QString::number(result.second.x).toStdString()
+                            + ", " + QString::number(result.second.y).toStdString()
+                            + ", " + QString::number(result.second.z).toStdString() << std::endl;
                 }
                 break;
             }
@@ -456,6 +460,7 @@ namespace CSVRender
         return std::make_pair("", Ogre::Vector3());
     }
 
+    // NOTE: also returns pathgrids
     std::pair<std::string, Ogre::Vector3> MouseState::objectUnderCursor(const int mouseX, const int mouseY)
     {
         if(!getViewport())
@@ -473,8 +478,9 @@ namespace CSVRender
             {
                 uint32_t visibilityMask = getViewport()->getVisibilityMask();
                 bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference);
+                bool ignorePathgrid = !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
 
-                if(!ignoreObjects && mSceneManager->hasSceneNode(result.first))
+                if((!ignoreObjects || !ignorePathgrid) && mSceneManager->hasSceneNode(result.first))
                 {
                     return result;
                 }
diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp
index 13b29ba5a5..788e42c55e 100644
--- a/apps/opencs/view/world/physicssystem.cpp
+++ b/apps/opencs/view/world/physicssystem.cpp
@@ -243,14 +243,29 @@ namespace CSVWorld
         uint32_t visibilityMask = camera->getViewport()->getVisibilityMask();
         bool ignoreHeightMap = !(visibilityMask & (uint32_t)CSVRender::Element_Terrain);
         bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference);
+        bool ignorePathgrid = !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
 
-        Ogre::Vector3 norm; // not used
-        std::pair<std::string, float> result =
-                                mEngine->rayTest(_from, _to, !ignoreObjects, ignoreHeightMap, &norm);
+        std::pair<std::string, float> result = std::make_pair("", -1);
+        short mask = OEngine::Physic::CollisionType_Raycasting;
+        std::vector<std::pair<float, std::string> > objects = mEngine->rayTest2(_from, _to, mask);
+
+        for (std::vector<std::pair<float, std::string> >::iterator it = objects.begin();
+                it != objects.end(); ++it)
+        {
+            if(ignorePathgrid && QString((*it).second.c_str()).contains(QRegExp("^Pathgrid")))
+                continue;
+            else if(ignoreObjects && QString((*it).second.c_str()).contains(QRegExp("^ref#")))
+                continue;
+            else if(ignoreHeightMap && QString((*it).second.c_str()).contains(QRegExp("^Height")))
+                continue;
+
+            result = std::make_pair((*it).second, (*it).first);
+            break;
+        }
 
         // result.first is the object's referenceId
         if(result.first == "")
-            return std::make_pair("", Ogre::Vector3(0,0,0));
+            return std::make_pair("", Ogre::Vector3());
         else
         {
             // FIXME: maybe below logic belongs in the caller, i.e. terrainUnderCursor or

From f5ba74cd1015bd652301fb48bd7ee1f83044aeae Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sun, 9 Nov 2014 18:10:44 +1100
Subject: [PATCH 05/29] Suppress warnings.

---
 apps/opencs/view/render/cell.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index a53a2939f3..238f60bb5d 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -399,7 +399,7 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
     int index = result.second;
 
     // check if the point exists
-    if(index < 0 || mPathgridId != pathgridId || index >= mPoints.size())
+    if(index < 0 || mPathgridId != pathgridId || (unsigned int)index >= mPoints.size())
         return;
 
     int numToDelete = mPoints[index].mConnectionNum * 2; // for sanity check later
@@ -484,7 +484,7 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name, const Ogre::Ve
     int index = result.second;
 
     // check if the point exists
-    if(index < 0 || mPathgridId != pathgridId || index >= mPoints.size())
+    if(index < 0 || mPathgridId != pathgridId || (unsigned int)index >= mPoints.size())
         return;
 
     float worldsize = ESM::Land::REAL_SIZE;

From 9f16b86bd8fd8f01d23589f4bc201a3ee1fcee49 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sun, 9 Nov 2014 18:21:47 +1100
Subject: [PATCH 06/29] Fix installing pathgrid_pt.nif

---
 files/CMakeLists.txt | 1 +
 1 file changed, 1 insertion(+)

diff --git a/files/CMakeLists.txt b/files/CMakeLists.txt
index 9b2325744e..6ee219a8ad 100644
--- a/files/CMakeLists.txt
+++ b/files/CMakeLists.txt
@@ -17,6 +17,7 @@ set(MATERIAL_FILES
     objects.shader
     objects.shaderset
     openmw.configuration
+    pathgrid_pt.nif
     quad.mat
     quad.shader
     quad.shaderset

From 89eb30054be61f4c7eab911f16e48aef5ca7b03a Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sun, 9 Nov 2014 18:37:04 +1100
Subject: [PATCH 07/29] Fix deleting physic objects.

---
 apps/opencs/view/world/physicssystem.cpp | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp
index 788e42c55e..cae5563e6f 100644
--- a/apps/opencs/view/world/physicssystem.cpp
+++ b/apps/opencs/view/world/physicssystem.cpp
@@ -103,7 +103,8 @@ namespace CSVWorld
             }
 
             // check whether the physics model should be deleted
-            if(mRefIdToSceneNode.find(referenceId) == mRefIdToSceneNode.end())
+            itRef = mRefIdToSceneNode.find(referenceId);
+            if(itRef == mRefIdToSceneNode.end() || (*itRef).second.empty())
             {
                 mEngine->removeRigidBody(referenceId);
                 mEngine->deleteRigidBody(referenceId);

From 1f73fae6efd077a6f744b8e2179017ac6c8595b2 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sun, 9 Nov 2014 20:03:29 +1100
Subject: [PATCH 08/29] Allow adding or dragging pathgrid points over objects
 as well as terrain.

---
 apps/opencs/view/render/cell.cpp              |  3 +-
 apps/opencs/view/render/mousestate.cpp        | 38 +++++++++-------
 apps/opencs/view/render/mousestate.hpp        |  3 +-
 .../view/render/pagedworldspacewidget.cpp     | 45 +++++++++++++------
 apps/opencs/view/render/worldspacewidget.cpp  |  2 +-
 5 files changed, 57 insertions(+), 34 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 238f60bb5d..4e3416111f 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -315,8 +315,7 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
 
 // FIXME:
 //  - updating indicies
-//  - add pathgrid point above an object
-//  - adding edges
+//  - adding edges (need the ability to select a pathgrid and highlight)
 //  - save to document & signals
 //  - repainting edges while moving
 void CSVRender::Cell::loadPathgrid()
diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index ee77768908..ee59770e45 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -234,8 +234,9 @@ namespace CSVRender
                         // move pathgrid point, but don't save yet (need pathgrid
                         // table feature & its data structure to be completed)
                         // FIXME: need to signal PathgridPoint object of change
+                        // FIXME: shouldn't allow pathgrid points under the cursor
                         std::pair<std::string, Ogre::Vector3> result =
-                            terrainUnderCursor(event->x(), event->y()); // FIXME: some pathgrid points are on objects
+                                anyUnderCursor(event->x(), event->y());
                         if(result.first != "")
                         {
                             // FIXME: rather than just updating at the end, should
@@ -243,8 +244,6 @@ namespace CSVRender
                             // while dragging the pathgrid point (maybe check whether
                             // the object is a pathgrid point at the begging and set
                             // a flag?)
-                            // FIXME: this also disallows dragging over objects, so
-                            // may need to use ignoreObjects while raycasting
                             placeObject(mGrabbedSceneNode, result.second);
                             mParent->pathgridMoved(referenceId, result.second);
                         }
@@ -440,13 +439,7 @@ namespace CSVRender
 
     std::pair<std::string, Ogre::Vector3> MouseState::terrainUnderCursor(const int mouseX, const int mouseY)
     {
-        if(!getViewport())
-            return std::make_pair("", Ogre::Vector3());
-
-        float x = (float) mouseX / getViewport()->getActualWidth();
-        float y = (float) mouseY / getViewport()->getActualHeight();
-
-        std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
+        std::pair<std::string, Ogre::Vector3> result = anyUnderCursor(mouseX, mouseY);
         if(result.first != "")
         {
             // FIXME: is there  a better way to distinguish terrain from objects?
@@ -463,13 +456,7 @@ namespace CSVRender
     // NOTE: also returns pathgrids
     std::pair<std::string, Ogre::Vector3> MouseState::objectUnderCursor(const int mouseX, const int mouseY)
     {
-        if(!getViewport())
-            return std::make_pair("", Ogre::Vector3());
-
-        float x = (float) mouseX / getViewport()->getActualWidth();
-        float y = (float) mouseY / getViewport()->getActualHeight();
-
-        std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
+        std::pair<std::string, Ogre::Vector3> result = anyUnderCursor(mouseX, mouseY);
         if(result.first != "")
         {
             // NOTE: anything not terrain is assumed to be an object, e.g pathgrid points
@@ -490,6 +477,23 @@ namespace CSVRender
         return std::make_pair("", Ogre::Vector3());
     }
 
+    std::pair<std::string, Ogre::Vector3> MouseState::anyUnderCursor(const int mouseX, const int mouseY)
+    {
+        if(!getViewport())
+            return std::make_pair("", Ogre::Vector3());
+
+        float x = (float) mouseX / getViewport()->getActualWidth();
+        float y = (float) mouseY / getViewport()->getActualHeight();
+
+        std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
+        if(result.first != "")
+        {
+            return result;
+        }
+
+        return std::make_pair("", Ogre::Vector3());
+    }
+
     void MouseState::updateSceneWidgets()
     {
         std::map<Ogre::SceneManager*, CSVRender::SceneWidget *> sceneWidgets = mPhysics->sceneWidgets();
diff --git a/apps/opencs/view/render/mousestate.hpp b/apps/opencs/view/render/mousestate.hpp
index b56860cace..fd752349a7 100644
--- a/apps/opencs/view/render/mousestate.hpp
+++ b/apps/opencs/view/render/mousestate.hpp
@@ -72,13 +72,14 @@ namespace CSVRender
             void mouseDoubleClickEvent (QMouseEvent *event);
             bool wheelEvent (QWheelEvent *event);
             std::pair<std::string, Ogre::Vector3> pgPointUnderCursor(const int mouseX, const int mouseY);
-            std::pair<std::string, Ogre::Vector3> terrainUnderCursor(const int mouseX, const int mouseY);
+            std::pair<std::string, Ogre::Vector3> anyUnderCursor(const int mouseX, const int mouseY);
 
             void cancelDrag();
 
         private:
 
             std::pair<bool, Ogre::Vector3> mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane);
+            std::pair<std::string, Ogre::Vector3> terrainUnderCursor(const int mouseX, const int mouseY);
             std::pair<std::string, Ogre::Vector3> objectUnderCursor(const int mouseX, const int mouseY);
             std::pair<Ogre::Vector3, Ogre::Vector3> planeAxis();
             void updateSceneWidgets();
diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp
index e528733c01..506fd54df9 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.cpp
@@ -21,6 +21,7 @@
 #include "../../model/world/idtable.hpp"
 
 #include "../widget/scenetooltoggle.hpp"
+#include "../world/physicssystem.hpp"
 
 #include "pathgridpoint.hpp"
 #include "elements.hpp"
@@ -332,26 +333,44 @@ CSVRender::Cell *CSVRender::PagedWorldspaceWidget::findCell(const std::string &c
     return NULL;
 }
 
-// FIXME: pathgrid may be inserted above an object, the parsing needs to change
-void CSVRender::PagedWorldspaceWidget::pathgridInserted (const std::string &terrain, const Ogre::Vector3 &pos)
+// NOTE: allow placing pathgrid points above objects and terrain
+void CSVRender::PagedWorldspaceWidget::pathgridInserted (const std::string &name, const Ogre::Vector3 &pos)
 {
-    // decode name
-    QString id = QString(terrain.c_str());
-    QRegExp terrainRe("^HeightField_([\\d-]+)_([\\d-]+)$");
+    QString id = QString(name.c_str());
+    std::string referenceId = getPhysics()->sceneNodeToRefId(name); // FIXME: move back
 
-    if (id.isEmpty() || !id.startsWith("HeightField_"))
+    bool terrain = id.startsWith("HeightField_");
+    bool object = QString(referenceId.c_str()).startsWith("ref#");
+    // don't allow placing another one on top of a pathgrid point
+    if (id.isEmpty() || (!terrain && !object))
         return;
 
-    if (terrainRe.indexIn(id) == -1)
-        return;
+    std::string cellId;
+    if(terrain)
+    {
+        QRegExp nameRe("^HeightField_([\\d-]+)_([\\d-]+)$");
+        if (nameRe.indexIn(id) == -1)
+            return;
 
-    int cellX = terrainRe.cap(1).toInt();
-    int cellY = terrainRe.cap(2).toInt();
+        int cellX = nameRe.cap(1).toInt();
+        int cellY = nameRe.cap(2).toInt();
 
-    std::ostringstream stream;
-    stream << "#" << cellX << " " << cellY;
+        std::ostringstream stream;
+        stream << "#" << cellX << " " << cellY;
+        cellId = stream.str();
+    }
+    else
+    {
+        const CSMWorld::RefCollection& references = mDocument.getData().getReferences();
+        int index = references.searchId(referenceId);
+        if(index == -1)
+            return;
 
-    Cell *cell = findCell(stream.str());
+        cellId = references.getData(index, references.findColumnIndex(CSMWorld::Columns::ColumnId_Cell))
+            .toString().toUtf8().constData();
+    }
+
+    Cell *cell = findCell(cellId);
     if(cell)
     {
         cell->pathgridPointAdded(pos);
diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp
index 1fedc8aa12..4f85f3a30c 100644
--- a/apps/opencs/view/render/worldspacewidget.cpp
+++ b/apps/opencs/view/render/worldspacewidget.cpp
@@ -422,7 +422,7 @@ void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event)
     else if(event->key() == Qt::Key_Insert)
     {
         QPoint p = this->mapFromGlobal(QCursor::pos());
-        std::pair<std::string, Ogre::Vector3> result = mMouse->terrainUnderCursor(p.x(), p.y());
+        std::pair<std::string, Ogre::Vector3> result = mMouse->anyUnderCursor(p.x(), p.y());
         if(result.first != "")
         {
             pathgridInserted(result.first, result.second);

From d7804974e8e2921cbda8f048ae01f2ca730fe414 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Mon, 10 Nov 2014 20:37:07 +1100
Subject: [PATCH 09/29] Better mouse cursor tracking for the third axis.

---
 apps/opencs/view/render/mousestate.cpp | 127 ++++++++++++++++---------
 apps/opencs/view/render/mousestate.hpp |   7 +-
 2 files changed, 83 insertions(+), 51 deletions(-)

diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index ee59770e45..989dbdb912 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -57,9 +57,9 @@ namespace CSVRender
 
     MouseState::MouseState(WorldspaceWidget *parent)
         : mParent(parent), mPhysics(parent->getPhysics()), mSceneManager(parent->getSceneManager())
-        , mCurrentObj(""), mMouseState(Mouse_Default), mOldPos(0,0), mMouseEventTimer(0), mPlane(0)
+        , mCurrentObj(""), mMouseState(Mouse_Default), mOldCursorPos(0,0), mMouseEventTimer(0)
         , mGrabbedSceneNode(""), mOrigObjPos(Ogre::Vector3()), mOrigMousePos(Ogre::Vector3())
-        , mCurrentMousePos(Ogre::Vector3()), mOffset(0.0f)
+        , mOldMousePos(Ogre::Vector3()), mPlane(0)
         , mColIndexPosX(0), mColIndexPosY(0), mColIndexPosZ(0), mIdTableModel(0)
     {
         const CSMWorld::RefCollection& references = mParent->mDocument.getData().getReferences();
@@ -79,11 +79,11 @@ namespace CSVRender
         Ogre::MeshPtr mesh = Ogre::MeshManager::getSingleton().createPlane("mouse",
             Ogre::ResourceGroupManager::DEFAULT_RESOURCE_GROUP_NAME,
             *mPlane,
-            300000,300000, // FIXME: use far clip dist?
-            1,1, // segments
-            true,  // normals
-            1,     // numTexCoordSets
-            1,1, // uTile, vTile
+            300000,300000,  // FIXME: use far clip dist?
+            1,1,            // segments
+            true,           // normals
+            1,              // numTexCoordSets
+            1,1,            // uTile, vTile
             planeRes.second // upVector
             );
     }
@@ -111,23 +111,24 @@ namespace CSVRender
             }
             case Mouse_Drag:
             {
-                if(event->pos() != mOldPos) // TODO: maybe don't update less than a quantum?
+                if(event->pos() != mOldCursorPos) // TODO: maybe don't update less than a quantum?
                 {
-                    mOldPos = event->pos();
+                    mOldCursorPos = event->pos();
 
                     // ray test against the plane to provide feedback to the user the
-                    // relative movement of the object on the x-y plane
-                    std::pair<bool, Ogre::Vector3> planeResult = mousePositionOnPlane(event->pos(), *mPlane);
+                    // relative movement of the object on the movement plane
+                    std::pair<bool, Ogre::Vector3> planeResult = mousePosOnPlane(event->pos(), *mPlane);
                     if(planeResult.first)
                     {
                         if(mGrabbedSceneNode != "")
                         {
-                            std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
-                            Ogre::Vector3 pos = mOrigObjPos + planeRes.first*mOffset;
-                            mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos+planeResult.second-mOrigMousePos);
-                            mCurrentMousePos = planeResult.second;
-                            mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+planeResult.second-mOrigMousePos);
+                            Ogre::Vector3 pos = mOrigObjPos + (planeResult.second-mOrigMousePos);
+
+                            mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos);
+                            mPhysics->moveSceneNodes(mGrabbedSceneNode, pos);
                             updateSceneWidgets();
+
+                            mOldMousePos = planeResult.second;
                         }
                     }
                 }
@@ -165,12 +166,11 @@ namespace CSVRender
                     // mouse in relation to the object position
                     std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
                     mPlane->redefine(planeRes.first, result.second);
-                    std::pair<bool, Ogre::Vector3> planeResult = mousePositionOnPlane(event->pos(), *mPlane);
+                    std::pair<bool, Ogre::Vector3> planeResult = mousePosOnPlane(event->pos(), *mPlane);
                     if(planeResult.first)
                     {
                         mOrigMousePos = planeResult.second;
-                        mCurrentMousePos = planeResult.second;
-                        mOffset = 0.0f;
+                        mOldMousePos = planeResult.second;
                     }
 
                     mOrigObjPos = mSceneManager->getSceneNode(mGrabbedSceneNode)->getPosition();
@@ -206,6 +206,7 @@ namespace CSVRender
                         mCurrentObj = result.first;
 
                     }
+//#if 0
                     // print some debug info
                     std::string referenceId = mPhysics->sceneNodeToRefId(result.first);
                     if(referenceId != "")
@@ -215,17 +216,18 @@ namespace CSVRender
                     std::cout << "  hit pos "+ QString::number(result.second.x).toStdString()
                             + ", " + QString::number(result.second.y).toStdString()
                             + ", " + QString::number(result.second.z).toStdString() << std::endl;
+//#endif
                 }
                 break;
             }
             case Mouse_Drag:
             {
                 // final placement
-                std::pair<bool, Ogre::Vector3> planeResult = mousePositionOnPlane(event->pos(), *mPlane);
+                std::pair<bool, Ogre::Vector3> planeResult = mousePosOnPlane(event->pos(), *mPlane);
                 if(planeResult.first && mGrabbedSceneNode != "")
                 {
-                    std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
-                    Ogre::Vector3 pos = mOrigObjPos+planeRes.first*mOffset+planeResult.second-mOrigMousePos;
+                    Ogre::Vector3 pos = mOrigObjPos + (planeResult.second-mOrigMousePos);
+
                     // use the saved scene node name since the physics model has not moved yet
                     std::string referenceId = mPhysics->sceneNodeToRefId(mGrabbedSceneNode);
 
@@ -234,18 +236,19 @@ namespace CSVRender
                         // move pathgrid point, but don't save yet (need pathgrid
                         // table feature & its data structure to be completed)
                         // FIXME: need to signal PathgridPoint object of change
-                        // FIXME: shouldn't allow pathgrid points under the cursor
-                        std::pair<std::string, Ogre::Vector3> result =
-                                anyUnderCursor(event->x(), event->y());
-                        if(result.first != "")
+                        //std::pair<std::string, Ogre::Vector3> result =
+                                //anyUnderCursor(event->x(), event->y());
+                        //std::string refId = mPhysics->sceneNodeToRefId(result.first);
+                        //if(result.first != "" && // don't allow pathgrid points under the cursor
+                            //!QString(refId.c_str()).contains(QRegExp("^Pathgrid")))
                         {
                             // FIXME: rather than just updating at the end, should
                             // consider providing visual feedback of terrain height
                             // while dragging the pathgrid point (maybe check whether
                             // the object is a pathgrid point at the begging and set
                             // a flag?)
-                            placeObject(mGrabbedSceneNode, result.second);
-                            mParent->pathgridMoved(referenceId, result.second);
+                            placeObject(mGrabbedSceneNode, pos); // result.second
+                            mParent->pathgridMoved(referenceId, pos); // result.second
                         }
                     }
                     else
@@ -267,12 +270,11 @@ namespace CSVRender
                     mMouseState = Mouse_Edit;
 
                     // reset states
-                    mCurrentMousePos = Ogre::Vector3(); // mouse pos to use in wheel event
-                    mOrigMousePos = Ogre::Vector3();    // starting pos of mouse in world space
-                    mOrigObjPos = Ogre::Vector3();      // starting pos of object in world space
-                    mGrabbedSceneNode = "";             // id of the object
-                    mOffset = 0.0f;                     // used for z-axis movement
-                    mOldPos = QPoint(0, 0);             // to calculate relative movement of mouse
+                    mGrabbedSceneNode = "";          // id of the object
+                    mOrigMousePos = Ogre::Vector3(); // starting pos of mouse in world space
+                    mOrigObjPos =   Ogre::Vector3(); // starting pos of object in world space
+                    mOldMousePos =  Ogre::Vector3(); // mouse pos to use in wheel event
+                    mOldCursorPos = QPoint(0, 0);    // to calculate relative movement of mouse
             }
                 break;
             }
@@ -284,10 +286,12 @@ namespace CSVRender
                 if(result.first != "")
                 {
                     // FIXME: terrain editing goes here
+//#if 0
                     std::cout << "result default/edit release: " << result.first << std::endl;
                     std::cout << "  hit pos "+ QString::number(result.second.x).toStdString()
                             + ", " + QString::number(result.second.y).toStdString()
                             + ", " + QString::number(result.second.z).toStdString() << std::endl;
+//#endif
                 }
                 break;
             }
@@ -298,9 +302,9 @@ namespace CSVRender
 
     void MouseState::mouseDoubleClickEvent (QMouseEvent *event)
     {
-        //event->ignore();
-        mPhysics->toggleDebugRendering(mSceneManager);
-        mParent->flagAsModified();
+        event->ignore();
+        //mPhysics->toggleDebugRendering(mSceneManager);
+        //mParent->flagAsModified();
     }
 
     bool MouseState::wheelEvent (QWheelEvent *event)
@@ -313,17 +317,47 @@ namespace CSVRender
                 /* FALL_THROUGH */
             case Mouse_Drag:
             {
-                // move the object along the z axis during Mouse_Drag or Mouse_Grab
+                // move the object along the axis normal to the plane during Mouse_Drag or Mouse_Grab
                 if (event->delta())
                 {
-                    // seems positive is up and negative is down
-                    mOffset += (event->delta()/1); // FIXME: arbitrary number, make config option?
+                    // The mouse point is where the mouse points the object when dragging starts.
+                    // The object position is usually a little away from the mount point.
 
+                    // Get the new world position of mouse on the plane offset from the wheel
                     std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
-                    Ogre::Vector3 pos = mOrigObjPos + planeRes.first*mOffset;
-                    mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos+mCurrentMousePos-mOrigMousePos);
-                    mPhysics->moveSceneNodes(mGrabbedSceneNode, pos+mCurrentMousePos-mOrigMousePos);
+                    Ogre::Vector3 mousePos = mOldMousePos + planeRes.first*(event->delta()/2);
+
+                    // Move the movement plane to the new mouse position. The plane is created on
+                    // the mouse point (i.e. not the object position)
+                    mPlane->redefine(planeRes.first, mousePos);
+
+                    // Calculate the new screen position of the cursor
+                    Ogre::Vector3 screenPos =
+                        getCamera()->getProjectionMatrix() * getCamera()->getViewMatrix() * mousePos;
+                    int screenX = (screenPos.x/2+0.5) * getViewport()->getActualWidth();
+                    int screenY = (1-(screenPos.y/2+0.5)) * getViewport()->getActualHeight();
+
+                    // Move the cursor to the new screen position
+                    QCursor::setPos(mParent->mapToGlobal(QPoint(screenX, screenY)));
+
+                    // Use the new position to check the world position of the mouse
+                    std::pair<bool, Ogre::Vector3> planeResult =
+                        mousePosOnPlane(QPoint(screenX, screenY), *mPlane);
+                    {
+                        if(planeResult.first)
+                            mousePos = planeResult.second;
+                    }
+
+                    // Find the final world position of the cursor
+                    Ogre::Vector3 finalPos = mOrigObjPos + (mousePos-mOrigMousePos);
+
+                    mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(finalPos);
+                    mPhysics->moveSceneNodes(mGrabbedSceneNode, finalPos);
                     updateSceneWidgets();
+
+                    // remember positions for next time
+                    mOldMousePos = mousePos;
+                    mOldCursorPos = QPoint(screenX, screenY);
                 }
                 break;
             }
@@ -353,14 +387,13 @@ namespace CSVRender
 
                 // reset states
                 mMouseState = Mouse_Default;
-                mCurrentMousePos = Ogre::Vector3();
+                mOldMousePos = Ogre::Vector3();
                 mOrigMousePos = Ogre::Vector3();
                 mOrigObjPos = Ogre::Vector3();
                 mGrabbedSceneNode = "";
                 mCurrentObj = "";
-                mOldPos = QPoint(0, 0);
+                mOldCursorPos = QPoint(0, 0);
                 mMouseEventTimer->invalidate();
-                mOffset = 0.0f;
 
                 break;
             }
@@ -420,7 +453,7 @@ namespace CSVRender
         return std::make_pair("", Ogre::Vector3());
     }
 
-    std::pair<bool, Ogre::Vector3> MouseState::mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane)
+    std::pair<bool, Ogre::Vector3> MouseState::mousePosOnPlane(const QPoint &pos, const Ogre::Plane &plane)
     {
         // using a really small value seems to mess up with the projections
         float nearClipDistance = getCamera()->getNearClipDistance(); // save existing
diff --git a/apps/opencs/view/render/mousestate.hpp b/apps/opencs/view/render/mousestate.hpp
index fd752349a7..d21735a78d 100644
--- a/apps/opencs/view/render/mousestate.hpp
+++ b/apps/opencs/view/render/mousestate.hpp
@@ -46,15 +46,14 @@ namespace CSVRender
             CSVWorld::PhysicsSystem *mPhysics; // local copy
             Ogre::SceneManager *mSceneManager; // local copy
 
-            QPoint mOldPos;
+            QPoint mOldCursorPos;
             std::string mCurrentObj;
             std::string mGrabbedSceneNode;
             QElapsedTimer *mMouseEventTimer;
             Ogre::Plane *mPlane;
             Ogre::Vector3 mOrigObjPos;
             Ogre::Vector3 mOrigMousePos;
-            Ogre::Vector3 mCurrentMousePos;
-            float mOffset;
+            Ogre::Vector3 mOldMousePos;
 
             CSMWorld::IdTable *mIdTableModel;
             int mColIndexPosX;
@@ -78,7 +77,7 @@ namespace CSVRender
 
         private:
 
-            std::pair<bool, Ogre::Vector3> mousePositionOnPlane(const QPoint &pos, const Ogre::Plane &plane);
+            std::pair<bool, Ogre::Vector3> mousePosOnPlane(const QPoint &pos, const Ogre::Plane &plane);
             std::pair<std::string, Ogre::Vector3> terrainUnderCursor(const int mouseX, const int mouseY);
             std::pair<std::string, Ogre::Vector3> objectUnderCursor(const int mouseX, const int mouseY);
             std::pair<Ogre::Vector3, Ogre::Vector3> planeAxis();

From 2412d127b0b2694f136e0f468034ffaec117560f Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Mon, 10 Nov 2014 22:29:01 +1100
Subject: [PATCH 10/29] Fix saving pathgrid positions when adding or moving.

---
 apps/opencs/view/render/cell.cpp | 36 +++++++++++++++++++++++++-------
 apps/opencs/view/render/cell.hpp |  5 +++--
 2 files changed, 32 insertions(+), 9 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 4e3416111f..0435159075 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -18,7 +18,6 @@
 #include "terrainstorage.hpp"
 #include "pathgridpoint.hpp"
 
-// FIXME: redraw edges when pathgrid points are moved, added and deleted
 namespace CSVRender
 {
     // PLEASE NOTE: pathgrid edge code copied and adapted from mwrender/debugging
@@ -314,7 +313,7 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
 }
 
 // FIXME:
-//  - updating indicies
+//  - updating indicies, including mData
 //  - adding edges (need the ability to select a pathgrid and highlight)
 //  - save to document & signals
 //  - repainting edges while moving
@@ -324,7 +323,7 @@ void CSVRender::Cell::loadPathgrid()
         Ogre::ResourceGroupManager::getSingleton().createResourceGroup(DEBUGGING_GROUP);
     createGridMaterials();
 
-    float worldsize = ESM::Land::REAL_SIZE;
+    int worldsize = ESM::Land::REAL_SIZE;
 
     CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgridCollection = mData.getPathgrids();
     int index = pathgridCollection.searchId(mId);
@@ -373,14 +372,24 @@ void CSVRender::Cell::loadPathgrid()
 
 // NOTE: pos is in world coordinates
 // FIXME: save to the document
-void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos)
+void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos, bool interior)
 {
     std::string name = PathgridPoint::getName(mId, mPoints.size());
 
     mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics)));
 
     // store to document
-    ESM::Pathgrid::Point point((int)pos.x, (int)pos.y, (int)pos.z);
+    int worldsize = ESM::Land::REAL_SIZE;
+
+    int x = pos.x;
+    int y = pos.y;
+    if(!interior)
+    {
+        x = x - (worldsize * mX);
+        y = y - (worldsize * mY);
+    }
+
+    ESM::Pathgrid::Point point(x, y, (int)pos.z);
     point.mConnectionNum = 0;
     mPoints.push_back(point);
     mPathgridId = mId;
@@ -473,7 +482,8 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
 }
 
 // NOTE: newPos is in world coordinates
-void CSVRender::Cell::pathgridPointMoved(const std::string &name, const Ogre::Vector3 &newPos)
+void CSVRender::Cell::pathgridPointMoved(const std::string &name,
+        const Ogre::Vector3 &newPos, bool interior)
 {
     std::pair<std::string, int> result = PathgridPoint::getIdAndIndex(name);
     if(result.first == "")
@@ -486,7 +496,19 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name, const Ogre::Ve
     if(index < 0 || mPathgridId != pathgridId || (unsigned int)index >= mPoints.size())
         return;
 
-    float worldsize = ESM::Land::REAL_SIZE;
+    int worldsize = ESM::Land::REAL_SIZE;
+
+    int x = newPos.x;
+    int y = newPos.y;
+    if(!interior)
+    {
+        x = x - (worldsize * mX);
+        y = y - (worldsize * mY);
+    }
+
+    mPoints[index].mX = x;
+    mPoints[index].mY = y;
+    mPoints[index].mZ = newPos.z;
 
     // delete then recreate the edges
     for(unsigned i = 0; i < mEdges.size(); ++i)
diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp
index 5d7b9088a5..8a7f01f202 100644
--- a/apps/opencs/view/render/cell.hpp
+++ b/apps/opencs/view/render/cell.hpp
@@ -95,8 +95,9 @@ namespace CSVRender
 
             float getTerrainHeightAt(const Ogre::Vector3 &pos) const;
 
-            void pathgridPointAdded(const Ogre::Vector3 &pos);
-            void pathgridPointMoved(const std::string &name, const Ogre::Vector3 &newPos);
+            void pathgridPointAdded(const Ogre::Vector3 &pos, bool interior = false);
+            void pathgridPointMoved(const std::string &name,
+                    const Ogre::Vector3 &newPos, bool interior = false);
             void pathgridPointRemoved(const std::string &name);
 
         private:

From 48ecea71036d92752f7a228a21e1c4b93f2d7394 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Mon, 10 Nov 2014 22:38:29 +1100
Subject: [PATCH 11/29] Simplistic drop-to-ground functionality for pathgrid
 points.

---
 apps/opencs/view/render/mousestate.cpp   | 17 +++++++----
 apps/opencs/view/world/physicssystem.cpp | 37 ++++++++++++++++++++++++
 apps/opencs/view/world/physicssystem.hpp |  2 ++
 3 files changed, 51 insertions(+), 5 deletions(-)

diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index 989dbdb912..01f16c3397 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -236,12 +236,17 @@ namespace CSVRender
                         // move pathgrid point, but don't save yet (need pathgrid
                         // table feature & its data structure to be completed)
                         // FIXME: need to signal PathgridPoint object of change
-                        //std::pair<std::string, Ogre::Vector3> result =
-                                //anyUnderCursor(event->x(), event->y());
-                        //std::string refId = mPhysics->sceneNodeToRefId(result.first);
-                        //if(result.first != "" && // don't allow pathgrid points under the cursor
-                            //!QString(refId.c_str()).contains(QRegExp("^Pathgrid")))
+                        std::pair<std::string, Ogre::Vector3> result =
+                                anyUnderCursor(event->x(), event->y());
+                        std::string refId = mPhysics->sceneNodeToRefId(result.first);
+                        if(result.first != "" && // don't allow pathgrid points under the cursor
+                            !QString(refId.c_str()).contains(QRegExp("^Pathgrid")))
                         {
+                            // drop (does not work if placed below the object/terrain)
+                            // maybe look for closest object/terrain in both directions?
+                            std::pair<std::string, float> res = mPhysics->distToGround(pos, getCamera());
+                            if(res.first != "")
+                                pos.z -= res.second;
                             // FIXME: rather than just updating at the end, should
                             // consider providing visual feedback of terrain height
                             // while dragging the pathgrid point (maybe check whether
@@ -250,6 +255,8 @@ namespace CSVRender
                             placeObject(mGrabbedSceneNode, pos); // result.second
                             mParent->pathgridMoved(referenceId, pos); // result.second
                         }
+                        else
+                            cancelDrag();
                     }
                     else
                     {
diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp
index cae5563e6f..55da2e2210 100644
--- a/apps/opencs/view/world/physicssystem.cpp
+++ b/apps/opencs/view/world/physicssystem.cpp
@@ -281,6 +281,43 @@ namespace CSVWorld
         }
     }
 
+    std::pair<std::string, float> PhysicsSystem::distToGround(Ogre::Vector3 &position,
+            Ogre::Camera *camera)
+    {
+        btVector3 _from, _to;
+        _from = btVector3(position.x, position.y, position.z);
+        _to = btVector3(position.x, position.y, position.z-300000);
+
+        uint32_t visibilityMask = camera->getViewport()->getVisibilityMask();
+        bool ignoreHeightMap = !(visibilityMask & (uint32_t)CSVRender::Element_Terrain);
+        bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference);
+        bool ignorePathgrid = !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
+
+        std::pair<std::string, float> result = std::make_pair("", -1);
+        short mask = OEngine::Physic::CollisionType_Raycasting;
+        std::vector<std::pair<float, std::string> > objects = mEngine->rayTest2(_from, _to, mask);
+
+        for (std::vector<std::pair<float, std::string> >::iterator it = objects.begin();
+                it != objects.end(); ++it)
+        {
+            if(ignorePathgrid && QString((*it).second.c_str()).contains(QRegExp("^Pathgrid")))
+                continue;
+            else if(ignoreObjects && QString((*it).second.c_str()).contains(QRegExp("^ref#")))
+                continue;
+            else if(ignoreHeightMap && QString((*it).second.c_str()).contains(QRegExp("^Height")))
+                continue;
+
+            result = std::make_pair((*it).second, (*it).first);
+            break;
+        }
+
+        // result.first is the object's referenceId
+        if(result.first == "")
+            return std::make_pair("", -1);
+        else
+            return std::make_pair(result.first, 300000*result.second);
+    }
+
     std::string PhysicsSystem::refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr)
     {
         return mRefIdToSceneNode[referenceId][sceneMgr];
diff --git a/apps/opencs/view/world/physicssystem.hpp b/apps/opencs/view/world/physicssystem.hpp
index 0036bf769d..f28272532d 100644
--- a/apps/opencs/view/world/physicssystem.hpp
+++ b/apps/opencs/view/world/physicssystem.hpp
@@ -73,6 +73,8 @@ namespace CSVWorld
             std::pair<std::string, Ogre::Vector3> castRay(float mouseX,
                     float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera);
 
+            std::pair<std::string, float> distToGround(Ogre::Vector3 &position, Ogre::Camera *camera);
+
             std::string sceneNodeToRefId(std::string sceneNodeName);
 
             // for multi-scene manager per physics engine

From 1504119da776121b79927c6188872bad06036cc6 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Tue, 11 Nov 2014 06:07:52 +1100
Subject: [PATCH 12/29] Increase the sensitivity of the wheel movement.

---
 apps/opencs/view/render/mousestate.cpp | 9 +++++----
 1 file changed, 5 insertions(+), 4 deletions(-)

diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index 01f16c3397..ba668901f2 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -233,9 +233,9 @@ namespace CSVRender
 
                     if(QString(referenceId.c_str()).contains(QRegExp("^Pathgrid")))
                     {
-                        // move pathgrid point, but don't save yet (need pathgrid
+                        // FIXME: move pathgrid point, but don't save yet (need pathgrid
                         // table feature & its data structure to be completed)
-                        // FIXME: need to signal PathgridPoint object of change
+                        // Also need to signal PathgridPoint object of change
                         std::pair<std::string, Ogre::Vector3> result =
                                 anyUnderCursor(event->x(), event->y());
                         std::string refId = mPhysics->sceneNodeToRefId(result.first);
@@ -256,7 +256,7 @@ namespace CSVRender
                             mParent->pathgridMoved(referenceId, pos); // result.second
                         }
                         else
-                            cancelDrag();
+                            cancelDrag(); // FIXME: does not allow editing if terrain not visible
                     }
                     else
                     {
@@ -331,8 +331,9 @@ namespace CSVRender
                     // The object position is usually a little away from the mount point.
 
                     // Get the new world position of mouse on the plane offset from the wheel
+                    // FIXME: make the sensitivity a user setting and/or allow modifiers
                     std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
-                    Ogre::Vector3 mousePos = mOldMousePos + planeRes.first*(event->delta()/2);
+                    Ogre::Vector3 mousePos = mOldMousePos + planeRes.first*(event->delta()/1.5);
 
                     // Move the movement plane to the new mouse position. The plane is created on
                     // the mouse point (i.e. not the object position)

From 407edc770c0005a9f179717fa9532d063b8ecbd8 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Tue, 11 Nov 2014 07:47:35 +1100
Subject: [PATCH 13/29] Added a simplistic snap to closest object or terrain.

---
 apps/opencs/view/render/mousestate.cpp   | 25 +++++----
 apps/opencs/view/world/physicssystem.cpp | 70 +++++++++++++++++++++++-
 apps/opencs/view/world/physicssystem.hpp |  6 +-
 3 files changed, 89 insertions(+), 12 deletions(-)

diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index ba668901f2..8f254f08d4 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -242,18 +242,23 @@ namespace CSVRender
                         if(result.first != "" && // don't allow pathgrid points under the cursor
                             !QString(refId.c_str()).contains(QRegExp("^Pathgrid")))
                         {
-                            // drop (does not work if placed below the object/terrain)
-                            // maybe look for closest object/terrain in both directions?
-                            std::pair<std::string, float> res = mPhysics->distToGround(pos, getCamera());
+                            // snap (defaults to 300 or less)
+                            // FIXME: sticks to the underside of the object if snapping up
+                            std::pair<std::string, float> res =
+                                mPhysics->distToClosest(pos, getCamera(), 500);
                             if(res.first != "")
+                            {
                                 pos.z -= res.second;
-                            // FIXME: rather than just updating at the end, should
-                            // consider providing visual feedback of terrain height
-                            // while dragging the pathgrid point (maybe check whether
-                            // the object is a pathgrid point at the begging and set
-                            // a flag?)
-                            placeObject(mGrabbedSceneNode, pos); // result.second
-                            mParent->pathgridMoved(referenceId, pos); // result.second
+                                // FIXME: rather than just updating at the end, should
+                                // consider providing visual feedback of terrain height
+                                // while dragging the pathgrid point (maybe check whether
+                                // the object is a pathgrid point at the begging and set
+                                // a flag?)
+                                placeObject(mGrabbedSceneNode, pos); // result.second
+                                mParent->pathgridMoved(referenceId, pos); // result.second
+                            }
+                            else
+                                cancelDrag();
                         }
                         else
                             cancelDrag(); // FIXME: does not allow editing if terrain not visible
diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp
index 55da2e2210..358bb90ca0 100644
--- a/apps/opencs/view/world/physicssystem.cpp
+++ b/apps/opencs/view/world/physicssystem.cpp
@@ -281,7 +281,7 @@ namespace CSVWorld
         }
     }
 
-    std::pair<std::string, float> PhysicsSystem::distToGround(Ogre::Vector3 &position,
+    std::pair<std::string, float> PhysicsSystem::distToGround(const Ogre::Vector3 &position,
             Ogre::Camera *camera)
     {
         btVector3 _from, _to;
@@ -318,6 +318,74 @@ namespace CSVWorld
             return std::make_pair(result.first, 300000*result.second);
     }
 
+    // FIXME: remove code duplication
+    std::pair<std::string, float> PhysicsSystem::distToClosest(const Ogre::Vector3 &position,
+            Ogre::Camera *camera, const float limit)
+    {
+        uint32_t visibilityMask = camera->getViewport()->getVisibilityMask();
+        bool ignoreHeightMap = !(visibilityMask & (uint32_t)CSVRender::Element_Terrain);
+        bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference);
+        bool ignorePathgrid = !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
+
+        short mask = OEngine::Physic::CollisionType_Raycasting;
+
+        btVector3 _from, _to;
+        _from = btVector3(position.x, position.y, position.z);
+        _to = btVector3(position.x, position.y, position.z-limit);
+
+        std::pair<std::string, float> resDown = std::make_pair("", -1);
+        std::vector<std::pair<float, std::string> > objectsDown = mEngine->rayTest2(_from, _to, mask);
+
+        for (std::vector<std::pair<float, std::string> >::iterator it = objectsDown.begin();
+                it != objectsDown.end(); ++it)
+        {
+            if(ignorePathgrid && QString((*it).second.c_str()).contains(QRegExp("^Pathgrid")))
+                continue;
+            else if(ignoreObjects && QString((*it).second.c_str()).contains(QRegExp("^ref#")))
+                continue;
+            else if(ignoreHeightMap && QString((*it).second.c_str()).contains(QRegExp("^Height")))
+                continue;
+
+            resDown = std::make_pair((*it).second, (*it).first);
+            break;
+        }
+
+        _to = btVector3(position.x, position.y, position.z+limit);
+        std::pair<std::string, float> resUp = std::make_pair("", -1);
+        std::vector<std::pair<float, std::string> > objectsUp = mEngine->rayTest2(_from, _to, mask);
+
+        for (std::vector<std::pair<float, std::string> >::iterator it = objectsDown.begin();
+                it != objectsDown.end(); ++it)
+        {
+            if(ignorePathgrid && QString((*it).second.c_str()).contains(QRegExp("^Pathgrid")))
+                continue;
+            else if(ignoreObjects && QString((*it).second.c_str()).contains(QRegExp("^ref#")))
+                continue;
+            else if(ignoreHeightMap && QString((*it).second.c_str()).contains(QRegExp("^Height")))
+                continue;
+
+            resUp = std::make_pair((*it).second, (*it).first);
+            break;
+        }
+
+        if(resDown.first != "")
+        {
+            if(resUp.first != "")
+            {
+                if(fabs(resUp.second) < fabs(resDown.second))
+                    return std::make_pair(resUp.first, -limit*resUp.second);
+                else
+                    return std::make_pair(resDown.first, limit*resDown.second);
+            }
+            else
+                return std::make_pair(resDown.first, limit*resDown.second);
+        }
+        else if(resUp.first != "")
+            return std::make_pair(resUp.first, -limit*resUp.second);
+        else
+            return std::make_pair("", -1);
+    }
+
     std::string PhysicsSystem::refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr)
     {
         return mRefIdToSceneNode[referenceId][sceneMgr];
diff --git a/apps/opencs/view/world/physicssystem.hpp b/apps/opencs/view/world/physicssystem.hpp
index f28272532d..6436b87431 100644
--- a/apps/opencs/view/world/physicssystem.hpp
+++ b/apps/opencs/view/world/physicssystem.hpp
@@ -73,7 +73,11 @@ namespace CSVWorld
             std::pair<std::string, Ogre::Vector3> castRay(float mouseX,
                     float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera);
 
-            std::pair<std::string, float> distToGround(Ogre::Vector3 &position, Ogre::Camera *camera);
+            std::pair<std::string, float> distToGround(const Ogre::Vector3 &position,
+                    Ogre::Camera *camera);
+
+            std::pair<std::string, float> distToClosest(const Ogre::Vector3 &position,
+                    Ogre::Camera *camera, const float limit = 300.0f);
 
             std::string sceneNodeToRefId(std::string sceneNodeName);
 

From 3eb556ff8ac7bf756ae5c2e2b5a6c4e5d2b6622a Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Tue, 11 Nov 2014 19:40:04 +1100
Subject: [PATCH 14/29] Enhanced snap functionality for pathgrid points.

---
 apps/opencs/view/render/mousestate.cpp   | 31 ++++------
 apps/opencs/view/world/physicssystem.cpp | 73 ++++--------------------
 apps/opencs/view/world/physicssystem.hpp |  4 +-
 3 files changed, 25 insertions(+), 83 deletions(-)

diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index 8f254f08d4..c9fcdbc867 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -236,29 +236,22 @@ namespace CSVRender
                         // FIXME: move pathgrid point, but don't save yet (need pathgrid
                         // table feature & its data structure to be completed)
                         // Also need to signal PathgridPoint object of change
-                        std::pair<std::string, Ogre::Vector3> result =
-                                anyUnderCursor(event->x(), event->y());
+                        std::pair<std::string, float> result =
+                            mPhysics->distToClosest(pos, getCamera(), 600); // snap
                         std::string refId = mPhysics->sceneNodeToRefId(result.first);
+
                         if(result.first != "" && // don't allow pathgrid points under the cursor
                             !QString(refId.c_str()).contains(QRegExp("^Pathgrid")))
                         {
-                            // snap (defaults to 300 or less)
-                            // FIXME: sticks to the underside of the object if snapping up
-                            std::pair<std::string, float> res =
-                                mPhysics->distToClosest(pos, getCamera(), 500);
-                            if(res.first != "")
-                            {
-                                pos.z -= res.second;
-                                // FIXME: rather than just updating at the end, should
-                                // consider providing visual feedback of terrain height
-                                // while dragging the pathgrid point (maybe check whether
-                                // the object is a pathgrid point at the begging and set
-                                // a flag?)
-                                placeObject(mGrabbedSceneNode, pos); // result.second
-                                mParent->pathgridMoved(referenceId, pos); // result.second
-                            }
-                            else
-                                cancelDrag();
+                            pos.z -= result.second;
+                            pos.z += 2; // arbitrary number, lift up slightly (maybe change the nif?)
+                            // FIXME: rather than just updating at the end, should
+                            // consider providing visual feedback of terrain height
+                            // while dragging the pathgrid point (maybe check whether
+                            // the object is a pathgrid point at the begging and set
+                            // a flag?)
+                            placeObject(mGrabbedSceneNode, pos); // result.second
+                            mParent->pathgridMoved(referenceId, pos); // result.second
                         }
                         else
                             cancelDrag(); // FIXME: does not allow editing if terrain not visible
diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp
index 358bb90ca0..9ff945829a 100644
--- a/apps/opencs/view/world/physicssystem.cpp
+++ b/apps/opencs/view/world/physicssystem.cpp
@@ -282,16 +282,17 @@ namespace CSVWorld
     }
 
     std::pair<std::string, float> PhysicsSystem::distToGround(const Ogre::Vector3 &position,
-            Ogre::Camera *camera)
+            Ogre::Camera *camera, const float limit, bool ignorePgPoint)
     {
         btVector3 _from, _to;
         _from = btVector3(position.x, position.y, position.z);
-        _to = btVector3(position.x, position.y, position.z-300000);
+        _to = btVector3(position.x, position.y, position.z-limit);
 
         uint32_t visibilityMask = camera->getViewport()->getVisibilityMask();
         bool ignoreHeightMap = !(visibilityMask & (uint32_t)CSVRender::Element_Terrain);
         bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference);
-        bool ignorePathgrid = !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
+        bool ignorePathgrid = ignorePgPoint ||
+                              !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
 
         std::pair<std::string, float> result = std::make_pair("", -1);
         short mask = OEngine::Physic::CollisionType_Raycasting;
@@ -315,73 +316,21 @@ namespace CSVWorld
         if(result.first == "")
             return std::make_pair("", -1);
         else
-            return std::make_pair(result.first, 300000*result.second);
+            return std::make_pair(result.first, limit*result.second);
     }
 
-    // FIXME: remove code duplication
+    // tries to find the distance to the "top" of the closest object
     std::pair<std::string, float> PhysicsSystem::distToClosest(const Ogre::Vector3 &position,
             Ogre::Camera *camera, const float limit)
     {
-        uint32_t visibilityMask = camera->getViewport()->getVisibilityMask();
-        bool ignoreHeightMap = !(visibilityMask & (uint32_t)CSVRender::Element_Terrain);
-        bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference);
-        bool ignorePathgrid = !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
+        const float thickness = 50; // arbitrary number
 
-        short mask = OEngine::Physic::CollisionType_Raycasting;
-
-        btVector3 _from, _to;
-        _from = btVector3(position.x, position.y, position.z);
-        _to = btVector3(position.x, position.y, position.z-limit);
-
-        std::pair<std::string, float> resDown = std::make_pair("", -1);
-        std::vector<std::pair<float, std::string> > objectsDown = mEngine->rayTest2(_from, _to, mask);
-
-        for (std::vector<std::pair<float, std::string> >::iterator it = objectsDown.begin();
-                it != objectsDown.end(); ++it)
-        {
-            if(ignorePathgrid && QString((*it).second.c_str()).contains(QRegExp("^Pathgrid")))
-                continue;
-            else if(ignoreObjects && QString((*it).second.c_str()).contains(QRegExp("^ref#")))
-                continue;
-            else if(ignoreHeightMap && QString((*it).second.c_str()).contains(QRegExp("^Height")))
-                continue;
-
-            resDown = std::make_pair((*it).second, (*it).first);
-            break;
-        }
-
-        _to = btVector3(position.x, position.y, position.z+limit);
-        std::pair<std::string, float> resUp = std::make_pair("", -1);
-        std::vector<std::pair<float, std::string> > objectsUp = mEngine->rayTest2(_from, _to, mask);
-
-        for (std::vector<std::pair<float, std::string> >::iterator it = objectsDown.begin();
-                it != objectsDown.end(); ++it)
-        {
-            if(ignorePathgrid && QString((*it).second.c_str()).contains(QRegExp("^Pathgrid")))
-                continue;
-            else if(ignoreObjects && QString((*it).second.c_str()).contains(QRegExp("^ref#")))
-                continue;
-            else if(ignoreHeightMap && QString((*it).second.c_str()).contains(QRegExp("^Height")))
-                continue;
-
-            resUp = std::make_pair((*it).second, (*it).first);
-            break;
-        }
+        std::pair<std::string, float> resDown =
+            distToGround(Ogre::Vector3(position.x, position.y, position.z+thickness),
+                         camera, limit+thickness, true);
 
         if(resDown.first != "")
-        {
-            if(resUp.first != "")
-            {
-                if(fabs(resUp.second) < fabs(resDown.second))
-                    return std::make_pair(resUp.first, -limit*resUp.second);
-                else
-                    return std::make_pair(resDown.first, limit*resDown.second);
-            }
-            else
-                return std::make_pair(resDown.first, limit*resDown.second);
-        }
-        else if(resUp.first != "")
-            return std::make_pair(resUp.first, -limit*resUp.second);
+            return std::make_pair(resDown.first, resDown.second-thickness);
         else
             return std::make_pair("", -1);
     }
diff --git a/apps/opencs/view/world/physicssystem.hpp b/apps/opencs/view/world/physicssystem.hpp
index 6436b87431..589dca9c5b 100644
--- a/apps/opencs/view/world/physicssystem.hpp
+++ b/apps/opencs/view/world/physicssystem.hpp
@@ -74,10 +74,10 @@ namespace CSVWorld
                     float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera);
 
             std::pair<std::string, float> distToGround(const Ogre::Vector3 &position,
-                    Ogre::Camera *camera);
+                    Ogre::Camera *camera, const float limit = 300000, bool ignorePgPoint = false);
 
             std::pair<std::string, float> distToClosest(const Ogre::Vector3 &position,
-                    Ogre::Camera *camera, const float limit = 300.0f);
+                    Ogre::Camera *camera, const float limit = 100.0f);
 
             std::string sceneNodeToRefId(std::string sceneNodeName);
 

From d65adc4376228d842c792c690bfb2bf95dbb3220 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Wed, 12 Nov 2014 22:04:48 +1100
Subject: [PATCH 15/29] Reduce code duplication.

---
 apps/opencs/view/render/mousestate.cpp        | 144 ++++++------------
 apps/opencs/view/render/mousestate.hpp        |  10 +-
 .../view/render/pagedworldspacewidget.cpp     |   5 +-
 .../view/render/pagedworldspacewidget.hpp     |   2 +-
 apps/opencs/view/render/worldspacewidget.cpp  |   8 +-
 apps/opencs/view/world/physicssystem.cpp      |  46 +++---
 apps/opencs/view/world/physicssystem.hpp      |   9 +-
 7 files changed, 83 insertions(+), 141 deletions(-)

diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index c9fcdbc867..65596b752d 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -58,8 +58,8 @@ namespace CSVRender
     MouseState::MouseState(WorldspaceWidget *parent)
         : mParent(parent), mPhysics(parent->getPhysics()), mSceneManager(parent->getSceneManager())
         , mCurrentObj(""), mMouseState(Mouse_Default), mOldCursorPos(0,0), mMouseEventTimer(0)
-        , mGrabbedSceneNode(""), mOrigObjPos(Ogre::Vector3()), mOrigMousePos(Ogre::Vector3())
-        , mOldMousePos(Ogre::Vector3()), mPlane(0)
+        , mGrabbedSceneNode(""), mGrabbedRefId(""), mOrigObjPos(Ogre::Vector3())
+        , mOrigMousePos(Ogre::Vector3()), mOldMousePos(Ogre::Vector3()), mPlane(0)
         , mColIndexPosX(0), mColIndexPosY(0), mColIndexPosZ(0), mIdTableModel(0)
     {
         const CSMWorld::RefCollection& references = mParent->mDocument.getData().getReferences();
@@ -120,16 +120,13 @@ namespace CSVRender
                     std::pair<bool, Ogre::Vector3> planeResult = mousePosOnPlane(event->pos(), *mPlane);
                     if(planeResult.first)
                     {
-                        if(mGrabbedSceneNode != "")
-                        {
-                            Ogre::Vector3 pos = mOrigObjPos + (planeResult.second-mOrigMousePos);
+                        Ogre::Vector3 pos = mOrigObjPos + (planeResult.second-mOrigMousePos);
 
-                            mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos);
-                            mPhysics->moveSceneNodes(mGrabbedSceneNode, pos);
-                            updateSceneWidgets();
+                        mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(pos);
+                        mPhysics->moveSceneNodes(mGrabbedSceneNode, pos);
+                        updateSceneWidgets();
 
-                            mOldMousePos = planeResult.second;
-                        }
+                        mOldMousePos = planeResult.second;
                     }
                 }
                 break;
@@ -157,11 +154,18 @@ namespace CSVRender
             {
                 if(event->buttons() & Qt::RightButton)
                 {
-                    std::pair<std::string, Ogre::Vector3> result = objectUnderCursor(event->x(), event->y());
+                    // get object or pathgrid
+                    std::pair<std::string, Ogre::Vector3> result = underCursor(event->x(), event->y(),
+                                CSVRender::Element_Reference|CSVRender::Element_Pathgrid);
+
                     if(result.first == "")
                         break;
 
-                    mGrabbedSceneNode = result.first;
+                    mGrabbedSceneNode = mPhysics->refIdToSceneNode(result.first, mSceneManager);
+                    if(!mSceneManager->hasSceneNode(mGrabbedSceneNode))
+                        break;
+
+                    mGrabbedRefId = result.first;
                     // ray test agaist the plane to get a starting position of the
                     // mouse in relation to the object position
                     std::pair<Ogre::Vector3, Ogre::Vector3> planeRes = planeAxis();
@@ -190,7 +194,9 @@ namespace CSVRender
         {
             case Mouse_Grab:
             {
-                std::pair<std::string, Ogre::Vector3> result = objectUnderCursor(event->x(), event->y());
+                std::pair<std::string, Ogre::Vector3> result = underCursor(event->x(), event->y(),
+                            CSVRender::Element_Reference|CSVRender::Element_Pathgrid);
+
                 if(result.first != "")
                 {
                     if(result.first == mCurrentObj)
@@ -208,11 +214,7 @@ namespace CSVRender
                     }
 //#if 0
                     // print some debug info
-                    std::string referenceId = mPhysics->sceneNodeToRefId(result.first);
-                    if(referenceId != "")
-                        std::cout << "result grab release: " << referenceId << std::endl;
-                    else
-                        std::cout << "result grab release: " << result.first << std::endl;
+                    std::cout << "result grab release: " << result.first << std::endl;
                     std::cout << "  hit pos "+ QString::number(result.second.x).toStdString()
                             + ", " + QString::number(result.second.y).toStdString()
                             + ", " + QString::number(result.second.z).toStdString() << std::endl;
@@ -224,24 +226,21 @@ namespace CSVRender
             {
                 // final placement
                 std::pair<bool, Ogre::Vector3> planeResult = mousePosOnPlane(event->pos(), *mPlane);
-                if(planeResult.first && mGrabbedSceneNode != "")
+                if(planeResult.first)
                 {
                     Ogre::Vector3 pos = mOrigObjPos + (planeResult.second-mOrigMousePos);
 
-                    // use the saved scene node name since the physics model has not moved yet
-                    std::string referenceId = mPhysics->sceneNodeToRefId(mGrabbedSceneNode);
-
-                    if(QString(referenceId.c_str()).contains(QRegExp("^Pathgrid")))
+                    // use the saved reference Id since the physics model has not moved yet
+                    if(QString(mGrabbedRefId.c_str()).contains(QRegExp("^Pathgrid")))
                     {
                         // FIXME: move pathgrid point, but don't save yet (need pathgrid
                         // table feature & its data structure to be completed)
                         // Also need to signal PathgridPoint object of change
                         std::pair<std::string, float> result =
                             mPhysics->distToClosest(pos, getCamera(), 600); // snap
-                        std::string refId = mPhysics->sceneNodeToRefId(result.first);
 
                         if(result.first != "" && // don't allow pathgrid points under the cursor
-                            !QString(refId.c_str()).contains(QRegExp("^Pathgrid")))
+                            !QString(result.first.c_str()).contains(QRegExp("^Pathgrid")))
                         {
                             pos.z -= result.second;
                             pos.z += 2; // arbitrary number, lift up slightly (maybe change the nif?)
@@ -251,7 +250,7 @@ namespace CSVRender
                             // the object is a pathgrid point at the begging and set
                             // a flag?)
                             placeObject(mGrabbedSceneNode, pos); // result.second
-                            mParent->pathgridMoved(referenceId, pos); // result.second
+                            mParent->pathgridMoved(mGrabbedRefId, pos); // result.second
                         }
                         else
                             cancelDrag(); // FIXME: does not allow editing if terrain not visible
@@ -260,22 +259,23 @@ namespace CSVRender
                     {
                         mParent->mDocument.getUndoStack().beginMacro (QObject::tr("Move Object"));
                         mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel,
-                            mIdTableModel->getModelIndex(referenceId, mColIndexPosX), pos.x));
+                            mIdTableModel->getModelIndex(mGrabbedRefId, mColIndexPosX), pos.x));
                         mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel,
-                            mIdTableModel->getModelIndex(referenceId, mColIndexPosY), pos.y));
+                            mIdTableModel->getModelIndex(mGrabbedRefId, mColIndexPosY), pos.y));
                         mParent->mDocument.getUndoStack().push(new CSMWorld::ModifyCommand(*mIdTableModel,
-                            mIdTableModel->getModelIndex(referenceId, mColIndexPosZ), pos.z));
+                            mIdTableModel->getModelIndex(mGrabbedRefId, mColIndexPosZ), pos.z));
                         mParent->mDocument.getUndoStack().endMacro();
                     }
 
                     // FIXME: highlight current object?
-                    //mCurrentObj = mGrabbedSceneNode; // FIXME: doesn't work?
+                    //mCurrentObj = mGrabbedRefId; // FIXME: doesn't work?
                     mCurrentObj = "";                   // whether the object is selected
 
                     mMouseState = Mouse_Edit;
 
                     // reset states
-                    mGrabbedSceneNode = "";          // id of the object
+                    mGrabbedRefId = "";              // id of the object
+                    mGrabbedSceneNode = "";
                     mOrigMousePos = Ogre::Vector3(); // starting pos of mouse in world space
                     mOrigObjPos =   Ogre::Vector3(); // starting pos of object in world space
                     mOldMousePos =  Ogre::Vector3(); // mouse pos to use in wheel event
@@ -287,7 +287,9 @@ namespace CSVRender
             case Mouse_Default:
             {
                 // probably terrain, check
-                std::pair<std::string, Ogre::Vector3> result = terrainUnderCursor(event->x(), event->y());
+                std::pair<std::string, Ogre::Vector3> result = underCursor(event->x(), event->y(),
+                        CSVRender::Element_Terrain);
+
                 if(result.first != "")
                 {
                     // FIXME: terrain editing goes here
@@ -354,9 +356,10 @@ namespace CSVRender
                             mousePos = planeResult.second;
                     }
 
-                    // Find the final world position of the cursor
+                    // Find the final world position of the object
                     Ogre::Vector3 finalPos = mOrigObjPos + (mousePos-mOrigMousePos);
 
+                    // update Ogre and Bullet
                     mSceneManager->getSceneNode(mGrabbedSceneNode)->setPosition(finalPos);
                     mPhysics->moveSceneNodes(mGrabbedSceneNode, finalPos);
                     updateSceneWidgets();
@@ -396,6 +399,7 @@ namespace CSVRender
                 mOldMousePos = Ogre::Vector3();
                 mOrigMousePos = Ogre::Vector3();
                 mOrigObjPos = Ogre::Vector3();
+                mGrabbedRefId = "";
                 mGrabbedSceneNode = "";
                 mCurrentObj = "";
                 mOldCursorPos = QPoint(0, 0);
@@ -444,21 +448,6 @@ namespace CSVRender
         }
     }
 
-    // FIXME: castRay converts referenceId to scene node name only to be re-converted
-    // here - investigate whether refactoring required
-    std::pair<std::string, Ogre::Vector3> MouseState::pgPointUnderCursor(const int mouseX, const int mouseY)
-    {
-        std::pair<std::string, Ogre::Vector3> result = objectUnderCursor(mouseX, mouseY);
-        std::string referenceId = mPhysics->sceneNodeToRefId(result.first);
-        if(result.first != "" &&
-                referenceId != "" && QString(referenceId.c_str()).contains(QRegExp("^Pathgrid")))
-        {
-            return std::make_pair(referenceId, result.second);
-        }
-
-        return std::make_pair("", Ogre::Vector3());
-    }
-
     std::pair<bool, Ogre::Vector3> MouseState::mousePosOnPlane(const QPoint &pos, const Ogre::Plane &plane)
     {
         // using a really small value seems to mess up with the projections
@@ -476,47 +465,8 @@ namespace CSVRender
             return std::make_pair(false, Ogre::Vector3()); // should only happen if the plane is too small
     }
 
-    std::pair<std::string, Ogre::Vector3> MouseState::terrainUnderCursor(const int mouseX, const int mouseY)
-    {
-        std::pair<std::string, Ogre::Vector3> result = anyUnderCursor(mouseX, mouseY);
-        if(result.first != "")
-        {
-            // FIXME: is there  a better way to distinguish terrain from objects?
-            QString name  = QString(result.first.c_str());
-            if(name.contains(QRegExp("^HeightField")))
-            {
-                return result;
-            }
-        }
-
-        return std::make_pair("", Ogre::Vector3());
-    }
-
-    // NOTE: also returns pathgrids
-    std::pair<std::string, Ogre::Vector3> MouseState::objectUnderCursor(const int mouseX, const int mouseY)
-    {
-        std::pair<std::string, Ogre::Vector3> result = anyUnderCursor(mouseX, mouseY);
-        if(result.first != "")
-        {
-            // NOTE: anything not terrain is assumed to be an object, e.g pathgrid points
-            QString name  = QString(result.first.c_str());
-            if(!name.contains(QRegExp("^HeightField")))
-            {
-                uint32_t visibilityMask = getViewport()->getVisibilityMask();
-                bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference);
-                bool ignorePathgrid = !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
-
-                if((!ignoreObjects || !ignorePathgrid) && mSceneManager->hasSceneNode(result.first))
-                {
-                    return result;
-                }
-            }
-        }
-
-        return std::make_pair("", Ogre::Vector3());
-    }
-
-    std::pair<std::string, Ogre::Vector3> MouseState::anyUnderCursor(const int mouseX, const int mouseY)
+    std::pair<std::string, Ogre::Vector3> MouseState::underCursor(const int mouseX,
+            const int mouseY, Ogre::uint32 elements)
     {
         if(!getViewport())
             return std::make_pair("", Ogre::Vector3());
@@ -524,13 +474,7 @@ namespace CSVRender
         float x = (float) mouseX / getViewport()->getActualWidth();
         float y = (float) mouseY / getViewport()->getActualHeight();
 
-        std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
-        if(result.first != "")
-        {
-            return result;
-        }
-
-        return std::make_pair("", Ogre::Vector3());
+        return mPhysics->castRay(x, y, mSceneManager, getCamera(), elements);
     }
 
     void MouseState::updateSceneWidgets()
@@ -554,14 +498,12 @@ namespace CSVRender
         return mParent->getCamera()->getViewport();
     }
 
-    void MouseState::placeObject(const std::string sceneNode, const Ogre::Vector3 &pos)
+    void MouseState::placeObject(const std::string sceneNodeName, const Ogre::Vector3 &pos)
     {
-        mSceneManager->getSceneNode(sceneNode)->setPosition(pos);
+        mSceneManager->getSceneNode(sceneNodeName)->setPosition(pos);
 
         // update physics
-        std::string refId = mPhysics->sceneNodeToRefId(sceneNode);
-
-        mPhysics->replaceObject(sceneNode, 1, pos, Ogre::Quaternion::IDENTITY);
+        mPhysics->replaceObject(sceneNodeName, 1, pos, Ogre::Quaternion::IDENTITY);
 
         // update all SceneWidgets and their SceneManagers
         updateSceneWidgets();
diff --git a/apps/opencs/view/render/mousestate.hpp b/apps/opencs/view/render/mousestate.hpp
index d21735a78d..d55d5f6c30 100644
--- a/apps/opencs/view/render/mousestate.hpp
+++ b/apps/opencs/view/render/mousestate.hpp
@@ -49,6 +49,7 @@ namespace CSVRender
             QPoint mOldCursorPos;
             std::string mCurrentObj;
             std::string mGrabbedSceneNode;
+            std::string mGrabbedRefId;
             QElapsedTimer *mMouseEventTimer;
             Ogre::Plane *mPlane;
             Ogre::Vector3 mOrigObjPos;
@@ -70,20 +71,19 @@ namespace CSVRender
             void mouseReleaseEvent (QMouseEvent *event);
             void mouseDoubleClickEvent (QMouseEvent *event);
             bool wheelEvent (QWheelEvent *event);
-            std::pair<std::string, Ogre::Vector3> pgPointUnderCursor(const int mouseX, const int mouseY);
-            std::pair<std::string, Ogre::Vector3> anyUnderCursor(const int mouseX, const int mouseY);
+
+            std::pair<std::string, Ogre::Vector3> underCursor(const int mouseX,
+                    const int mouseY, Ogre::uint32 elements);
 
             void cancelDrag();
 
         private:
 
             std::pair<bool, Ogre::Vector3> mousePosOnPlane(const QPoint &pos, const Ogre::Plane &plane);
-            std::pair<std::string, Ogre::Vector3> terrainUnderCursor(const int mouseX, const int mouseY);
-            std::pair<std::string, Ogre::Vector3> objectUnderCursor(const int mouseX, const int mouseY);
             std::pair<Ogre::Vector3, Ogre::Vector3> planeAxis();
             void updateSceneWidgets();
 
-            void placeObject(const std::string sceneNode, const Ogre::Vector3 &pos); // FIXME
+            void placeObject(const std::string sceneNodeName, const Ogre::Vector3 &pos); // FIXME
 
             Ogre::Camera *getCamera();     // friend access
             Ogre::Viewport *getViewport(); // friend access
diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp
index 506fd54df9..bb856dbffd 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.cpp
@@ -334,10 +334,9 @@ CSVRender::Cell *CSVRender::PagedWorldspaceWidget::findCell(const std::string &c
 }
 
 // NOTE: allow placing pathgrid points above objects and terrain
-void CSVRender::PagedWorldspaceWidget::pathgridInserted (const std::string &name, const Ogre::Vector3 &pos)
+void CSVRender::PagedWorldspaceWidget::pathgridInserted (const std::string &referenceId, const Ogre::Vector3 &pos)
 {
-    QString id = QString(name.c_str());
-    std::string referenceId = getPhysics()->sceneNodeToRefId(name); // FIXME: move back
+    QString id = QString(referenceId.c_str());
 
     bool terrain = id.startsWith("HeightField_");
     bool object = QString(referenceId.c_str()).startsWith("ref#");
diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp
index b5ca91880b..63f6caa60a 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.hpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.hpp
@@ -96,7 +96,7 @@ namespace CSVRender
             virtual void mouseDoubleClickEvent (QMouseEvent *event);
 
             // FIXME: temporary only until signals from the document is implemented
-            virtual void pathgridInserted (const std::string &terrain, const Ogre::Vector3 &pos);
+            virtual void pathgridInserted (const std::string &referenceId, const Ogre::Vector3 &pos);
             virtual void pathgridMoved (const std::string &pgName, const Ogre::Vector3 &pos);
             virtual void pathgridAboutToBeRemoved (const std::string &pgName);
 
diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp
index 4f85f3a30c..54d0ce8e85 100644
--- a/apps/opencs/view/render/worldspacewidget.cpp
+++ b/apps/opencs/view/render/worldspacewidget.cpp
@@ -411,7 +411,9 @@ void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event)
     else if(event->key() == Qt::Key_Delete)
     {
         QPoint p = this->mapFromGlobal(QCursor::pos());
-        std::pair<std::string, Ogre::Vector3> result = mMouse->pgPointUnderCursor(p.x(), p.y());
+        std::pair<std::string, Ogre::Vector3> result =
+                mMouse->underCursor(p.x(), p.y(), CSVRender::Element_Pathgrid);
+
         if(result.first != "")
         {
             pathgridAboutToBeRemoved(result.first);
@@ -422,7 +424,9 @@ void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event)
     else if(event->key() == Qt::Key_Insert)
     {
         QPoint p = this->mapFromGlobal(QCursor::pos());
-        std::pair<std::string, Ogre::Vector3> result = mMouse->anyUnderCursor(p.x(), p.y());
+        std::pair<std::string, Ogre::Vector3> result =
+                mMouse->underCursor(p.x(), p.y(), CSVRender::Element_Reference|CSVRender::Element_Terrain);
+
         if(result.first != "")
         {
             pathgridInserted(result.first, result.second);
diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp
index 9ff945829a..9012a06c2c 100644
--- a/apps/opencs/view/world/physicssystem.cpp
+++ b/apps/opencs/view/world/physicssystem.cpp
@@ -211,15 +211,11 @@ namespace CSVWorld
         }
     }
 
-    // sceneMgr: to lookup the scene node name from the object's referenceId
-    // camera: primarily used to get the visibility mask for the viewport
-    //
-    // returns the found object's scene node name and its position in the world space
-    //
     // WARNING: far clip distance is a global setting, if it changes in future
     //          this method will need to be updated
     std::pair<std::string, Ogre::Vector3> PhysicsSystem::castRay(float mouseX,
-            float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera)
+            float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera,
+            Ogre::uint32 elements)
     {
         // NOTE: there could be more than one camera for the scene manager
         // TODO: check whether camera belongs to sceneMgr
@@ -241,10 +237,10 @@ namespace CSVWorld
         _from = btVector3(from.x, from.y, from.z);
         _to = btVector3(to.x, to.y, to.z);
 
-        uint32_t visibilityMask = camera->getViewport()->getVisibilityMask();
-        bool ignoreHeightMap = !(visibilityMask & (uint32_t)CSVRender::Element_Terrain);
-        bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference);
-        bool ignorePathgrid = !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
+        Ogre::uint32 visibilityMask = camera->getViewport()->getVisibilityMask();
+        bool ignoreHeightMap = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Terrain);
+        bool ignoreObjects = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Reference);
+        bool ignorePathgrid = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Pathgrid);
 
         std::pair<std::string, float> result = std::make_pair("", -1);
         short mask = OEngine::Physic::CollisionType_Raycasting;
@@ -265,20 +261,18 @@ namespace CSVWorld
         }
 
         // result.first is the object's referenceId
-        if(result.first == "")
-            return std::make_pair("", Ogre::Vector3());
-        else
+        if(result.first != "" &&
+            ((elements & (Ogre::uint32)CSVRender::Element_Terrain &&
+            QString(result.first.c_str()).contains(QRegExp("^Height"))) ||
+            (elements & (Ogre::uint32)CSVRender::Element_Reference &&
+            QString(result.first.c_str()).contains(QRegExp("^ref#"))) ||
+            (elements & (Ogre::uint32)CSVRender::Element_Pathgrid &&
+            QString(result.first.c_str()).contains(QRegExp("^Pathgrid")))))
         {
-            // FIXME: maybe below logic belongs in the caller, i.e. terrainUnderCursor or
-            // objectUnderCursor
-            std::string name = refIdToSceneNode(result.first, sceneMgr);
-            if(name == "")
-                name = result.first; // prob terrain
-            else
-                name = refIdToSceneNode(result.first, sceneMgr); // prob object
-
-            return std::make_pair(name, ray.getPoint(farClipDist*result.second));
+            return std::make_pair(result.first, ray.getPoint(farClipDist*result.second));
         }
+        else
+            return std::make_pair("", Ogre::Vector3());
     }
 
     std::pair<std::string, float> PhysicsSystem::distToGround(const Ogre::Vector3 &position,
@@ -288,11 +282,11 @@ namespace CSVWorld
         _from = btVector3(position.x, position.y, position.z);
         _to = btVector3(position.x, position.y, position.z-limit);
 
-        uint32_t visibilityMask = camera->getViewport()->getVisibilityMask();
-        bool ignoreHeightMap = !(visibilityMask & (uint32_t)CSVRender::Element_Terrain);
-        bool ignoreObjects = !(visibilityMask & (uint32_t)CSVRender::Element_Reference);
+        Ogre::uint32 visibilityMask = camera->getViewport()->getVisibilityMask();
+        bool ignoreHeightMap = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Terrain);
+        bool ignoreObjects = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Reference);
         bool ignorePathgrid = ignorePgPoint ||
-                              !(visibilityMask & (uint32_t)CSVRender::Element_Pathgrid);
+                              !(visibilityMask & (Ogre::uint32)CSVRender::Element_Pathgrid);
 
         std::pair<std::string, float> result = std::make_pair("", -1);
         short mask = OEngine::Physic::CollisionType_Raycasting;
diff --git a/apps/opencs/view/world/physicssystem.hpp b/apps/opencs/view/world/physicssystem.hpp
index 589dca9c5b..5543c84b96 100644
--- a/apps/opencs/view/world/physicssystem.hpp
+++ b/apps/opencs/view/world/physicssystem.hpp
@@ -4,6 +4,8 @@
 #include <string>
 #include <map>
 
+#include <OgrePlatform.h>
+
 namespace Ogre
 {
     class Vector3;
@@ -71,7 +73,8 @@ namespace CSVWorld
 
             // return the object's SceneNode name and position for the given SceneManager
             std::pair<std::string, Ogre::Vector3> castRay(float mouseX,
-                    float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera);
+                    float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera,
+                    Ogre::uint32 elements = 0xFFFFFFFF);
 
             std::pair<std::string, float> distToGround(const Ogre::Vector3 &position,
                     Ogre::Camera *camera, const float limit = 300000, bool ignorePgPoint = false);
@@ -79,7 +82,7 @@ namespace CSVWorld
             std::pair<std::string, float> distToClosest(const Ogre::Vector3 &position,
                     Ogre::Camera *camera, const float limit = 100.0f);
 
-            std::string sceneNodeToRefId(std::string sceneNodeName);
+            std::string refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr);
 
             // for multi-scene manager per physics engine
             std::map<Ogre::SceneManager*, CSVRender::SceneWidget *> sceneWidgets();
@@ -91,7 +94,7 @@ namespace CSVWorld
 
             void updateSelectionHighlight(std::string sceneNode, const Ogre::Vector3 &position);
 
-            std::string refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr);
+            std::string sceneNodeToRefId(std::string sceneNodeName);
 
             Ogre::SceneManager *findSceneManager(std::string sceneNodeName);
     };

From 8c6890c682e283b401dbd4286deada91822f94c5 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Wed, 12 Nov 2014 22:28:41 +1100
Subject: [PATCH 16/29] Move element filtering back out of castRay().

---
 apps/opencs/view/render/mousestate.cpp   | 18 ++++++++++++++++--
 apps/opencs/view/render/mousestate.hpp   |  2 +-
 apps/opencs/view/world/physicssystem.cpp | 24 +++++++-----------------
 apps/opencs/view/world/physicssystem.hpp |  7 +++----
 4 files changed, 27 insertions(+), 24 deletions(-)

diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index 65596b752d..e62a4b1ff8 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -237,7 +237,8 @@ namespace CSVRender
                         // table feature & its data structure to be completed)
                         // Also need to signal PathgridPoint object of change
                         std::pair<std::string, float> result =
-                            mPhysics->distToClosest(pos, getCamera(), 600); // snap
+                            mPhysics->distToClosest(pos,
+                                    getCamera()->getViewport()->getVisibilityMask(), 600); // snap
 
                         if(result.first != "" && // don't allow pathgrid points under the cursor
                             !QString(result.first.c_str()).contains(QRegExp("^Pathgrid")))
@@ -474,7 +475,20 @@ namespace CSVRender
         float x = (float) mouseX / getViewport()->getActualWidth();
         float y = (float) mouseY / getViewport()->getActualHeight();
 
-        return mPhysics->castRay(x, y, mSceneManager, getCamera(), elements);
+        std::pair<std::string, Ogre::Vector3> result = mPhysics->castRay(x, y, mSceneManager, getCamera());
+
+        if(result.first != "" &&
+            ((elements & (Ogre::uint32)CSVRender::Element_Terrain &&
+            QString(result.first.c_str()).contains(QRegExp("^Height"))) ||
+            (elements & (Ogre::uint32)CSVRender::Element_Reference &&
+            QString(result.first.c_str()).contains(QRegExp("^ref#"))) ||
+            (elements & (Ogre::uint32)CSVRender::Element_Pathgrid &&
+            QString(result.first.c_str()).contains(QRegExp("^Pathgrid")))))
+        {
+            return result;
+        }
+        else
+            return std::make_pair("", Ogre::Vector3());
     }
 
     void MouseState::updateSceneWidgets()
diff --git a/apps/opencs/view/render/mousestate.hpp b/apps/opencs/view/render/mousestate.hpp
index d55d5f6c30..cde53f2c58 100644
--- a/apps/opencs/view/render/mousestate.hpp
+++ b/apps/opencs/view/render/mousestate.hpp
@@ -73,7 +73,7 @@ namespace CSVRender
             bool wheelEvent (QWheelEvent *event);
 
             std::pair<std::string, Ogre::Vector3> underCursor(const int mouseX,
-                    const int mouseY, Ogre::uint32 elements);
+                    const int mouseY, Ogre::uint32 elements = 0xFFFFFFFF);
 
             void cancelDrag();
 
diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp
index 9012a06c2c..6450b5cf6c 100644
--- a/apps/opencs/view/world/physicssystem.cpp
+++ b/apps/opencs/view/world/physicssystem.cpp
@@ -214,8 +214,7 @@ namespace CSVWorld
     // WARNING: far clip distance is a global setting, if it changes in future
     //          this method will need to be updated
     std::pair<std::string, Ogre::Vector3> PhysicsSystem::castRay(float mouseX,
-            float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera,
-            Ogre::uint32 elements)
+            float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera)
     {
         // NOTE: there could be more than one camera for the scene manager
         // TODO: check whether camera belongs to sceneMgr
@@ -261,28 +260,19 @@ namespace CSVWorld
         }
 
         // result.first is the object's referenceId
-        if(result.first != "" &&
-            ((elements & (Ogre::uint32)CSVRender::Element_Terrain &&
-            QString(result.first.c_str()).contains(QRegExp("^Height"))) ||
-            (elements & (Ogre::uint32)CSVRender::Element_Reference &&
-            QString(result.first.c_str()).contains(QRegExp("^ref#"))) ||
-            (elements & (Ogre::uint32)CSVRender::Element_Pathgrid &&
-            QString(result.first.c_str()).contains(QRegExp("^Pathgrid")))))
-        {
-            return std::make_pair(result.first, ray.getPoint(farClipDist*result.second));
-        }
-        else
+        if(result.first == "")
             return std::make_pair("", Ogre::Vector3());
+        else
+            return std::make_pair(result.first, ray.getPoint(farClipDist*result.second));
     }
 
     std::pair<std::string, float> PhysicsSystem::distToGround(const Ogre::Vector3 &position,
-            Ogre::Camera *camera, const float limit, bool ignorePgPoint)
+            Ogre::uint32 visibilityMask, const float limit, bool ignorePgPoint)
     {
         btVector3 _from, _to;
         _from = btVector3(position.x, position.y, position.z);
         _to = btVector3(position.x, position.y, position.z-limit);
 
-        Ogre::uint32 visibilityMask = camera->getViewport()->getVisibilityMask();
         bool ignoreHeightMap = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Terrain);
         bool ignoreObjects = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Reference);
         bool ignorePathgrid = ignorePgPoint ||
@@ -315,13 +305,13 @@ namespace CSVWorld
 
     // tries to find the distance to the "top" of the closest object
     std::pair<std::string, float> PhysicsSystem::distToClosest(const Ogre::Vector3 &position,
-            Ogre::Camera *camera, const float limit)
+            Ogre::uint32 visibilityMask, const float limit)
     {
         const float thickness = 50; // arbitrary number
 
         std::pair<std::string, float> resDown =
             distToGround(Ogre::Vector3(position.x, position.y, position.z+thickness),
-                         camera, limit+thickness, true);
+                         visibilityMask, limit+thickness, true);
 
         if(resDown.first != "")
             return std::make_pair(resDown.first, resDown.second-thickness);
diff --git a/apps/opencs/view/world/physicssystem.hpp b/apps/opencs/view/world/physicssystem.hpp
index 5543c84b96..f71ab685c7 100644
--- a/apps/opencs/view/world/physicssystem.hpp
+++ b/apps/opencs/view/world/physicssystem.hpp
@@ -73,14 +73,13 @@ namespace CSVWorld
 
             // return the object's SceneNode name and position for the given SceneManager
             std::pair<std::string, Ogre::Vector3> castRay(float mouseX,
-                    float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera,
-                    Ogre::uint32 elements = 0xFFFFFFFF);
+                    float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera);
 
             std::pair<std::string, float> distToGround(const Ogre::Vector3 &position,
-                    Ogre::Camera *camera, const float limit = 300000, bool ignorePgPoint = false);
+                    Ogre::uint32 visibilityMask, const float limit = 300000, bool ignorePgPoint = false);
 
             std::pair<std::string, float> distToClosest(const Ogre::Vector3 &position,
-                    Ogre::Camera *camera, const float limit = 100.0f);
+                    Ogre::uint32 visibilityMask, const float limit = 100.0f);
 
             std::string refIdToSceneNode(std::string referenceId, Ogre::SceneManager *sceneMgr);
 

From 12511778d1d262365205b0c532c2d62e670a6ba2 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Wed, 12 Nov 2014 22:43:22 +1100
Subject: [PATCH 17/29] Remove duplicate parameter.

---
 apps/opencs/view/render/mousestate.cpp   | 2 +-
 apps/opencs/view/world/physicssystem.cpp | 9 ++++-----
 apps/opencs/view/world/physicssystem.hpp | 2 +-
 3 files changed, 6 insertions(+), 7 deletions(-)

diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index e62a4b1ff8..41908d11c3 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -244,7 +244,7 @@ namespace CSVRender
                             !QString(result.first.c_str()).contains(QRegExp("^Pathgrid")))
                         {
                             pos.z -= result.second;
-                            pos.z += 2; // arbitrary number, lift up slightly (maybe change the nif?)
+                            pos.z += 1; // arbitrary number, lift up slightly (maybe change the nif?)
                             // FIXME: rather than just updating at the end, should
                             // consider providing visual feedback of terrain height
                             // while dragging the pathgrid point (maybe check whether
diff --git a/apps/opencs/view/world/physicssystem.cpp b/apps/opencs/view/world/physicssystem.cpp
index 6450b5cf6c..c5f1d0a75e 100644
--- a/apps/opencs/view/world/physicssystem.cpp
+++ b/apps/opencs/view/world/physicssystem.cpp
@@ -267,7 +267,7 @@ namespace CSVWorld
     }
 
     std::pair<std::string, float> PhysicsSystem::distToGround(const Ogre::Vector3 &position,
-            Ogre::uint32 visibilityMask, const float limit, bool ignorePgPoint)
+            Ogre::uint32 visibilityMask, const float limit)
     {
         btVector3 _from, _to;
         _from = btVector3(position.x, position.y, position.z);
@@ -275,8 +275,7 @@ namespace CSVWorld
 
         bool ignoreHeightMap = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Terrain);
         bool ignoreObjects = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Reference);
-        bool ignorePathgrid = ignorePgPoint ||
-                              !(visibilityMask & (Ogre::uint32)CSVRender::Element_Pathgrid);
+        bool ignorePathgrid = !(visibilityMask & (Ogre::uint32)CSVRender::Element_Pathgrid);
 
         std::pair<std::string, float> result = std::make_pair("", -1);
         short mask = OEngine::Physic::CollisionType_Raycasting;
@@ -303,7 +302,7 @@ namespace CSVWorld
             return std::make_pair(result.first, limit*result.second);
     }
 
-    // tries to find the distance to the "top" of the closest object
+    // tries to find the distance to the "top" of the closest object (ignores pathgrid points)
     std::pair<std::string, float> PhysicsSystem::distToClosest(const Ogre::Vector3 &position,
             Ogre::uint32 visibilityMask, const float limit)
     {
@@ -311,7 +310,7 @@ namespace CSVWorld
 
         std::pair<std::string, float> resDown =
             distToGround(Ogre::Vector3(position.x, position.y, position.z+thickness),
-                         visibilityMask, limit+thickness, true);
+                         visibilityMask&(~CSVRender::Element_Pathgrid), limit+thickness);
 
         if(resDown.first != "")
             return std::make_pair(resDown.first, resDown.second-thickness);
diff --git a/apps/opencs/view/world/physicssystem.hpp b/apps/opencs/view/world/physicssystem.hpp
index f71ab685c7..839d556543 100644
--- a/apps/opencs/view/world/physicssystem.hpp
+++ b/apps/opencs/view/world/physicssystem.hpp
@@ -76,7 +76,7 @@ namespace CSVWorld
                     float mouseY, Ogre::SceneManager *sceneMgr, Ogre::Camera *camera);
 
             std::pair<std::string, float> distToGround(const Ogre::Vector3 &position,
-                    Ogre::uint32 visibilityMask, const float limit = 300000, bool ignorePgPoint = false);
+                    Ogre::uint32 visibilityMask, const float limit = 300000);
 
             std::pair<std::string, float> distToClosest(const Ogre::Vector3 &position,
                     Ogre::uint32 visibilityMask, const float limit = 100.0f);

From e430dcfd8ad1884cfab800e38874bcc1e854cfff Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Thu, 13 Nov 2014 05:59:04 +1100
Subject: [PATCH 18/29] Check the resource group's existence before destroying
 it.

---
 apps/opencs/view/render/cell.cpp | 10 +++++++---
 1 file changed, 7 insertions(+), 3 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 0435159075..40cf904f84 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -42,8 +42,13 @@ void CSVRender::Cell::createGridMaterials()
 
 void CSVRender::Cell::destroyGridMaterials()
 {
-    if(!Ogre::MaterialManager::getSingleton().getByName(PG_LINE_MATERIAL, DEBUGGING_GROUP).isNull())
-        Ogre::MaterialManager::getSingleton().remove(PG_LINE_MATERIAL);
+    if(Ogre::ResourceGroupManager::getSingleton().resourceGroupExists(DEBUGGING_GROUP))
+    {
+        if(!Ogre::MaterialManager::getSingleton().getByName(PG_LINE_MATERIAL, DEBUGGING_GROUP).isNull())
+            Ogre::MaterialManager::getSingleton().remove(PG_LINE_MATERIAL);
+
+        Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(DEBUGGING_GROUP);
+    }
 }
 
 Ogre::ManualObject *CSVRender::Cell::createPathgridEdge(const std::string &name,
@@ -165,7 +170,6 @@ CSVRender::Cell::~Cell()
         }
     }
     destroyGridMaterials();
-    Ogre::ResourceGroupManager::getSingleton().destroyResourceGroup(DEBUGGING_GROUP);
 
     for(std::map<std::string, PathgridPoint *>::iterator iter (mPgPoints.begin());
         iter!=mPgPoints.end(); ++iter)

From 0e0ad97a9108580b9ee8c8472342d4624c98a6ad Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Thu, 13 Nov 2014 06:36:47 +1100
Subject: [PATCH 19/29] Pathgrid edge resource management for editing multiple
 cells or multiple documents.

---
 apps/opencs/view/render/cell.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 40cf904f84..32ef101e56 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -28,6 +28,9 @@ namespace CSVRender
 
 void CSVRender::Cell::createGridMaterials()
 {
+    if(!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists(DEBUGGING_GROUP))
+        Ogre::ResourceGroupManager::getSingleton().createResourceGroup(DEBUGGING_GROUP);
+
     if(Ogre::MaterialManager::getSingleton().getByName(PG_LINE_MATERIAL, DEBUGGING_GROUP).isNull())
     {
         Ogre::MaterialPtr lineMatPtr =
@@ -56,6 +59,7 @@ Ogre::ManualObject *CSVRender::Cell::createPathgridEdge(const std::string &name,
 {
     Ogre::ManualObject *result = mSceneMgr->createManualObject(name);
 
+    createGridMaterials();
     result->begin(PG_LINE_MATERIAL, Ogre::RenderOperation::OT_LINE_LIST);
 
     Ogre::Vector3 direction = (end - start);
@@ -323,8 +327,6 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
 //  - repainting edges while moving
 void CSVRender::Cell::loadPathgrid()
 {
-    if(!Ogre::ResourceGroupManager::getSingleton().resourceGroupExists(DEBUGGING_GROUP))
-        Ogre::ResourceGroupManager::getSingleton().createResourceGroup(DEBUGGING_GROUP);
     createGridMaterials();
 
     int worldsize = ESM::Land::REAL_SIZE;

From ba1a42ec05161d5816a8bf67855cf21ba4afc308 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Thu, 13 Nov 2014 07:41:31 +1100
Subject: [PATCH 20/29] Don't delete physics object if it was never created.

---
 apps/opencs/view/render/object.cpp | 6 ++++--
 apps/opencs/view/render/object.hpp | 1 +
 2 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp
index 21219db8f4..e273717664 100644
--- a/apps/opencs/view/render/object.cpp
+++ b/apps/opencs/view/render/object.cpp
@@ -93,6 +93,7 @@ void CSVRender::Object::update()
             Ogre::Quaternion yr (Ogre::Radian (-reference.mPos.rot[1]), Ogre::Vector3::UNIT_Y);
             Ogre::Quaternion zr (Ogre::Radian (-reference.mPos.rot[2]), Ogre::Vector3::UNIT_Z);
 
+            mPhysicsObject = mReferenceId;
             mPhysics->addObject("meshes\\" + model, mBase->getName(), mReferenceId, reference.mScale, position, xr*yr*zr);
         }
     }
@@ -134,7 +135,7 @@ const CSMWorld::CellRef& CSVRender::Object::getReference() const
 CSVRender::Object::Object (const CSMWorld::Data& data, Ogre::SceneNode *cellNode,
     const std::string& id, bool referenceable, CSVWorld::PhysicsSystem *physics,
     bool forceBaseToZero)
-: mData (data), mBase (0), mForceBaseToZero (forceBaseToZero), mPhysics(physics)
+: mData (data), mBase (0), mForceBaseToZero (forceBaseToZero), mPhysics(physics), mPhysicsObject("")
 {
     mBase = cellNode->createChildSceneNode();
 
@@ -156,7 +157,8 @@ CSVRender::Object::~Object()
 {
     clear();
 
-    mPhysics->removeObject(mBase->getName());
+    if(mPhysicsObject != "")
+        mPhysics->removeObject(mBase->getName());
 
     if (mBase)
         mBase->getCreator()->destroySceneNode (mBase);
diff --git a/apps/opencs/view/render/object.hpp b/apps/opencs/view/render/object.hpp
index eba2dc8148..8ce204e1c7 100644
--- a/apps/opencs/view/render/object.hpp
+++ b/apps/opencs/view/render/object.hpp
@@ -32,6 +32,7 @@ namespace CSVRender
             NifOgre::ObjectScenePtr mObject;
             bool mForceBaseToZero;
             CSVWorld::PhysicsSystem *mPhysics;
+            std::string mPhysicsObject;
 
             /// Not implemented
             Object (const Object&);

From 0a66877cf150a178b7cbc977ed553b2539083bca Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Thu, 13 Nov 2014 07:47:32 +1100
Subject: [PATCH 21/29] Remove no longer needed code.

---
 apps/opencs/view/render/cell.cpp         | 2 --
 apps/opencs/view/world/physicssystem.hpp | 2 --
 2 files changed, 4 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 32ef101e56..12e00ca46e 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -327,8 +327,6 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
 //  - repainting edges while moving
 void CSVRender::Cell::loadPathgrid()
 {
-    createGridMaterials();
-
     int worldsize = ESM::Land::REAL_SIZE;
 
     CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgridCollection = mData.getPathgrids();
diff --git a/apps/opencs/view/world/physicssystem.hpp b/apps/opencs/view/world/physicssystem.hpp
index 839d556543..6a634ed26b 100644
--- a/apps/opencs/view/world/physicssystem.hpp
+++ b/apps/opencs/view/world/physicssystem.hpp
@@ -91,8 +91,6 @@ namespace CSVWorld
             void moveSceneNodeImpl(const std::string sceneNodeName,
                     const std::string referenceId, const Ogre::Vector3 &position);
 
-            void updateSelectionHighlight(std::string sceneNode, const Ogre::Vector3 &position);
-
             std::string sceneNodeToRefId(std::string sceneNodeName);
 
             Ogre::SceneManager *findSceneManager(std::string sceneNodeName);

From 4c3c67422313ba747808eff5a6c3bc2882671c98 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Tue, 9 Dec 2014 19:37:37 +1100
Subject: [PATCH 22/29] Resolve merge issues.

---
 apps/opencs/view/render/pathgridpoint.cpp | 2 +-
 apps/opencs/view/render/pathgridpoint.hpp | 7 +++++--
 2 files changed, 6 insertions(+), 3 deletions(-)

diff --git a/apps/opencs/view/render/pathgridpoint.cpp b/apps/opencs/view/render/pathgridpoint.cpp
index 3e365932f9..85b03beea9 100644
--- a/apps/opencs/view/render/pathgridpoint.cpp
+++ b/apps/opencs/view/render/pathgridpoint.cpp
@@ -15,7 +15,7 @@
 namespace CSVRender
 {
     PathgridPoint::PathgridPoint(const std::string &name,
-            Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, CSVWorld::PhysicsSystem *physics)
+            Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, boost::shared_ptr<CSVWorld::PhysicsSystem> physics)
         : mBase(cellNode), mPhysics(physics)
     {
         mBase = cellNode->createChildSceneNode();
diff --git a/apps/opencs/view/render/pathgridpoint.hpp b/apps/opencs/view/render/pathgridpoint.hpp
index 3419082c3a..54fe70e311 100644
--- a/apps/opencs/view/render/pathgridpoint.hpp
+++ b/apps/opencs/view/render/pathgridpoint.hpp
@@ -1,6 +1,8 @@
 #ifndef OPENCS_VIEW_PATHGRIDPOINT_H
 #define OPENCS_VIEW_PATHGRIDPOINT_H
 
+#include <boost/shared_ptr.hpp>
+
 #include <components/nifogre/ogrenifloader.hpp>
 
 namespace Ogre
@@ -19,14 +21,15 @@ namespace CSVRender
 {
     class PathgridPoint
     {
-            CSVWorld::PhysicsSystem *mPhysics; // local copy
+            boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics; // local copy
             NifOgre::ObjectScenePtr mPgPoint;
             Ogre::SceneNode *mBase;
 
         public:
 
             PathgridPoint(const std::string &name,
-                Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos, CSVWorld::PhysicsSystem *physics);
+                Ogre::SceneNode *cellNode, const Ogre::Vector3 &pos,
+                boost::shared_ptr<CSVWorld::PhysicsSystem> physics);
 
             ~PathgridPoint();
 

From 68dbf929a2c024d0cbc3086ff15865a7125844af Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sat, 18 Apr 2015 16:14:25 +1000
Subject: [PATCH 23/29] Add updated pathgrid marker nif file and fix merge
 issues.

---
 apps/opencs/view/render/cell.cpp |   3 +--
 apps/opencs/view/render/cell.hpp |   3 ---
 files/materials/pathgrid_pt.nif  | Bin 1683 -> 1484 bytes
 3 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index d13649d3ee..ef63909afa 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -93,7 +93,7 @@ bool CSVRender::Cell::addObjects (int start, int end)
     bool modified = false;
 
     const CSMWorld::RefCollection& collection = mData.getReferences();
-    
+
     for (int i=start; i<=end; ++i)
     {
         std::string cell = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mCell);
@@ -177,7 +177,6 @@ CSVRender::Cell::~Cell()
     if (mTerrain.get())
         mPhysics->removeHeightField(mSceneMgr, mX, mY);
 
-
     for (std::map<std::string, Object *>::iterator iter (mObjects.begin());
         iter!=mObjects.end(); ++iter)
         delete iter->second;
diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp
index e504fc7a48..0afd0920b3 100644
--- a/apps/opencs/view/render/cell.hpp
+++ b/apps/opencs/view/render/cell.hpp
@@ -11,11 +11,8 @@
 
 #ifndef Q_MOC_RUN
 #include <components/terrain/terraingrid.hpp>
-<<<<<<< .mine
 #include <components/esm/loadpgrd.hpp>  // FIXME: temporaty storage until saving to document
-=======
 #endif
->>>>>>> .theirs
 
 #include "object.hpp"
 
diff --git a/files/materials/pathgrid_pt.nif b/files/materials/pathgrid_pt.nif
index 579b03036c05d5deefe564880dbe312ebbb671f2..cc37e0ee37ef732d4b42d2aadde343580bbd717c 100644
GIT binary patch
literal 1484
zcmb7E%}&BV5FVrmA_{^(9C-jk(0K4*Bp5X@(ME&uYE~`Mq)1ci!5eQz;#ptA8;@Z0
zo9(V_q4+ao*>7gP`N^<@?Sv=2o)bJewPV-AS3l^9aI<y=liPo&?bI9iZ0F~QtQ--|
z<Jhj<zjKy>d=Yl<1NZLQ53g^$PbCF-Oc^vyjVMW<v_%o9N+}$!%96*i-OIpjcZKhi
zpv60=HFOUa(+!!-pQOiaX+(2<zIC<?Fjpwzw%wNJcg1<o_njbohDEyAJt|XKGTOd#
z`{aoriIh^DGZ8w0D?H8N>1kWN(b(dIEIfxuI-U3ZG45cx1L*o_58r#Ox7X7q|5D?r
z9g0v?<y2uF@Ok(+Iv^^%M2Bq!r6*$eq=2VmBLnhq4vtlvSRUnLu35|M!NjpVM(k8A
z_++2VG4VmfH1ot_nXfogc9};{e3ZEu?!HyzD^9|&Ngs3mfQJTW5)Zi(l_wFyJ>YoN
z&n!&faL+Qw518|4&J^Z2)erZXh}o|tdPiEzQB}eewkS;&Ww6Cr%FzP0c##UUM9Wm9
z60KklDzr*#v`!m{_;ts>H^S1T)E(bn45}F?us&&v7+GArW7C^5|KClS^`c^S-Pi1i
b@yz&tCynW4R1aw&3wjE7G*L@>^gI0m*A66H

literal 1683
zcmb_c&2G~`5T2w?TS{o5<)=VFsl*Xg0afBs5fvdM4p>M<NE}dPwVTFLLadEBRN~+V
z;02o3sJOOLU!vj-xbO;u?_00qpSUy!##-+;GvCgP*Sq7Y@NK`Z^iVb4_&vM^y6=Tg
z8y{iv2VWZ7TTMLAm#QMpqKM36xV}3$P%A*eVLJ%hS{<}|o<3A#D|NWC61B3LNSR)=
z#Ve3jDi};lS;BDr58B`BdVyMlmX;87kB0vKu+dD)aJV8P#S~?F-ZqETEP-<ptp>~t
z{C?Np?FC)$y&eQghexm&7u~;CWz{hD0=54Y+fzoWSk5~yRND7?35Oe)*tVL@XADTG
z$msazOQxZWjz48KHiA7Fu|IzA>~=bx)-sfI<6e8A_t<Q#Nc~kj$Ts*KAOgnBF~Q(X
zB>M49L(g%nZumu9vX(p%#5uQlEoZ8R#7-VYypD5R1EVB3_buexwNtrloXhIZ@iTF9
z{Hz^${+)}5IF;yZR(x^t`RvblZ2ox}hRvJ-a%RA%8BP(Knrxg>jG7~^xAk0x1sv9V
z%5N;g-(S>Q9HU>GX1)qD24gYiESTIxGG_#)x;!zCin-NQ+-pZ_Xf6w~DA&+ju7hzy
zmSsh5$}QZ@HMuQ!<gVP4b-dT*zC4hJvVr%8JVK;d+R^0lI!R4-&bJ!ut@)8DiF(l%
zdlxXV9ZX;9`TuaKvxh&ehUn_etRg>;I=7Xbdk!1z;9(1`L8re49DCSSsnHYnc}(&b
DaQ$}<


From 27a73a25e345307cfa92adc38b7990c69dc0c74b Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Thu, 23 Apr 2015 08:50:05 +1000
Subject: [PATCH 24/29] Saving to document via UndoStack implemented.

---
 apps/opencs/model/world/commands.cpp          |  26 ++++
 apps/opencs/model/world/commands.hpp          |  26 +++-
 apps/opencs/model/world/idtree.cpp            |   4 +
 .../model/world/nestedcoladapterimp.cpp       |   6 +
 .../model/world/nestedcoladapterimp.hpp       |  16 ---
 .../opencs/model/world/pathgridpointswrap.hpp |  26 ++++
 apps/opencs/view/render/cell.cpp              | 129 ++++++++++++------
 apps/opencs/view/render/cell.hpp              |  19 ++-
 .../view/render/pagedworldspacewidget.cpp     |   2 +-
 .../view/render/unpagedworldspacewidget.cpp   |   4 +-
 10 files changed, 186 insertions(+), 72 deletions(-)
 create mode 100644 apps/opencs/model/world/pathgridpointswrap.hpp

diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp
index d12c5d228f..11a675db0c 100644
--- a/apps/opencs/model/world/commands.cpp
+++ b/apps/opencs/model/world/commands.cpp
@@ -244,3 +244,29 @@ const CSMWorld::NestedTableWrapperBase& CSMWorld::NestedTableStoring::getOld() c
 {
     return *mOld;
 }
+
+// Current interface does not allow adding a non-blank row, so we're forced to modify
+// the whole record.
+CSMWorld::ModifyPathgridCommand::ModifyPathgridCommand(IdTree& model,
+    const std::string& id, int parentColumn, NestedTableWrapperBase* newRecord, QUndoCommand* parent)
+    : mModel(model), mId(id), mParentColumn(parentColumn), mRecord(newRecord)
+    , QUndoCommand(parent), NestedTableStoring(model, id, parentColumn)
+{
+    setText (("Modify Pathgrid record " + mId).c_str()); // FIXME: better description
+}
+
+void CSMWorld::ModifyPathgridCommand::redo()
+{
+    const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
+
+    mModel.setNestedTable(parentIndex, *mRecord);
+}
+
+void CSMWorld::ModifyPathgridCommand::undo()
+{
+    const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
+
+    mModel.setNestedTable(parentIndex, getOld());
+
+    // FIXME: needs to tell the cell to redraw, possibly using signals
+}
diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp
index a70b361783..4e4de25c75 100644
--- a/apps/opencs/model/world/commands.hpp
+++ b/apps/opencs/model/world/commands.hpp
@@ -168,7 +168,8 @@ namespace CSMWorld
 
         public:
 
-            DeleteNestedCommand (IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0);
+            DeleteNestedCommand (IdTree& model,
+                    const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0);
 
             virtual void redo();
 
@@ -187,7 +188,28 @@ namespace CSMWorld
 
         public:
 
-            AddNestedCommand(IdTree& model, const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0);
+            AddNestedCommand(IdTree& model,
+                    const std::string& id, int nestedRow, int parentColumn, QUndoCommand* parent = 0);
+
+            virtual void redo();
+
+            virtual void undo();
+    };
+
+    class ModifyPathgridCommand : public QUndoCommand, private NestedTableStoring
+    {
+            IdTree& mModel;
+            std::string mId;
+
+            int mParentColumn;
+
+            NestedTableWrapperBase* mRecord;
+
+        public:
+
+            // if newEdges is NULL, only the paths are updated
+            ModifyPathgridCommand(IdTree& model, const std::string& id, int parentColumn,
+                    NestedTableWrapperBase* newRecord, QUndoCommand* parent = 0);
 
             virtual void redo();
 
diff --git a/apps/opencs/model/world/idtree.cpp b/apps/opencs/model/world/idtree.cpp
index 06db09a0fc..3cdc58f4d3 100644
--- a/apps/opencs/model/world/idtree.cpp
+++ b/apps/opencs/model/world/idtree.cpp
@@ -248,6 +248,10 @@ void CSMWorld::IdTree::setNestedTable(const QModelIndex& index, const CSMWorld::
     {
         emit resetEnd(this->index(index.row(), 0).data().toString());
     }
+    // FIXME: Removing pathgrid points will also remove pathgrid edges, but we have
+    //        no way of emitting signals to indicate those changes.  In addition, the
+    //        removed edges can be any index (and there can be several edges removed
+    //        but always by a factor of two)
 }
 
 CSMWorld::NestedTableWrapperBase* CSMWorld::IdTree::nestedTable(const QModelIndex& index) const
diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp
index d29155a478..060a6bb39a 100644
--- a/apps/opencs/model/world/nestedcoladapterimp.cpp
+++ b/apps/opencs/model/world/nestedcoladapterimp.cpp
@@ -6,6 +6,7 @@
 #include "idcollection.hpp"
 #include "pathgrid.hpp"
 #include "info.hpp"
+#include "pathgridpointswrap.hpp"
 
 namespace CSMWorld
 {
@@ -147,6 +148,8 @@ namespace CSMWorld
     PathgridEdgeListAdapter::PathgridEdgeListAdapter () {}
 
     // ToDo: seems to be auto-sorted in the dialog table display after insertion
+    //
+    // FIXME: edges should be added in pairs
     void PathgridEdgeListAdapter::addRow(Record<Pathgrid>& record, int position) const
     {
         Pathgrid pathgrid = record.get();
@@ -168,6 +171,7 @@ namespace CSMWorld
         record.setModified (pathgrid);
     }
 
+    // FIXME: edges should be removed in pairs and Point.mConnectionNum updated
     void PathgridEdgeListAdapter::removeRow(Record<Pathgrid>& record, int rowToRemove) const
     {
         Pathgrid pathgrid = record.get();
@@ -218,6 +222,8 @@ namespace CSMWorld
     }
 
     // ToDo: detect duplicates in mEdges
+    //
+    // FIXME: Point.mConnectionNum needs to be updated
     void PathgridEdgeListAdapter::setData(Record<Pathgrid>& record,
             const QVariant& value, int subRowIndex, int subColIndex) const
     {
diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp
index 96dcd943de..2613d0f203 100644
--- a/apps/opencs/model/world/nestedcoladapterimp.hpp
+++ b/apps/opencs/model/world/nestedcoladapterimp.hpp
@@ -3,7 +3,6 @@
 
 #include <QVariant>
 
-#include <components/esm/loadpgrd.hpp>
 #include <components/esm/effectlist.hpp>
 #include <components/esm/loadmgef.hpp> // for converting magic effect id to string & back
 #include <components/esm/loadskil.hpp> // for converting skill names
@@ -23,21 +22,6 @@ namespace CSMWorld
     struct Pathgrid;
     struct Info;
 
-    struct PathgridPointsWrap : public NestedTableWrapperBase
-    {
-        ESM::Pathgrid mRecord;
-
-        PathgridPointsWrap(ESM::Pathgrid pathgrid)
-            : mRecord(pathgrid) {}
-
-        virtual ~PathgridPointsWrap() {}
-
-        virtual int size() const
-        {
-            return mRecord.mPoints.size(); // used in IdTree::setNestedTable()
-        }
-    };
-
     class PathgridPointListAdapter : public NestedColumnAdapter<Pathgrid>
     {
     public:
diff --git a/apps/opencs/model/world/pathgridpointswrap.hpp b/apps/opencs/model/world/pathgridpointswrap.hpp
new file mode 100644
index 0000000000..6f1f61fc61
--- /dev/null
+++ b/apps/opencs/model/world/pathgridpointswrap.hpp
@@ -0,0 +1,26 @@
+#ifndef CSM_WOLRD_PATHGRIDPOINTSWRAP_H
+#define CSM_WOLRD_PATHGRIDPOINTSWRAP_H
+
+#include <components/esm/loadpgrd.hpp>
+
+#include "nestedtablewrapper.hpp"
+
+namespace CSMWorld
+{
+    struct PathgridPointsWrap : public NestedTableWrapperBase
+    {
+        ESM::Pathgrid mRecord;
+
+        PathgridPointsWrap(ESM::Pathgrid pathgrid)
+            : mRecord(pathgrid) {}
+
+        virtual ~PathgridPointsWrap() {}
+
+        virtual int size() const
+        {
+            return mRecord.mPoints.size(); // used in IdTree::setNestedTable()
+        }
+    };
+}
+
+#endif // CSM_WOLRD_PATHGRIDPOINTSWRAP_H
diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index ef63909afa..698912344b 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -8,11 +8,16 @@
 #include <components/misc/stringops.hpp>
 #include <components/esm/loadland.hpp>
 
+#include "../../model/doc/document.hpp"
 #include "../../model/world/idtable.hpp"
+#include "../../model/world/idtree.hpp"
 #include "../../model/world/columns.hpp"
 #include "../../model/world/data.hpp"
 #include "../../model/world/refcollection.hpp"
 #include "../../model/world/pathgrid.hpp"
+#include "../../model/world/commands.hpp"
+#include "../../model/world/pathgridpointswrap.hpp"
+#include "../../model/world/nestedtableproxymodel.hpp"
 #include "../world/physicssystem.hpp"
 
 #include "elements.hpp"
@@ -92,7 +97,7 @@ bool CSVRender::Cell::addObjects (int start, int end)
 {
     bool modified = false;
 
-    const CSMWorld::RefCollection& collection = mData.getReferences();
+    const CSMWorld::RefCollection& collection = mDocument.getData().getReferences();
 
     for (int i=start; i<=end; ++i)
     {
@@ -104,7 +109,7 @@ bool CSVRender::Cell::addObjects (int start, int end)
         {
             std::string id = Misc::StringUtils::lowerCase (collection.getRecord (i).get().mId);
 
-            mObjects.insert (std::make_pair (id, new Object (mData, mCellNode, id, false, mPhysics)));
+            mObjects.insert (std::make_pair (id, new Object (mDocument.getData(), mCellNode, id, false, mPhysics)));
             modified = true;
         }
     }
@@ -112,29 +117,29 @@ bool CSVRender::Cell::addObjects (int start, int end)
     return modified;
 }
 
-CSVRender::Cell::Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager,
+CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager,
     const std::string& id, boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin)
-: mData (data), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager), mPhysics(physics), mX(0), mY(0),
-    mPathgridId("")
+: mDocument (document), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager)
+, mPhysics(physics), mX(0), mY(0), mPgIndex(-1), mModel(0), mProxyModel(0)// ,mPathgridId("")
 {
     mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
     mCellNode->setPosition (origin);
 
     CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
-        *mData.getTableModel (CSMWorld::UniversalId::Type_References));
+        *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References));
 
     int rows = references.rowCount();
 
     addObjects (0, rows-1);
 
-    const CSMWorld::IdCollection<CSMWorld::Land>& land = mData.getLand();
+    const CSMWorld::IdCollection<CSMWorld::Land>& land = mDocument.getData().getLand();
     int landIndex = land.searchId(mId);
     if (landIndex != -1)
     {
         const ESM::Land* esmLand = land.getRecord(mId).get().mLand.get();
         if(esmLand && esmLand->mDataTypes&ESM::Land::DATA_VHGT)
         {
-            mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mData), Element_Terrain, true,
+            mTerrain.reset(new Terrain::TerrainGrid(sceneManager, new TerrainStorage(mDocument.getData()), Element_Terrain, true,
                                                     Terrain::Align_XY));
             mTerrain->loadCell(esmLand->mX,
                                esmLand->mY);
@@ -174,6 +179,8 @@ CSVRender::Cell::~Cell()
         delete iter->second;
     }
 
+    delete mProxyModel;
+
     if (mTerrain.get())
         mPhysics->removeHeightField(mSceneMgr, mX, mY);
 
@@ -217,7 +224,7 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
     const QModelIndex& bottomRight)
 {
     CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
-        *mData.getTableModel (CSMWorld::UniversalId::Type_References));
+        *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References));
 
     int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);
     int cellColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Cell);
@@ -269,7 +276,7 @@ bool CSVRender::Cell::referenceDataChanged (const QModelIndex& topLeft,
     for (std::map<std::string, bool>::iterator iter (ids.begin()); iter!=ids.end(); ++iter)
     {
         mObjects.insert (std::make_pair (
-            iter->first, new Object (mData, mCellNode, iter->first, false, mPhysics)));
+            iter->first, new Object (mDocument.getData(), mCellNode, iter->first, false, mPhysics)));
 
         modified = true;
     }
@@ -284,7 +291,7 @@ bool CSVRender::Cell::referenceAboutToBeRemoved (const QModelIndex& parent, int
         return false;
 
     CSMWorld::IdTable& references = dynamic_cast<CSMWorld::IdTable&> (
-        *mData.getTableModel (CSMWorld::UniversalId::Type_References));
+        *mDocument.getData().getTableModel (CSMWorld::UniversalId::Type_References));
 
     int idColumn = references.findColumnIndex (CSMWorld::Columns::ColumnId_Id);
 
@@ -323,14 +330,22 @@ void CSVRender::Cell::loadPathgrid()
 {
     int worldsize = ESM::Land::REAL_SIZE;
 
-    CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgridCollection = mData.getPathgrids();
-    int index = pathgridCollection.searchId(mId);
+    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();
+    int index = pathgrids.searchId(mId);
     if(index != -1)
     {
-        const CSMWorld::Pathgrid &pathgrid = pathgridCollection.getRecord(index).get();
-        mPathgridId = pathgrid.mId; // FIXME: temporary storage (should be document)
+        mPgIndex = index; // keep a copy to save from searching mId all the time
+
+        int col = pathgrids.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints);
+
+        mModel = dynamic_cast<CSMWorld::IdTree *>(
+                mDocument.getData().getTableModel(CSMWorld::UniversalId::Type_Pathgrid));
+
+        mProxyModel = new CSMWorld::NestedTableProxyModel (mModel->index(mPgIndex, col),
+                CSMWorld::ColumnBase::Display_NestedHeader, mModel);
+
+        const CSMWorld::Pathgrid &pathgrid = pathgrids.getRecord(index).get();
 
-        mPoints.resize(pathgrid.mPoints.size());
         std::vector<ESM::Pathgrid::Point>::const_iterator iter = pathgrid.mPoints.begin();
         for(index = 0; iter != pathgrid.mPoints.end(); ++iter, ++index)
         {
@@ -340,7 +355,6 @@ void CSVRender::Cell::loadPathgrid()
                 Ogre::Vector3(worldsize*mX+(*iter).mX, worldsize*mY+(*iter).mY, (*iter).mZ);
 
             mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics)));
-            mPoints[index] = *iter; // FIXME: temporary storage (should be document)
         }
 
         for(ESM::Pathgrid::EdgeList::const_iterator it = pathgrid.mEdges.begin();
@@ -363,16 +377,17 @@ void CSVRender::Cell::loadPathgrid()
             node->attachObject(line);
 
             mPgEdges.insert(std::make_pair(std::make_pair(edge.mV0, edge.mV1), name));
-            mEdges.push_back(*it); // FIXME: temporary storage (should be document)
         }
     }
 }
 
 // NOTE: pos is in world coordinates
-// FIXME: save to the document
 void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos, bool interior)
 {
-    std::string name = PathgridPoint::getName(mId, mPoints.size());
+    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();
+    CSMWorld::Pathgrid pathgrid = pathgrids.getRecord(mPgIndex).get();
+
+    std::string name = PathgridPoint::getName(mId, pathgrid.mPoints.size()); // generate a new name
 
     mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics)));
 
@@ -389,12 +404,18 @@ void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos, bool interior
 
     ESM::Pathgrid::Point point(x, y, (int)pos.z);
     point.mConnectionNum = 0;
-    mPoints.push_back(point);
-    mPathgridId = mId;
+    pathgrid.mPoints.push_back(point);
     // FIXME: update other scene managers
+
+    pathgrid.mData.mS2 += 1; // increment the number of points
+
+    // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards
+    mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel,
+            mProxyModel->getParentId(), mProxyModel->getParentColumn(),
+            new CSMWorld::PathgridPointsWrap(pathgrid)));
+    // emit signal here?
 }
 
-// FIXME: save to the document
 void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
 {
     std::pair<std::string, int> result = PathgridPoint::getIdAndIndex(name);
@@ -404,18 +425,21 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
     std::string pathgridId = result.first;
     int index = result.second;
 
+    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();
+    CSMWorld::Pathgrid pathgrid = pathgrids.getRecord(mPgIndex).get();
+
     // check if the point exists
-    if(index < 0 || mPathgridId != pathgridId || (unsigned int)index >= mPoints.size())
+    if(index < 0 || (unsigned int)index >= pathgrid.mPoints.size())
         return;
 
-    int numToDelete = mPoints[index].mConnectionNum * 2; // for sanity check later
+    int numToDelete = pathgrid.mPoints[index].mConnectionNum * 2; // for sanity check later
     int edgeCount = 0;
 
     // find edges to delete
     std::vector<std::pair<int, int> > edges;
-    for(unsigned i = 0; i < mEdges.size(); ++i)
+    for(unsigned i = 0; i < pathgrid.mEdges.size(); ++i)
     {
-        if(mEdges[i].mV0 == index || mEdges[i].mV1 == index)
+        if(pathgrid.mEdges[i].mV0 == index || pathgrid.mEdges[i].mV1 == index)
         {
             for(std::map<std::pair<int, int>, std::string>::iterator iter = mPgEdges.begin();
                 iter != mPgEdges.end(); ++iter)
@@ -448,12 +472,12 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
             mPgEdges.erase(*iter);
 
             // update document
-            assert(mPoints[(*iter).first].mConnectionNum > 0);
-            mPoints[(*iter).first].mConnectionNum -= 1;
-            for(unsigned i = mEdges.size() - 1; i > 0; --i)
+            assert(pathgrid.mPoints[(*iter).first].mConnectionNum > 0);
+            pathgrid.mPoints[(*iter).first].mConnectionNum -= 1;
+            for(unsigned i = pathgrid.mEdges.size() - 1; i > 0; --i)
             {
-                if(mEdges[i].mV0 == index || mEdges[i].mV1 == index)
-                    mEdges.erase(mEdges.begin() + i);
+                if(pathgrid.mEdges[i].mV0 == index || pathgrid.mEdges[i].mV1 == index)
+                    pathgrid.mEdges.erase(pathgrid.mEdges.begin() + i);
             }
         }
     }
@@ -462,10 +486,10 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
     {
         // WARNING: continue anyway?  Or should this be an exception?
         std::cerr << "The no of edges del does not match the no of conn for: "
-            << mPathgridId + "_" + QString::number(index).toStdString() << std::endl;
+            << pathgridId + "_" + QString::number(index).toStdString() << std::endl;
     }
 
-    if(edgeCount || mPoints[index].mConnectionNum == 0)
+    if(edgeCount || pathgrid.mPoints[index].mConnectionNum == 0)
     {
         // remove the point
         delete mPgPoints[name];
@@ -477,6 +501,13 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
     //mPoints.erase(mPoints.begin() + index);  // WARNING: Can't erase because the index will change
     // FIXME: it should be possible to refresh indicies but that means index values
     // can't be stored in maps, names, etc
+
+
+
+
+    // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards
+    mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel,
+            mProxyModel->getParentId(), mProxyModel->getParentColumn(), new CSMWorld::PathgridPointsWrap(pathgrid)));
 }
 
 // NOTE: newPos is in world coordinates
@@ -490,8 +521,11 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name,
     std::string pathgridId = result.first;
     int index = result.second;
 
+    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();
+    CSMWorld::Pathgrid pathgrid = pathgrids.getRecord(mPgIndex).get();
+
     // check if the point exists
-    if(index < 0 || mPathgridId != pathgridId || (unsigned int)index >= mPoints.size())
+    if(index < 0 || (unsigned int)index >= pathgrid.mPoints.size())
         return;
 
     int worldsize = ESM::Land::REAL_SIZE;
@@ -504,17 +538,17 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name,
         y = y - (worldsize * mY);
     }
 
-    mPoints[index].mX = x;
-    mPoints[index].mY = y;
-    mPoints[index].mZ = newPos.z;
+    pathgrid.mPoints[index].mX = x;
+    pathgrid.mPoints[index].mY = y;
+    pathgrid.mPoints[index].mZ = newPos.z;
 
     // delete then recreate the edges
-    for(unsigned i = 0; i < mEdges.size(); ++i)
+    for(unsigned i = 0; i < pathgrid.mEdges.size(); ++i)
     {
-        if(mEdges[i].mV0 == index || mEdges[i].mV1 == index)
+        if(pathgrid.mEdges[i].mV0 == index || pathgrid.mEdges[i].mV1 == index)
         {
             std::ostringstream stream;
-            stream << pathgridId << "_" << mEdges[i].mV0 << " " << mEdges[i].mV1;
+            stream << pathgridId << "_" << pathgrid.mEdges[i].mV0 << " " << pathgrid.mEdges[i].mV1;
             std::string name = stream.str();
 
             if(mSceneMgr->hasManualObject(name))
@@ -524,9 +558,9 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name,
                 Ogre::SceneNode *node = manual->getParentSceneNode();
                 mSceneMgr->destroyManualObject(name);
 
-                if(mEdges[i].mV0 == index)
+                if(pathgrid.mEdges[i].mV0 == index)
                 {
-                    const ESM::Pathgrid::Point &p1 = mPoints[mEdges[i].mV1];
+                    const ESM::Pathgrid::Point &p1 = pathgrid.mPoints[pathgrid.mEdges[i].mV1];
 
                     Ogre::ManualObject *line = createPathgridEdge(name,
                         newPos,
@@ -534,9 +568,9 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name,
                     line->setVisibilityFlags(Element_Pathgrid);
                     node->attachObject(line);
                 }
-                else if(mEdges[i].mV1 == index)
+                else if(pathgrid.mEdges[i].mV1 == index)
                 {
-                    const ESM::Pathgrid::Point &p0 = mPoints[mEdges[i].mV0];
+                    const ESM::Pathgrid::Point &p0 = pathgrid.mPoints[pathgrid.mEdges[i].mV0];
 
                     Ogre::ManualObject *line = createPathgridEdge(name,
                         Ogre::Vector3(worldsize*mX+p0.mX, worldsize*mY+p0.mY, p0.mZ),
@@ -547,6 +581,11 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name,
             }
         }
     }
+
+    // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards
+    mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel,
+            mProxyModel->getParentId(), mProxyModel->getParentColumn(),
+            new CSMWorld::PathgridPointsWrap(pathgrid)));
 }
 
 // FIXME: save to the document
diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp
index 0afd0920b3..2658ff1adc 100644
--- a/apps/opencs/view/render/cell.hpp
+++ b/apps/opencs/view/render/cell.hpp
@@ -25,10 +25,17 @@ namespace Ogre
     class ManualObject;
 }
 
+namespace CSMDoc
+{
+    class Document;
+}
+
 namespace CSMWorld
 {
-    class Data;
+    //class Data;
     class Pathgrid;
+    class NestedTableProxyModel;
+    class IdTree;
 }
 
 namespace CSVWorld
@@ -42,16 +49,16 @@ namespace CSVRender
 
     class Cell
     {
-            CSMWorld::Data& mData;
+            CSMDoc::Document& mDocument;
             std::string mId;
             Ogre::SceneNode *mCellNode;
             std::map<std::string, Object *> mObjects;
             std::map<std::string, PathgridPoint *> mPgPoints;
             std::map<std::pair<int, int>, std::string> mPgEdges;
 
-            ESM::Pathgrid::PointList mPoints; // FIXME: temporary storage until saving to document
-            ESM::Pathgrid::EdgeList mEdges; // FIXME: temporary storage until saving to document
-            std::string mPathgridId; // FIXME: temporary storage until saving to document
+            CSMWorld::NestedTableProxyModel *mProxyModel;
+            CSMWorld::IdTree *mModel;
+            int mPgIndex;
 
             std::auto_ptr<Terrain::TerrainGrid> mTerrain;
             boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics;
@@ -71,7 +78,7 @@ namespace CSVRender
 
         public:
 
-            Cell (CSMWorld::Data& data, Ogre::SceneManager *sceneManager, const std::string& id,
+            Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager, const std::string& id,
                 boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin = Ogre::Vector3 (0, 0, 0));
 
             ~Cell();
diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp
index 9c42e471ed..cc00b91594 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.cpp
@@ -113,7 +113,7 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
         if (index > 0 && cells.getRecord (index).mState!=CSMWorld::RecordBase::State_Deleted &&
             mCells.find (*iter)==mCells.end())
         {
-            Cell *cell = new Cell (mDocument.getData(), getSceneManager(),
+            Cell *cell = new Cell (mDocument, getSceneManager(),
                     iter->getId (mWorldspace), mDocument.getPhysics());
             mCells.insert (std::make_pair (*iter, cell));
 
diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp
index 237bfabf50..b5e9504f33 100644
--- a/apps/opencs/view/render/unpagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp
@@ -49,7 +49,7 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string&
 
     update();
 
-    mCell.reset (new Cell (document.getData(), getSceneManager(), mCellId, document.getPhysics()));
+    mCell.reset (new Cell (document, getSceneManager(), mCellId, document.getPhysics()));
 }
 
 void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft,
@@ -91,7 +91,7 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld:
         return false;
 
     mCellId = data.begin()->getId();
-    mCell.reset (new Cell (getDocument().getData(), getSceneManager(), mCellId, getDocument().getPhysics()));
+    mCell.reset (new Cell (getDocument(), getSceneManager(), mCellId, getDocument().getPhysics()));
 
     update();
     emit cellChanged(*data.begin());

From 1a31aecc2f1eb3599922e5b1595f982847edd723 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Thu, 23 Apr 2015 22:20:45 +1000
Subject: [PATCH 25/29] Undo rendering works, but not using signals yet.

---
 apps/opencs/model/world/commands.cpp      |   9 +-
 apps/opencs/model/world/commands.hpp      |   9 +-
 apps/opencs/view/render/cell.cpp          | 264 +++++++++-------------
 apps/opencs/view/render/cell.hpp          |   8 +-
 apps/opencs/view/render/pathgridpoint.cpp |   3 -
 5 files changed, 127 insertions(+), 166 deletions(-)

diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp
index 11a675db0c..172211d7b5 100644
--- a/apps/opencs/model/world/commands.cpp
+++ b/apps/opencs/model/world/commands.cpp
@@ -6,6 +6,7 @@
 #include "idtree.hpp"
 #include <components/misc/stringops.hpp>
 #include "nestedtablewrapper.hpp"
+#include "../../view/render/cell.hpp"
 
 CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
                                         const QVariant& new_, QUndoCommand* parent)
@@ -248,8 +249,9 @@ const CSMWorld::NestedTableWrapperBase& CSMWorld::NestedTableStoring::getOld() c
 // Current interface does not allow adding a non-blank row, so we're forced to modify
 // the whole record.
 CSMWorld::ModifyPathgridCommand::ModifyPathgridCommand(IdTree& model,
-    const std::string& id, int parentColumn, NestedTableWrapperBase* newRecord, QUndoCommand* parent)
-    : mModel(model), mId(id), mParentColumn(parentColumn), mRecord(newRecord)
+    const std::string& id, int parentColumn, CSVRender::Cell *cell,
+    NestedTableWrapperBase* newRecord, QUndoCommand* parent)
+    : mModel(model), mId(id), mParentColumn(parentColumn), mRecord(newRecord), mCell(cell)
     , QUndoCommand(parent), NestedTableStoring(model, id, parentColumn)
 {
     setText (("Modify Pathgrid record " + mId).c_str()); // FIXME: better description
@@ -268,5 +270,6 @@ void CSMWorld::ModifyPathgridCommand::undo()
 
     mModel.setNestedTable(parentIndex, getOld());
 
-    // FIXME: needs to tell the cell to redraw, possibly using signals
+    mCell->clearPathgrid();
+    mCell->buildPathgrid();
 }
diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp
index 4e4de25c75..8661f987ad 100644
--- a/apps/opencs/model/world/commands.hpp
+++ b/apps/opencs/model/world/commands.hpp
@@ -17,6 +17,11 @@
 class QModelIndex;
 class QAbstractItemModel;
 
+namespace CSVRender
+{
+    class Cell;
+}
+
 namespace CSMWorld
 {
     class IdTable;
@@ -204,11 +209,13 @@ namespace CSMWorld
             int mParentColumn;
 
             NestedTableWrapperBase* mRecord;
+            CSVRender::Cell *mCell;
 
         public:
 
             // if newEdges is NULL, only the paths are updated
-            ModifyPathgridCommand(IdTree& model, const std::string& id, int parentColumn,
+            ModifyPathgridCommand(IdTree& model,
+                    const std::string& id, int parentColumn, CSVRender::Cell *cell,
                     NestedTableWrapperBase* newRecord, QUndoCommand* parent = 0);
 
             virtual void redo();
diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 698912344b..87f70b48eb 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -120,7 +120,7 @@ bool CSVRender::Cell::addObjects (int start, int end)
 CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager,
     const std::string& id, boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin)
 : mDocument (document), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager)
-, mPhysics(physics), mX(0), mY(0), mPgIndex(-1), mModel(0), mProxyModel(0)// ,mPathgridId("")
+, mPhysics(physics), mX(0), mY(0), mPgIndex(-1), mModel(0), mProxyModel(0)
 {
     mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
     mCellNode->setPosition (origin);
@@ -153,32 +153,15 @@ CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneMana
         }
     }
 
-    loadPathgrid();
+    setupPathgrid();
+    buildPathgrid();
 }
 
 CSVRender::Cell::~Cell()
 {
-    // destroy manual objects
-    for(std::map<std::pair<int, int>, std::string>::iterator iter = mPgEdges.begin();
-        iter != mPgEdges.end(); ++iter)
-    {
-        if(mSceneMgr->hasManualObject((*iter).second))
-        {
-            Ogre::ManualObject *manual = mSceneMgr->getManualObject((*iter).second);
-            Ogre::SceneNode *node = manual->getParentSceneNode();
-            mSceneMgr->destroyManualObject((*iter).second);
-            if(mSceneMgr->hasSceneNode(node->getName()))
-                mSceneMgr->destroySceneNode(node);
-        }
-    }
+    clearPathgrid();
     destroyGridMaterials();
 
-    for(std::map<std::string, PathgridPoint *>::iterator iter (mPgPoints.begin());
-        iter!=mPgPoints.end(); ++iter)
-    {
-        delete iter->second;
-    }
-
     delete mProxyModel;
 
     if (mTerrain.get())
@@ -322,62 +305,101 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
 }
 
 // FIXME:
-//  - updating indicies, including mData
 //  - adding edges (need the ability to select a pathgrid and highlight)
 //  - save to document & signals
 //  - repainting edges while moving
-void CSVRender::Cell::loadPathgrid()
+void CSVRender::Cell::setupPathgrid()
 {
-    int worldsize = ESM::Land::REAL_SIZE;
-
     const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();
     int index = pathgrids.searchId(mId);
     if(index != -1)
     {
-        mPgIndex = index; // keep a copy to save from searching mId all the time
-
         int col = pathgrids.findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints);
 
+        mPgIndex = index; // keep a copy to save from searching mId all the time
+
         mModel = dynamic_cast<CSMWorld::IdTree *>(
                 mDocument.getData().getTableModel(CSMWorld::UniversalId::Type_Pathgrid));
 
         mProxyModel = new CSMWorld::NestedTableProxyModel (mModel->index(mPgIndex, col),
                 CSMWorld::ColumnBase::Display_NestedHeader, mModel);
 
-        const CSMWorld::Pathgrid &pathgrid = pathgrids.getRecord(index).get();
+    }
+}
 
-        std::vector<ESM::Pathgrid::Point>::const_iterator iter = pathgrid.mPoints.begin();
-        for(index = 0; iter != pathgrid.mPoints.end(); ++iter, ++index)
+void CSVRender::Cell::clearPathgrid()
+{
+    // destroy manual objects (edges)
+    for(std::map<std::pair<int, int>, std::string>::iterator iter = mPgEdges.begin();
+        iter != mPgEdges.end(); ++iter)
+    {
+        if(mSceneMgr->hasManualObject((*iter).second))
         {
-            std::string name = PathgridPoint::getName(pathgrid.mId, index);
-
-            Ogre::Vector3 pos =
-                Ogre::Vector3(worldsize*mX+(*iter).mX, worldsize*mY+(*iter).mY, (*iter).mZ);
-
-            mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics)));
+            Ogre::ManualObject *manual = mSceneMgr->getManualObject((*iter).second);
+            Ogre::SceneNode *node = manual->getParentSceneNode();
+            mSceneMgr->destroyManualObject((*iter).second);
+            if(mSceneMgr->hasSceneNode(node->getName()))
+                mSceneMgr->destroySceneNode(node);
         }
+    }
+    mPgEdges.clear();
 
-        for(ESM::Pathgrid::EdgeList::const_iterator it = pathgrid.mEdges.begin();
-            it != pathgrid.mEdges.end();
-            ++it)
-        {
-            Ogre::SceneNode *node = mCellNode->createChildSceneNode();
-            const ESM::Pathgrid::Edge &edge = *it;
-            const ESM::Pathgrid::Point &p0 = pathgrid.mPoints[edge.mV0];
-            const ESM::Pathgrid::Point &p1 = pathgrid.mPoints[edge.mV1];
+    // destroy points
+    for(std::map<std::string, PathgridPoint *>::iterator iter (mPgPoints.begin());
+        iter!=mPgPoints.end(); ++iter)
+    {
+        delete iter->second;
+    }
+    mPgPoints.clear();
+}
 
-            std::ostringstream stream;
-            stream << pathgrid.mId << "_" << edge.mV0 << " " << edge.mV1;
-            std::string name = stream.str();
+// NOTE: getName() generates a string representation of mId+index to uniquely identify a
+// pathgrid point.  The trouble is that the index can change when a pathgrid point is deleted.
+// Need a new way of uniquely identifying a pathgrid point.
+//
+// A workaround is to re-generate the pathgrids and edges each time a point is deleted or
+// undo() is called (probably via a signal)
+void CSVRender::Cell::buildPathgrid()
+{
+    if (!mModel)
+        return;
 
-            Ogre::ManualObject *line = createPathgridEdge(name,
-                Ogre::Vector3(worldsize*mX+p0.mX, worldsize*mY+p0.mY, p0.mZ),
-                Ogre::Vector3(worldsize*mX+p1.mX, worldsize*mY+p1.mY, p1.mZ));
-            line->setVisibilityFlags(Element_Pathgrid);
-            node->attachObject(line);
+    const CSMWorld::SubCellCollection<CSMWorld::Pathgrid>& pathgrids = mDocument.getData().getPathgrids();
+    const CSMWorld::Pathgrid &pathgrid = pathgrids.getRecord(mPgIndex).get();
 
-            mPgEdges.insert(std::make_pair(std::make_pair(edge.mV0, edge.mV1), name));
-        }
+    int worldsize = ESM::Land::REAL_SIZE;
+
+    std::vector<ESM::Pathgrid::Point>::const_iterator iter = pathgrid.mPoints.begin();
+    for(int index = 0; iter != pathgrid.mPoints.end(); ++iter, ++index)
+    {
+        std::string name = PathgridPoint::getName(pathgrid.mId, index);
+
+        Ogre::Vector3 pos =
+            Ogre::Vector3(worldsize*mX+(*iter).mX, worldsize*mY+(*iter).mY, (*iter).mZ);
+
+        mPgPoints.insert(std::make_pair(name, new PathgridPoint(name, mCellNode, pos, mPhysics)));
+    }
+
+    for(ESM::Pathgrid::EdgeList::const_iterator it = pathgrid.mEdges.begin();
+        it != pathgrid.mEdges.end();
+        ++it)
+    {
+        Ogre::SceneNode *node = mCellNode->createChildSceneNode();
+        const ESM::Pathgrid::Edge &edge = *it;
+        const ESM::Pathgrid::Point &p0 = pathgrid.mPoints[edge.mV0];
+        const ESM::Pathgrid::Point &p1 = pathgrid.mPoints[edge.mV1];
+
+        std::ostringstream stream;
+        stream << pathgrid.mId << "_" << edge.mV0 << " " << edge.mV1;
+        std::string name = stream.str();
+
+        Ogre::ManualObject *line = createPathgridEdge(name,
+            Ogre::Vector3(worldsize*mX+p0.mX, worldsize*mY+p0.mY, p0.mZ),
+            Ogre::Vector3(worldsize*mX+p1.mX, worldsize*mY+p1.mY, p1.mZ));
+        line->setVisibilityFlags(Element_Pathgrid);
+        node->attachObject(line);
+
+        mPgEdges.insert(std::make_pair(std::make_pair(edge.mV0, edge.mV1), name));
     }
 }
 
@@ -411,7 +433,7 @@ void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos, bool interior
 
     // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards
     mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel,
-            mProxyModel->getParentId(), mProxyModel->getParentColumn(),
+            mProxyModel->getParentId(), mProxyModel->getParentColumn(), this,
             new CSMWorld::PathgridPointsWrap(pathgrid)));
     // emit signal here?
 }
@@ -433,81 +455,46 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
         return;
 
     int numToDelete = pathgrid.mPoints[index].mConnectionNum * 2; // for sanity check later
-    int edgeCount = 0;
+    int deletedEdgeCount = 0;
 
-    // find edges to delete
-    std::vector<std::pair<int, int> > edges;
-    for(unsigned i = 0; i < pathgrid.mEdges.size(); ++i)
+    // update edge indicies to account for the deleted pathgrid point
+    std::vector<ESM::Pathgrid::Edge>::iterator iter = pathgrid.mEdges.begin();
+    for (; iter != pathgrid.mEdges.end();)
     {
-        if(pathgrid.mEdges[i].mV0 == index || pathgrid.mEdges[i].mV1 == index)
+        if (((*iter).mV0 == index) || ((*iter).mV1 == index))
         {
-            for(std::map<std::pair<int, int>, std::string>::iterator iter = mPgEdges.begin();
-                iter != mPgEdges.end(); ++iter)
-            {
-                if((*iter).first.first == index || (*iter).first.second == index)
-                {
-                    edges.push_back(std::make_pair((*iter).first.first, (*iter).first.second));
-                }
-            }
+            iter = pathgrid.mEdges.erase(iter);
+            pathgrid.mPoints[index].mConnectionNum -= 1;
+            deletedEdgeCount++; // for sanity check later
+        }
+        else
+        {
+            if ((*iter).mV0 > index)
+                (*iter).mV0--;
+
+            if ((*iter).mV1 > index)
+                (*iter).mV1--;
+
+            ++iter;
         }
     }
+    pathgrid.mPoints.erase(pathgrid.mPoints.begin()+index);
+    pathgrid.mData.mS2 -= 1; // decrement the number of points
 
-    // delete the edges
-    for(std::vector<std::pair<int, int> >::iterator iter = edges.begin();
-        iter != edges.end(); ++iter)
-    {
-        std::string name = mPgEdges[*iter];
-        if(mSceneMgr->hasManualObject(name))
-        {
-            // remove manual objects
-            Ogre::ManualObject *manual = mSceneMgr->getManualObject(name);
-            Ogre::SceneNode *node = manual->getParentSceneNode();
-            mSceneMgr->destroyManualObject(name);
-            if(mSceneMgr->hasSceneNode(node->getName()))
-                mSceneMgr->destroySceneNode(node);
-
-            edgeCount++; // for sanity check later
-
-            // update map
-            mPgEdges.erase(*iter);
-
-            // update document
-            assert(pathgrid.mPoints[(*iter).first].mConnectionNum > 0);
-            pathgrid.mPoints[(*iter).first].mConnectionNum -= 1;
-            for(unsigned i = pathgrid.mEdges.size() - 1; i > 0; --i)
-            {
-                if(pathgrid.mEdges[i].mV0 == index || pathgrid.mEdges[i].mV1 == index)
-                    pathgrid.mEdges.erase(pathgrid.mEdges.begin() + i);
-            }
-        }
-    }
-
-    if(edgeCount != numToDelete)
+    if(deletedEdgeCount != numToDelete)
     {
         // WARNING: continue anyway?  Or should this be an exception?
         std::cerr << "The no of edges del does not match the no of conn for: "
             << pathgridId + "_" + QString::number(index).toStdString() << std::endl;
     }
 
-    if(edgeCount || pathgrid.mPoints[index].mConnectionNum == 0)
-    {
-        // remove the point
-        delete mPgPoints[name];
-        mPgPoints.erase(name);
-        // FIXME: update other scene managers
-    }
-
-    // store to document
-    //mPoints.erase(mPoints.begin() + index);  // WARNING: Can't erase because the index will change
-    // FIXME: it should be possible to refresh indicies but that means index values
-    // can't be stored in maps, names, etc
-
-
-
-
     // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards
     mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel,
-            mProxyModel->getParentId(), mProxyModel->getParentColumn(), new CSMWorld::PathgridPointsWrap(pathgrid)));
+            mProxyModel->getParentId(), mProxyModel->getParentColumn(), this,
+            new CSMWorld::PathgridPointsWrap(pathgrid)));
+
+    clearPathgrid();
+    buildPathgrid();
 }
 
 // NOTE: newPos is in world coordinates
@@ -542,50 +529,13 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name,
     pathgrid.mPoints[index].mY = y;
     pathgrid.mPoints[index].mZ = newPos.z;
 
-    // delete then recreate the edges
-    for(unsigned i = 0; i < pathgrid.mEdges.size(); ++i)
-    {
-        if(pathgrid.mEdges[i].mV0 == index || pathgrid.mEdges[i].mV1 == index)
-        {
-            std::ostringstream stream;
-            stream << pathgridId << "_" << pathgrid.mEdges[i].mV0 << " " << pathgrid.mEdges[i].mV1;
-            std::string name = stream.str();
-
-            if(mSceneMgr->hasManualObject(name))
-            {
-                // remove manual objects
-                Ogre::ManualObject *manual = mSceneMgr->getManualObject(name);
-                Ogre::SceneNode *node = manual->getParentSceneNode();
-                mSceneMgr->destroyManualObject(name);
-
-                if(pathgrid.mEdges[i].mV0 == index)
-                {
-                    const ESM::Pathgrid::Point &p1 = pathgrid.mPoints[pathgrid.mEdges[i].mV1];
-
-                    Ogre::ManualObject *line = createPathgridEdge(name,
-                        newPos,
-                        Ogre::Vector3(worldsize*mX+p1.mX, worldsize*mY+p1.mY, p1.mZ));
-                    line->setVisibilityFlags(Element_Pathgrid);
-                    node->attachObject(line);
-                }
-                else if(pathgrid.mEdges[i].mV1 == index)
-                {
-                    const ESM::Pathgrid::Point &p0 = pathgrid.mPoints[pathgrid.mEdges[i].mV0];
-
-                    Ogre::ManualObject *line = createPathgridEdge(name,
-                        Ogre::Vector3(worldsize*mX+p0.mX, worldsize*mY+p0.mY, p0.mZ),
-                        newPos);
-                    line->setVisibilityFlags(Element_Pathgrid);
-                    node->attachObject(line);
-                }
-            }
-        }
-    }
-
     // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards
     mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel,
-            mProxyModel->getParentId(), mProxyModel->getParentColumn(),
+            mProxyModel->getParentId(), mProxyModel->getParentColumn(), this,
             new CSMWorld::PathgridPointsWrap(pathgrid)));
+
+    clearPathgrid();
+    buildPathgrid();
 }
 
 // FIXME: save to the document
diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp
index 2658ff1adc..fac4ded59e 100644
--- a/apps/opencs/view/render/cell.hpp
+++ b/apps/opencs/view/render/cell.hpp
@@ -11,7 +11,6 @@
 
 #ifndef Q_MOC_RUN
 #include <components/terrain/terraingrid.hpp>
-#include <components/esm/loadpgrd.hpp>  // FIXME: temporaty storage until saving to document
 #endif
 
 #include "object.hpp"
@@ -116,12 +115,17 @@ namespace CSVRender
             // for drawing pathgrid points & lines
             void createGridMaterials();
             void destroyGridMaterials();
-            void loadPathgrid();
+            void setupPathgrid();
             Ogre::ManualObject *createPathgridEdge(const std::string &name,
                     const Ogre::Vector3 &start, const Ogre::Vector3 &end);
 
             void addPathgridEdge();
             void removePathgridEdge();
+
+        public:
+
+            void clearPathgrid();
+            void buildPathgrid();
     };
 }
 
diff --git a/apps/opencs/view/render/pathgridpoint.cpp b/apps/opencs/view/render/pathgridpoint.cpp
index 85b03beea9..ea6815fbe8 100644
--- a/apps/opencs/view/render/pathgridpoint.cpp
+++ b/apps/opencs/view/render/pathgridpoint.cpp
@@ -1,13 +1,10 @@
 #include "pathgridpoint.hpp"
 
-#include <iostream> // FIXME
-
 #include <QRegExp>
 
 #include <OgreSceneManager.h>
 #include <OgreSceneNode.h>
 
-//#include "../../model/world/pathgrid.hpp"
 #include "../world/physicssystem.hpp"
 
 #include "elements.hpp"

From e7bd29873922d8788422e3da83cd3a668feeb0c3 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Fri, 24 Apr 2015 01:28:49 +1000
Subject: [PATCH 26/29] Undo now working via signals.

---
 apps/opencs/CMakeLists.txt                    |  3 +-
 apps/opencs/model/world/commands.cpp          | 29 ---------
 apps/opencs/model/world/commands.hpp          | 29 +--------
 apps/opencs/model/world/pathgridcommands.cpp  | 48 ++++++++++++++
 apps/opencs/model/world/pathgridcommands.hpp  | 65 +++++++++++++++++++
 apps/opencs/view/render/cell.cpp              | 42 ++++++++----
 apps/opencs/view/render/cell.hpp              |  4 +-
 .../view/render/pagedworldspacewidget.cpp     |  7 ++
 .../view/render/pagedworldspacewidget.hpp     |  1 +
 .../view/render/unpagedworldspacewidget.cpp   | 14 +++-
 .../view/render/unpagedworldspacewidget.hpp   |  2 +
 11 files changed, 169 insertions(+), 75 deletions(-)
 create mode 100644 apps/opencs/model/world/pathgridcommands.cpp
 create mode 100644 apps/opencs/model/world/pathgridcommands.hpp

diff --git a/apps/opencs/CMakeLists.txt b/apps/opencs/CMakeLists.txt
index 5822b433ab..35d5e5d3cb 100644
--- a/apps/opencs/CMakeLists.txt
+++ b/apps/opencs/CMakeLists.txt
@@ -18,7 +18,8 @@ opencs_hdrs_noqt (model/doc
 
 
 opencs_units (model/world
-    idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable nestedtableproxymodel idtree
+    idtable idtableproxymodel regionmap data commanddispatcher idtablebase resourcetable
+    nestedtableproxymodel idtree pathgridcommands
     )
 
 
diff --git a/apps/opencs/model/world/commands.cpp b/apps/opencs/model/world/commands.cpp
index 172211d7b5..d12c5d228f 100644
--- a/apps/opencs/model/world/commands.cpp
+++ b/apps/opencs/model/world/commands.cpp
@@ -6,7 +6,6 @@
 #include "idtree.hpp"
 #include <components/misc/stringops.hpp>
 #include "nestedtablewrapper.hpp"
-#include "../../view/render/cell.hpp"
 
 CSMWorld::ModifyCommand::ModifyCommand (QAbstractItemModel& model, const QModelIndex& index,
                                         const QVariant& new_, QUndoCommand* parent)
@@ -245,31 +244,3 @@ const CSMWorld::NestedTableWrapperBase& CSMWorld::NestedTableStoring::getOld() c
 {
     return *mOld;
 }
-
-// Current interface does not allow adding a non-blank row, so we're forced to modify
-// the whole record.
-CSMWorld::ModifyPathgridCommand::ModifyPathgridCommand(IdTree& model,
-    const std::string& id, int parentColumn, CSVRender::Cell *cell,
-    NestedTableWrapperBase* newRecord, QUndoCommand* parent)
-    : mModel(model), mId(id), mParentColumn(parentColumn), mRecord(newRecord), mCell(cell)
-    , QUndoCommand(parent), NestedTableStoring(model, id, parentColumn)
-{
-    setText (("Modify Pathgrid record " + mId).c_str()); // FIXME: better description
-}
-
-void CSMWorld::ModifyPathgridCommand::redo()
-{
-    const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
-
-    mModel.setNestedTable(parentIndex, *mRecord);
-}
-
-void CSMWorld::ModifyPathgridCommand::undo()
-{
-    const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
-
-    mModel.setNestedTable(parentIndex, getOld());
-
-    mCell->clearPathgrid();
-    mCell->buildPathgrid();
-}
diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp
index 8661f987ad..0cd8a00f8f 100644
--- a/apps/opencs/model/world/commands.hpp
+++ b/apps/opencs/model/world/commands.hpp
@@ -12,16 +12,11 @@
 #include <QModelIndex>
 
 #include "universalid.hpp"
-#include "nestedtablewrapper.hpp"
+//#include "nestedtablewrapper.hpp"
 
 class QModelIndex;
 class QAbstractItemModel;
 
-namespace CSVRender
-{
-    class Cell;
-}
-
 namespace CSMWorld
 {
     class IdTable;
@@ -200,28 +195,6 @@ namespace CSMWorld
 
             virtual void undo();
     };
-
-    class ModifyPathgridCommand : public QUndoCommand, private NestedTableStoring
-    {
-            IdTree& mModel;
-            std::string mId;
-
-            int mParentColumn;
-
-            NestedTableWrapperBase* mRecord;
-            CSVRender::Cell *mCell;
-
-        public:
-
-            // if newEdges is NULL, only the paths are updated
-            ModifyPathgridCommand(IdTree& model,
-                    const std::string& id, int parentColumn, CSVRender::Cell *cell,
-                    NestedTableWrapperBase* newRecord, QUndoCommand* parent = 0);
-
-            virtual void redo();
-
-            virtual void undo();
-    };
 }
 
 #endif
diff --git a/apps/opencs/model/world/pathgridcommands.cpp b/apps/opencs/model/world/pathgridcommands.cpp
new file mode 100644
index 0000000000..356806dcdb
--- /dev/null
+++ b/apps/opencs/model/world/pathgridcommands.cpp
@@ -0,0 +1,48 @@
+#include "pathgridcommands.hpp"
+
+#include "../../view/render/cell.hpp"
+#include "idtree.hpp"
+#include "nestedtablewrapper.hpp"
+
+// Current interface does not allow adding a non-blank row, so we're forced to modify
+// the whole record.
+CSMWorld::ModifyPathgridCommand::ModifyPathgridCommand(IdTree& model,
+    const std::string& id, int parentColumn, NestedTableWrapperBase* newRecord, QUndoCommand* parent)
+    : mModel(model), mId(id), mParentColumn(parentColumn), mRecord(newRecord)
+    , QUndoCommand(parent), NestedTableStoring(model, id, parentColumn)
+{
+    setText (("Modify Pathgrid record " + mId).c_str()); // FIXME: better description
+}
+
+void CSMWorld::ModifyPathgridCommand::redo()
+{
+    const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
+
+    mModel.setNestedTable(parentIndex, *mRecord);
+}
+
+void CSMWorld::ModifyPathgridCommand::undo()
+{
+    const QModelIndex& parentIndex = mModel.getModelIndex(mId, mParentColumn);
+
+    mModel.setNestedTable(parentIndex, getOld());
+
+    emit undoActioned();
+}
+
+void CSMWorld::SignalHandler::rebuildPathgrid()
+{
+    mParent->clearPathgrid();
+    mParent->buildPathgrid();
+
+    emit flagAsModified();
+}
+
+CSMWorld::SignalHandler::SignalHandler (CSVRender::Cell *parent) : mParent(parent)
+{}
+
+void CSMWorld::SignalHandler::connectToCommand(const CSMWorld::ModifyPathgridCommand *command)
+{
+    connect (command, SIGNAL(undoActioned()),
+            this, SLOT(rebuildPathgrid()));
+}
diff --git a/apps/opencs/model/world/pathgridcommands.hpp b/apps/opencs/model/world/pathgridcommands.hpp
new file mode 100644
index 0000000000..335e3a8933
--- /dev/null
+++ b/apps/opencs/model/world/pathgridcommands.hpp
@@ -0,0 +1,65 @@
+#ifndef CSM_WOLRD_PATHGRIDCOMMANDS_H
+#define CSM_WOLRD_PATHGRIDCOMMANDS_H
+
+#include <QObject>
+
+#include "commands.hpp"
+
+namespace CSVRender
+{
+    class Cell;
+}
+
+namespace CSMWorld
+{
+    class IdTree;
+    class NestedTableWrapperBase;
+
+    class ModifyPathgridCommand : public QObject, public QUndoCommand, private NestedTableStoring
+    {
+        Q_OBJECT
+
+            IdTree& mModel;
+            std::string mId;
+
+            int mParentColumn;
+
+            NestedTableWrapperBase* mRecord;
+
+        public:
+
+            ModifyPathgridCommand(IdTree& model,
+                    const std::string& id, int parentColumn, NestedTableWrapperBase* newRecord,
+                    QUndoCommand* parent = 0);
+
+            virtual void redo();
+
+            virtual void undo();
+
+        signals:
+
+            void undoActioned();
+    };
+
+    class SignalHandler : public QObject
+    {
+        Q_OBJECT
+
+            CSVRender::Cell *mParent;
+
+        public:
+
+            SignalHandler (CSVRender::Cell *parent);
+
+            void connectToCommand(const ModifyPathgridCommand *command);
+
+        private slots:
+
+            void rebuildPathgrid();
+
+        signals:
+
+            void flagAsModified();
+    };
+}
+#endif // CSM_WOLRD_PATHGRIDCOMMANDS_H
diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 87f70b48eb..7976fd55e9 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -16,6 +16,7 @@
 #include "../../model/world/refcollection.hpp"
 #include "../../model/world/pathgrid.hpp"
 #include "../../model/world/commands.hpp"
+#include "../../model/world/pathgridcommands.hpp"
 #include "../../model/world/pathgridpointswrap.hpp"
 #include "../../model/world/nestedtableproxymodel.hpp"
 #include "../world/physicssystem.hpp"
@@ -119,8 +120,9 @@ bool CSVRender::Cell::addObjects (int start, int end)
 
 CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager,
     const std::string& id, boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin)
-: mDocument (document), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager)
-, mPhysics(physics), mX(0), mY(0), mPgIndex(-1), mModel(0), mProxyModel(0)
+    : mDocument (document), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager)
+    , mPhysics(physics), mX(0), mY(0), mPgIndex(-1), mModel(0), mProxyModel(0)
+    , mHandler(new CSMWorld::SignalHandler(this))
 {
     mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
     mCellNode->setPosition (origin);
@@ -163,6 +165,7 @@ CSVRender::Cell::~Cell()
     destroyGridMaterials();
 
     delete mProxyModel;
+    delete mHandler;
 
     if (mTerrain.get())
         mPhysics->removeHeightField(mSceneMgr, mX, mY);
@@ -431,10 +434,12 @@ void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos, bool interior
 
     pathgrid.mData.mS2 += 1; // increment the number of points
 
-    // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards
-    mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel,
-            mProxyModel->getParentId(), mProxyModel->getParentColumn(), this,
-            new CSMWorld::PathgridPointsWrap(pathgrid)));
+    // FIXME: possible issue if this cell is deleted and undo() is actioned afterwards
+    CSMWorld::ModifyPathgridCommand *cmd = new CSMWorld::ModifyPathgridCommand(*mModel,
+            mProxyModel->getParentId(), mProxyModel->getParentColumn(),
+            new CSMWorld::PathgridPointsWrap(pathgrid));
+    mHandler->connectToCommand(cmd);
+    mDocument.getUndoStack().push(cmd);
     // emit signal here?
 }
 
@@ -488,10 +493,12 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
             << pathgridId + "_" + QString::number(index).toStdString() << std::endl;
     }
 
-    // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards
-    mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel,
-            mProxyModel->getParentId(), mProxyModel->getParentColumn(), this,
-            new CSMWorld::PathgridPointsWrap(pathgrid)));
+    // FIXME: possible issue if this cell is deleted and undo() is actioned afterwards
+    CSMWorld::ModifyPathgridCommand *cmd = new CSMWorld::ModifyPathgridCommand(*mModel,
+            mProxyModel->getParentId(), mProxyModel->getParentColumn(),
+            new CSMWorld::PathgridPointsWrap(pathgrid));
+    mHandler->connectToCommand(cmd);
+    mDocument.getUndoStack().push(cmd);
 
     clearPathgrid();
     buildPathgrid();
@@ -529,10 +536,12 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name,
     pathgrid.mPoints[index].mY = y;
     pathgrid.mPoints[index].mZ = newPos.z;
 
-    // FIXME: probably will crash if this cell is deleted and undo() is actioned afterwards
-    mDocument.getUndoStack().push(new CSMWorld::ModifyPathgridCommand(*mModel,
-            mProxyModel->getParentId(), mProxyModel->getParentColumn(), this,
-            new CSMWorld::PathgridPointsWrap(pathgrid)));
+    // FIXME: possible issue if this cell is deleted and undo() is actioned afterwards
+    CSMWorld::ModifyPathgridCommand *cmd = new CSMWorld::ModifyPathgridCommand(*mModel,
+            mProxyModel->getParentId(), mProxyModel->getParentColumn(),
+            new CSMWorld::PathgridPointsWrap(pathgrid));
+    mHandler->connectToCommand(cmd);
+    mDocument.getUndoStack().push(cmd);
 
     clearPathgrid();
     buildPathgrid();
@@ -551,3 +560,8 @@ void CSVRender::Cell::addPathgridEdge()
 void CSVRender::Cell::removePathgridEdge()
 {
 }
+
+CSMWorld::SignalHandler *CSVRender::Cell::getSignalHandler()
+{
+    return mHandler;
+}
diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp
index fac4ded59e..cc00ee1c8b 100644
--- a/apps/opencs/view/render/cell.hpp
+++ b/apps/opencs/view/render/cell.hpp
@@ -31,10 +31,10 @@ namespace CSMDoc
 
 namespace CSMWorld
 {
-    //class Data;
     class Pathgrid;
     class NestedTableProxyModel;
     class IdTree;
+    class SignalHandler;
 }
 
 namespace CSVWorld
@@ -58,6 +58,7 @@ namespace CSVRender
             CSMWorld::NestedTableProxyModel *mProxyModel;
             CSMWorld::IdTree *mModel;
             int mPgIndex;
+            CSMWorld::SignalHandler *mHandler;
 
             std::auto_ptr<Terrain::TerrainGrid> mTerrain;
             boost::shared_ptr<CSVWorld::PhysicsSystem> mPhysics;
@@ -126,6 +127,7 @@ namespace CSVRender
 
             void clearPathgrid();
             void buildPathgrid();
+            CSMWorld::SignalHandler *getSignalHandler();
     };
 }
 
diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp
index cc00b91594..130f5c4096 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.cpp
@@ -19,6 +19,7 @@
 
 #include "../../model/world/tablemimedata.hpp"
 #include "../../model/world/idtable.hpp"
+#include "../../model/world/pathgridcommands.hpp"
 
 #include "../widget/scenetooltoggle.hpp"
 #include "../widget/scenetoolmode.hpp"
@@ -115,6 +116,7 @@ bool CSVRender::PagedWorldspaceWidget::adjustCells()
         {
             Cell *cell = new Cell (mDocument, getSceneManager(),
                     iter->getId (mWorldspace), mDocument.getPhysics());
+            connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot()));
             mCells.insert (std::make_pair (*iter, cell));
 
             float height = cell->getTerrainHeightAt(Ogre::Vector3(
@@ -637,3 +639,8 @@ void CSVRender::PagedWorldspaceWidget::cellAdded (const QModelIndex& index, int
     if (adjustCells())
         flagAsModified();
 }
+
+void CSVRender::PagedWorldspaceWidget::flagAsModSlot ()
+{
+    flagAsModified();
+}
diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp
index 45e5dd8f7e..099b4db256 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.hpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.hpp
@@ -120,6 +120,7 @@ namespace CSVRender
 
             virtual void cellAdded (const QModelIndex& index, int start, int end);
 
+            virtual void flagAsModSlot();
     };
 }
 
diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp
index b5e9504f33..c95a87d038 100644
--- a/apps/opencs/view/render/unpagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp
@@ -13,6 +13,7 @@
 #include "../../model/world/data.hpp"
 #include "../../model/world/idtable.hpp"
 #include "../../model/world/tablemimedata.hpp"
+#include "../../model/world/pathgridcommands.hpp"
 
 #include "../widget/scenetooltoggle.hpp"
 #include "../widget/scenetooltoggle2.hpp"
@@ -49,7 +50,9 @@ CSVRender::UnpagedWorldspaceWidget::UnpagedWorldspaceWidget (const std::string&
 
     update();
 
-    mCell.reset (new Cell (document, getSceneManager(), mCellId, document.getPhysics()));
+    Cell *cell = new Cell (document, getSceneManager(), mCellId, document.getPhysics());
+    connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot()));
+    mCell.reset (cell);
 }
 
 void CSVRender::UnpagedWorldspaceWidget::cellDataChanged (const QModelIndex& topLeft,
@@ -91,7 +94,9 @@ bool CSVRender::UnpagedWorldspaceWidget::handleDrop (const std::vector<CSMWorld:
         return false;
 
     mCellId = data.begin()->getId();
-    mCell.reset (new Cell (getDocument(), getSceneManager(), mCellId, getDocument().getPhysics()));
+    Cell *cell = new Cell (getDocument(), getSceneManager(), mCellId, getDocument().getPhysics());
+    connect (cell->getSignalHandler(), SIGNAL(flagAsModified()), this, SLOT(flagAsModSlot()));
+    mCell.reset (cell);
 
     update();
     emit cellChanged(*data.begin());
@@ -212,3 +217,8 @@ CSVRender::WorldspaceWidget::dropRequirments CSVRender::UnpagedWorldspaceWidget:
             return ignored;
     }
 }
+
+void CSVRender::UnpagedWorldspaceWidget::flagAsModSlot ()
+{
+    flagAsModified();
+}
diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp
index 11bfaeca30..468c48739b 100644
--- a/apps/opencs/view/render/unpagedworldspacewidget.hpp
+++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp
@@ -76,6 +76,8 @@ namespace CSVRender
 
             void cellRowsAboutToBeRemoved (const QModelIndex& parent, int start, int end);
 
+            virtual void flagAsModSlot();
+
         signals:
 
             void cellChanged(const CSMWorld::UniversalId& id);

From 3102a175229982ae5db914fe327718776d3732aa Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Fri, 24 Apr 2015 04:00:49 +1000
Subject: [PATCH 27/29] Changes made to DialogueSubview pathgrid tables are now
 reflected on PagedWorldspaceWidget.

---
 apps/opencs/model/world/commands.hpp          |  1 -
 apps/opencs/model/world/pathgridcommands.cpp  |  4 +--
 apps/opencs/model/world/pathgridcommands.hpp  |  2 +-
 apps/opencs/view/render/cell.cpp              | 33 ++++++++++++++++---
 apps/opencs/view/render/cell.hpp              |  2 ++
 .../view/render/pagedworldspacewidget.cpp     | 24 ++++----------
 .../view/render/pagedworldspacewidget.hpp     |  9 ++---
 .../view/render/unpagedworldspacewidget.cpp   | 22 +++----------
 .../view/render/unpagedworldspacewidget.hpp   |  8 ++---
 apps/opencs/view/render/worldspacewidget.cpp  | 15 +++------
 apps/opencs/view/render/worldspacewidget.hpp  |  6 +---
 11 files changed, 55 insertions(+), 71 deletions(-)

diff --git a/apps/opencs/model/world/commands.hpp b/apps/opencs/model/world/commands.hpp
index 02d0424873..2d63b66c97 100644
--- a/apps/opencs/model/world/commands.hpp
+++ b/apps/opencs/model/world/commands.hpp
@@ -12,7 +12,6 @@
 #include <QModelIndex>
 
 #include "universalid.hpp"
-//#include "nestedtablewrapper.hpp"
 
 class QModelIndex;
 class QAbstractItemModel;
diff --git a/apps/opencs/model/world/pathgridcommands.cpp b/apps/opencs/model/world/pathgridcommands.cpp
index 356806dcdb..193ab44571 100644
--- a/apps/opencs/model/world/pathgridcommands.cpp
+++ b/apps/opencs/model/world/pathgridcommands.cpp
@@ -1,6 +1,7 @@
 #include "pathgridcommands.hpp"
 
 #include "../../view/render/cell.hpp"
+
 #include "idtree.hpp"
 #include "nestedtablewrapper.hpp"
 
@@ -43,6 +44,5 @@ CSMWorld::SignalHandler::SignalHandler (CSVRender::Cell *parent) : mParent(paren
 
 void CSMWorld::SignalHandler::connectToCommand(const CSMWorld::ModifyPathgridCommand *command)
 {
-    connect (command, SIGNAL(undoActioned()),
-            this, SLOT(rebuildPathgrid()));
+    connect (command, SIGNAL(undoActioned()), this, SLOT(rebuildPathgrid()));
 }
diff --git a/apps/opencs/model/world/pathgridcommands.hpp b/apps/opencs/model/world/pathgridcommands.hpp
index 335e3a8933..3acf0865bf 100644
--- a/apps/opencs/model/world/pathgridcommands.hpp
+++ b/apps/opencs/model/world/pathgridcommands.hpp
@@ -53,7 +53,7 @@ namespace CSMWorld
 
             void connectToCommand(const ModifyPathgridCommand *command);
 
-        private slots:
+        public slots:
 
             void rebuildPathgrid();
 
diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index 7976fd55e9..f7ec24a56d 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -307,9 +307,34 @@ float CSVRender::Cell::getTerrainHeightAt(const Ogre::Vector3 &pos) const
         return -std::numeric_limits<float>::max();
 }
 
+void CSVRender::Cell::pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight)
+{
+    CSMWorld::IdTree *pathgrids = dynamic_cast<CSMWorld::IdTree *>(
+                mDocument.getData().getTableModel(CSMWorld::UniversalId::Type_Pathgrid));
+
+    int idColumn = pathgrids->findColumnIndex(CSMWorld::Columns::ColumnId_Id);
+    int colPaths = pathgrids->findColumnIndex(CSMWorld::Columns::ColumnId_PathgridPoints);
+    //int colEdges = pathgrids->findColumnIndex(CSMWorld::Columns::ColumnId_PathgridEdges);
+
+    // FIXME: how to detect adds/deletes/modifies?
+
+    for (int i=topLeft.row(); i<=bottomRight.row(); ++i)
+    {
+        std::string cell = Misc::StringUtils::lowerCase (pathgrids->data (
+            pathgrids->index (i, idColumn)).toString().toUtf8().constData());
+
+        if (cell==mId && colPaths >= topLeft.column() && colPaths <= bottomRight.column())
+        {
+            if (!mModel)
+                setupPathgrid();
+
+            mHandler->rebuildPathgrid();
+        }
+    }
+}
+
 // FIXME:
 //  - adding edges (need the ability to select a pathgrid and highlight)
-//  - save to document & signals
 //  - repainting edges while moving
 void CSVRender::Cell::setupPathgrid()
 {
@@ -434,7 +459,7 @@ void CSVRender::Cell::pathgridPointAdded(const Ogre::Vector3 &pos, bool interior
 
     pathgrid.mData.mS2 += 1; // increment the number of points
 
-    // FIXME: possible issue if this cell is deleted and undo() is actioned afterwards
+    // TODO: check for possible issue if this cell is deleted and undo() is actioned afterwards
     CSMWorld::ModifyPathgridCommand *cmd = new CSMWorld::ModifyPathgridCommand(*mModel,
             mProxyModel->getParentId(), mProxyModel->getParentColumn(),
             new CSMWorld::PathgridPointsWrap(pathgrid));
@@ -493,7 +518,7 @@ void CSVRender::Cell::pathgridPointRemoved(const std::string &name)
             << pathgridId + "_" + QString::number(index).toStdString() << std::endl;
     }
 
-    // FIXME: possible issue if this cell is deleted and undo() is actioned afterwards
+    // TODO: check for possible issue if this cell is deleted and undo() is actioned afterwards
     CSMWorld::ModifyPathgridCommand *cmd = new CSMWorld::ModifyPathgridCommand(*mModel,
             mProxyModel->getParentId(), mProxyModel->getParentColumn(),
             new CSMWorld::PathgridPointsWrap(pathgrid));
@@ -536,7 +561,7 @@ void CSVRender::Cell::pathgridPointMoved(const std::string &name,
     pathgrid.mPoints[index].mY = y;
     pathgrid.mPoints[index].mZ = newPos.z;
 
-    // FIXME: possible issue if this cell is deleted and undo() is actioned afterwards
+    // TODO: check for possible issue if this cell is deleted and undo() is actioned afterwards
     CSMWorld::ModifyPathgridCommand *cmd = new CSMWorld::ModifyPathgridCommand(*mModel,
             mProxyModel->getParentId(), mProxyModel->getParentColumn(),
             new CSMWorld::PathgridPointsWrap(pathgrid));
diff --git a/apps/opencs/view/render/cell.hpp b/apps/opencs/view/render/cell.hpp
index cc00ee1c8b..dbf680dd4f 100644
--- a/apps/opencs/view/render/cell.hpp
+++ b/apps/opencs/view/render/cell.hpp
@@ -111,6 +111,8 @@ namespace CSVRender
                     const Ogre::Vector3 &newPos, bool interior = false);
             void pathgridPointRemoved(const std::string &name);
 
+            void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
+
         private:
 
             // for drawing pathgrid points & lines
diff --git a/apps/opencs/view/render/pagedworldspacewidget.cpp b/apps/opencs/view/render/pagedworldspacewidget.cpp
index 130f5c4096..799dd1e7fe 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.cpp
@@ -330,23 +330,13 @@ void CSVRender::PagedWorldspaceWidget::referenceAdded (const QModelIndex& parent
             flagAsModified();
 }
 
-//void CSVRender::PagedWorldspaceWidget::pathgridAdded (const QModelIndex& parent,
-//    int start, int end)
-//{
-//    // FIXME:
-//}
-//
-//void CSVRender::PagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft,
-//    const QModelIndex& bottomRight)
-//{
-//    // FIXME:
-//}
-//
-//void CSVRender::PagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelIndex& parent,
-//    int start, int end)
-//{
-//    // FIXME:
-//}
+void CSVRender::PagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft,
+    const QModelIndex& bottomRight)
+{
+    for (std::map<CSMWorld::CellCoordinates, Cell *>::iterator iter (mCells.begin());
+        iter!=mCells.end(); ++iter)
+        iter->second->pathgridDataChanged (topLeft, bottomRight);
+}
 
 CSVRender::Cell *CSVRender::PagedWorldspaceWidget::findCell(const std::string &cellId)
 {
diff --git a/apps/opencs/view/render/pagedworldspacewidget.hpp b/apps/opencs/view/render/pagedworldspacewidget.hpp
index 099b4db256..ea9449b2ff 100644
--- a/apps/opencs/view/render/pagedworldspacewidget.hpp
+++ b/apps/opencs/view/render/pagedworldspacewidget.hpp
@@ -53,12 +53,6 @@ namespace CSVRender
 
             virtual void referenceAdded (const QModelIndex& index, int start, int end);
 
-            //virtual void pathgridAdded (const QModelIndex& parent, int start, int end);
-
-            //virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
-
-            //virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end);
-
             virtual std::string getStartupInstruction();
 
             Cell *findCell(const std::string &cellId);
@@ -103,7 +97,6 @@ namespace CSVRender
 
             virtual void mouseDoubleClickEvent (QMouseEvent *event);
 
-            // FIXME: temporary only until signals from the document is implemented
             virtual void pathgridInserted (const std::string &referenceId, const Ogre::Vector3 &pos);
             virtual void pathgridMoved (const std::string &pgName, const Ogre::Vector3 &pos);
             virtual void pathgridAboutToBeRemoved (const std::string &pgName);
@@ -121,6 +114,8 @@ namespace CSVRender
             virtual void cellAdded (const QModelIndex& index, int start, int end);
 
             virtual void flagAsModSlot();
+
+            virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
     };
 }
 
diff --git a/apps/opencs/view/render/unpagedworldspacewidget.cpp b/apps/opencs/view/render/unpagedworldspacewidget.cpp
index c95a87d038..af121ec196 100644
--- a/apps/opencs/view/render/unpagedworldspacewidget.cpp
+++ b/apps/opencs/view/render/unpagedworldspacewidget.cpp
@@ -166,23 +166,11 @@ void CSVRender::UnpagedWorldspaceWidget::addVisibilitySelectorButtons (
     tool->addButton (Element_Fog, "Fog");
 }
 
-//void CSVRender::UnpagedWorldspaceWidget::pathgridAdded (const QModelIndex& parent,
-//    int start, int end)
-//{
-//    // FIXME:
-//}
-//
-//void CSVRender::UnpagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft,
-//    const QModelIndex& bottomRight)
-//{
-//    // FIXME:
-//}
-//
-//void CSVRender::UnpagedWorldspaceWidget::pathgridAboutToBeRemoved (const QModelIndex& parent,
-//    int start, int end)
-//{
-//    // FIXME:
-//}
+void CSVRender::UnpagedWorldspaceWidget::pathgridDataChanged (const QModelIndex& topLeft,
+    const QModelIndex& bottomRight)
+{
+    // FIXME:
+}
 
 std::string CSVRender::UnpagedWorldspaceWidget::getStartupInstruction()
 {
diff --git a/apps/opencs/view/render/unpagedworldspacewidget.hpp b/apps/opencs/view/render/unpagedworldspacewidget.hpp
index 468c48739b..4e19efbf07 100644
--- a/apps/opencs/view/render/unpagedworldspacewidget.hpp
+++ b/apps/opencs/view/render/unpagedworldspacewidget.hpp
@@ -58,12 +58,6 @@ namespace CSVRender
 
             virtual void referenceAdded (const QModelIndex& index, int start, int end);
 
-            //virtual void pathgridAdded (const QModelIndex& index, int start, int end);
-
-            //virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
-
-            //virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end);
-
             virtual std::string getStartupInstruction();
 
         protected:
@@ -78,6 +72,8 @@ namespace CSVRender
 
             virtual void flagAsModSlot();
 
+            virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight);
+
         signals:
 
             void cellChanged(const CSMWorld::UniversalId& id);
diff --git a/apps/opencs/view/render/worldspacewidget.cpp b/apps/opencs/view/render/worldspacewidget.cpp
index 8418a32a6a..2d690303c5 100644
--- a/apps/opencs/view/render/worldspacewidget.cpp
+++ b/apps/opencs/view/render/worldspacewidget.cpp
@@ -56,15 +56,11 @@ CSVRender::WorldspaceWidget::WorldspaceWidget (CSMDoc::Document& document, QWidg
     connect (debugProfiles, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
         this, SLOT (debugProfileAboutToBeRemoved (const QModelIndex&, int, int)));
 
-    //QAbstractItemModel *pathgrids =
-        //document.getData().getTableModel (CSMWorld::UniversalId::Type_Pathgrid);
+    QAbstractItemModel *pathgrids =
+        document.getData().getTableModel (CSMWorld::UniversalId::Type_Pathgrid);
 
-    //connect (pathgrids, SIGNAL (rowsInserted (const QModelIndex&, int, int)),
-        //this, SLOT (pathgridAdded (const QModelIndex&, int, int)));
-    //connect (pathgrids, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
-        //this, SLOT (pathgridDataChanged (const QModelIndex&, const QModelIndex&)));
-    //connect (pathgrids, SIGNAL (rowsAboutToBeRemoved (const QModelIndex&, int, int)),
-        //this, SLOT (pathgridAboutToBeRemoved (const QModelIndex&, int, int)));
+    connect (pathgrids, SIGNAL (dataChanged (const QModelIndex&, const QModelIndex&)),
+        this, SLOT (pathgridDataChanged (const QModelIndex&, const QModelIndex&)));
 
     mPhysics = document.getPhysics(); // create physics if one doesn't exist
     mPhysics->addSceneManager(getSceneManager(), this);
@@ -460,17 +456,14 @@ void CSVRender::WorldspaceWidget::keyPressEvent (QKeyEvent *event)
         SceneWidget::keyPressEvent(event);
 }
 
-// FIXME: temporary until signals from the document are implemented
 void CSVRender::WorldspaceWidget::pathgridAboutToBeRemoved (const std::string &pgName)
 {
 }
 
-// FIXME: temporary until signals from the document are implemented
 void CSVRender::WorldspaceWidget::pathgridMoved (const std::string &pgName, const Ogre::Vector3 &newPos)
 {
 }
 
-// FIXME: temporary until signals from the document are implemented
 void CSVRender::WorldspaceWidget::pathgridInserted (const std::string &name, const Ogre::Vector3 &pos)
 {
 }
diff --git a/apps/opencs/view/render/worldspacewidget.hpp b/apps/opencs/view/render/worldspacewidget.hpp
index efd2ee8c05..a250547b32 100644
--- a/apps/opencs/view/render/worldspacewidget.hpp
+++ b/apps/opencs/view/render/worldspacewidget.hpp
@@ -163,11 +163,7 @@ namespace CSVRender
 
             void debugProfileAboutToBeRemoved (const QModelIndex& parent, int start, int end);
 
-            //virtual void pathgridAdded (const QModelIndex& index, int start, int end) = 0;
-
-            //virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0;
-
-            //virtual void pathgridAboutToBeRemoved (const QModelIndex& parent, int start, int end) = 0;
+            virtual void pathgridDataChanged (const QModelIndex& topLeft, const QModelIndex& bottomRight) = 0;
 
         protected slots:
 

From 193873b82952baba6866051058dc7bbfe23cf341 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Sun, 31 May 2015 10:28:45 +1000
Subject: [PATCH 28/29] Feature #1278: Editor: Mouse picking in worldspace
 widget

- With the move to OSG (see: https://forum.openmw.org/viewtopic.php?f=7&t=2755&p=31850#p31839) all the work done so far will soon be removed from the master branch.
- The pathgrid editing and integration to the model, as well as other improvements made, are unlikely to be accepted to the master.
- These will be maintained here until they can be ported to OSG.  It is easier to port when there is a working code base, anyway.
---
 apps/opencs/view/render/cell.cpp       |  7 ++++---
 apps/opencs/view/render/mousestate.cpp | 10 +++++-----
 2 files changed, 9 insertions(+), 8 deletions(-)

diff --git a/apps/opencs/view/render/cell.cpp b/apps/opencs/view/render/cell.cpp
index f7ec24a56d..49af585fd5 100644
--- a/apps/opencs/view/render/cell.cpp
+++ b/apps/opencs/view/render/cell.cpp
@@ -120,9 +120,10 @@ bool CSVRender::Cell::addObjects (int start, int end)
 
 CSVRender::Cell::Cell (CSMDoc::Document& document, Ogre::SceneManager *sceneManager,
     const std::string& id, boost::shared_ptr<CSVWorld::PhysicsSystem> physics, const Ogre::Vector3& origin)
-    : mDocument (document), mId (Misc::StringUtils::lowerCase (id)), mSceneMgr(sceneManager)
-    , mPhysics(physics), mX(0), mY(0), mPgIndex(-1), mModel(0), mProxyModel(0)
-    , mHandler(new CSMWorld::SignalHandler(this))
+: mDocument (document), mId (Misc::StringUtils::lowerCase (id))
+, mProxyModel(0), mModel(0), mPgIndex(-1), mHandler(new CSMWorld::SignalHandler(this))
+, mPhysics(physics), mSceneMgr(sceneManager), mX(0), mY(0)
+
 {
     mCellNode = sceneManager->getRootSceneNode()->createChildSceneNode();
     mCellNode->setPosition (origin);
diff --git a/apps/opencs/view/render/mousestate.cpp b/apps/opencs/view/render/mousestate.cpp
index 38dc41fbc4..e4921c4b70 100644
--- a/apps/opencs/view/render/mousestate.cpp
+++ b/apps/opencs/view/render/mousestate.cpp
@@ -56,11 +56,11 @@ namespace CSVRender
     //
 
     MouseState::MouseState(WorldspaceWidget *parent)
-        : mParent(parent), mPhysics(parent->mDocument.getPhysics()), mSceneManager(parent->getSceneManager())
-        , mCurrentObj(""), mMouseState(Mouse_Default), mOldCursorPos(0,0), mMouseEventTimer(0)
-        , mGrabbedSceneNode(""), mGrabbedRefId(""), mOrigObjPos(Ogre::Vector3())
-        , mOrigMousePos(Ogre::Vector3()), mOldMousePos(Ogre::Vector3()), mPlane(0)
-        , mColIndexPosX(0), mColIndexPosY(0), mColIndexPosZ(0), mIdTableModel(0)
+        : mMouseState(Mouse_Default), mParent(parent), mPhysics(parent->mDocument.getPhysics())
+        , mSceneManager(parent->getSceneManager()), mOldCursorPos(0,0), mCurrentObj(""), mGrabbedSceneNode(""), mGrabbedRefId("")
+        , mMouseEventTimer(0), mPlane(0), mOrigObjPos(Ogre::Vector3()), mOrigMousePos(Ogre::Vector3())
+        , mOldMousePos(Ogre::Vector3()), mIdTableModel(0), mColIndexPosX(0)
+        , mColIndexPosY(0), mColIndexPosZ(0)
     {
         const CSMWorld::RefCollection& references = mParent->mDocument.getData().getReferences();
 

From 610e6591213bb1ce07ed8869f39b0c14c7765646 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Fri, 12 Jun 2015 21:38:53 +1000
Subject: [PATCH 29/29] Fix physics model not being moved since commit ec80884.

---
 apps/opencs/view/render/object.cpp | 4 +---
 1 file changed, 1 insertion(+), 3 deletions(-)

diff --git a/apps/opencs/view/render/object.cpp b/apps/opencs/view/render/object.cpp
index 3607fb415d..9e39ee37bc 100644
--- a/apps/opencs/view/render/object.cpp
+++ b/apps/opencs/view/render/object.cpp
@@ -223,12 +223,10 @@ bool CSVRender::Object::referenceDataChanged (const QModelIndex& topLeft,
         {
             mReferenceableId =
                 references.getData (index, columnIndex).toString().toUtf8().constData();
-
-            update();
         }
 
+        update();
         adjust();
-
         return true;
     }