From b27a879352b155205a9809f567007e97bf7dd1a6 Mon Sep 17 00:00:00 2001
From: cc9cii <cc9c@iinet.net.au>
Date: Tue, 19 May 2015 22:01:40 +1000
Subject: [PATCH] Add the remaining Cell data for editing with dialogue
 subview. Should resolve Bug #2551.

- NOTE: the interior water flag and water level logic needs reviewing
- does not automatically disable region names for interiors without mQuasiEx flag
- Colour values can't be entered as RGB
- Region names are not drop down menus
---
 apps/opencs/model/world/columns.cpp           |  11 +-
 apps/opencs/model/world/columns.hpp           |  11 +-
 apps/opencs/model/world/data.cpp              |  25 ++-
 apps/opencs/model/world/data.hpp              |   2 +-
 .../model/world/nestedcoladapterimp.cpp       | 161 +++++++++++++++++-
 .../model/world/nestedcoladapterimp.hpp       |  26 +++
 6 files changed, 230 insertions(+), 6 deletions(-)

diff --git a/apps/opencs/model/world/columns.cpp b/apps/opencs/model/world/columns.cpp
index daf537c0c9..ee7a00ca0f 100644
--- a/apps/opencs/model/world/columns.cpp
+++ b/apps/opencs/model/world/columns.cpp
@@ -62,7 +62,7 @@ namespace CSMWorld
             { ColumnId_StarterSpell, "Starter Spell" },
             { ColumnId_AlwaysSucceeds, "Always Succeeds" },
             { ColumnId_SleepForbidden, "Sleep Forbidden" },
-            { ColumnId_InteriorWater, "Interior Water" },
+            { ColumnId_Water, "Has Water" },
             { ColumnId_InteriorSky, "Interior Sky" },
             { ColumnId_Model, "Model" },
             { ColumnId_Script, "Script" },
@@ -303,6 +303,15 @@ namespace CSMWorld
             { ColumnId_RaceSkill, "Skills" },
             { ColumnId_RaceBonus, "Bonus" },
 
+            { ColumnId_Interior, "Interior" },
+            { ColumnId_Ambient, "Ambient" },
+            { ColumnId_Sunlight, "Sunlight" },
+            { ColumnId_Fog, "Fog" },
+            { ColumnId_FogDensity, "Fog Density" },
+            { ColumnId_WaterLevel, "Water Level" },
+            { ColumnId_InteriorWater, "Interior Water" },
+            { ColumnId_MapColor, "Map Color" },
+
             { ColumnId_UseValue1, "Use value 1" },
             { ColumnId_UseValue2, "Use value 2" },
             { ColumnId_UseValue3, "Use value 3" },
diff --git a/apps/opencs/model/world/columns.hpp b/apps/opencs/model/world/columns.hpp
index 85276a695f..3a345b3ecd 100644
--- a/apps/opencs/model/world/columns.hpp
+++ b/apps/opencs/model/world/columns.hpp
@@ -57,7 +57,7 @@ namespace CSMWorld
             ColumnId_StarterSpell = 42,
             ColumnId_AlwaysSucceeds = 43,
             ColumnId_SleepForbidden = 44,
-            ColumnId_InteriorWater = 45,
+            ColumnId_Water = 45,
             ColumnId_InteriorSky = 46,
             ColumnId_Model = 47,
             ColumnId_Script = 48,
@@ -294,6 +294,15 @@ namespace CSMWorld
             ColumnId_RaceSkill = 266,
             ColumnId_RaceBonus = 267,
 
+            ColumnId_Interior = 268,
+            ColumnId_Ambient = 269,
+            ColumnId_Sunlight = 270,
+            ColumnId_Fog = 271,
+            ColumnId_FogDensity = 272,
+            ColumnId_WaterLevel = 273,
+            ColumnId_InteriorWater = 274,
+            ColumnId_MapColor = 275,
+
             // Allocated to a separate value range, so we don't get a collision should we ever need
             // to extend the number of use values.
             ColumnId_UseValue1 = 0x10000,
diff --git a/apps/opencs/model/world/data.cpp b/apps/opencs/model/world/data.cpp
index fa7e48a0ea..00349d4765 100644
--- a/apps/opencs/model/world/data.cpp
+++ b/apps/opencs/model/world/data.cpp
@@ -287,10 +287,31 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
     mCells.addColumn (new FixedRecordTypeColumn<Cell> (UniversalId::Type_Cell));
     mCells.addColumn (new NameColumn<Cell>);
     mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_SleepForbidden, ESM::Cell::NoSleep));
-    mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorWater, ESM::Cell::HasWater));
+    mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_Water, ESM::Cell::HasWater));
     mCells.addColumn (new FlagColumn<Cell> (Columns::ColumnId_InteriorSky, ESM::Cell::QuasiEx));
     mCells.addColumn (new RegionColumn<Cell>);
     mCells.addColumn (new RefNumCounterColumn<Cell>);
+    // Misc Cell data
+    mCells.addColumn (new NestedParentColumn<Cell> (Columns::ColumnId_Cell,
+        ColumnBase::Flag_Dialogue | ColumnBase::Flag_Dialogue_List));
+    index = mCells.getColumns()-1;
+    mCells.addAdapter (std::make_pair(&mCells.getColumn(index), new CellListAdapter ()));
+    mCells.getNestableColumn(index)->addColumn(
+        new NestedChildColumn (Columns::ColumnId_Interior, ColumnBase::Display_Boolean));
+    mCells.getNestableColumn(index)->addColumn(
+        new NestedChildColumn (Columns::ColumnId_Ambient, ColumnBase::Display_Integer));
+    mCells.getNestableColumn(index)->addColumn(
+        new NestedChildColumn (Columns::ColumnId_Sunlight, ColumnBase::Display_Integer));
+    mCells.getNestableColumn(index)->addColumn(
+        new NestedChildColumn (Columns::ColumnId_Fog, ColumnBase::Display_Integer));
+    mCells.getNestableColumn(index)->addColumn(
+        new NestedChildColumn (Columns::ColumnId_FogDensity, ColumnBase::Display_Float));
+    mCells.getNestableColumn(index)->addColumn(
+        new NestedChildColumn (Columns::ColumnId_InteriorWater, ColumnBase::Display_Boolean));
+    mCells.getNestableColumn(index)->addColumn(
+        new NestedChildColumn (Columns::ColumnId_WaterLevel, ColumnBase::Display_Float));
+    mCells.getNestableColumn(index)->addColumn(
+        new NestedChildColumn (Columns::ColumnId_MapColor, ColumnBase::Display_Integer));
 
     mEnchantments.addColumn (new StringIdColumn<ESM::Enchantment>);
     mEnchantments.addColumn (new RecordStateColumn<ESM::Enchantment>);
@@ -465,7 +486,7 @@ CSMWorld::Data::Data (ToUTF8::FromType encoding, const ResourcesManager& resourc
     addModel (new IdTree (&mTopicInfos, &mTopicInfos, IdTable::Feature_ReorderWithinTopic),
         UniversalId::Type_TopicInfo);
     addModel (new IdTable (&mJournalInfos, IdTable::Feature_ReorderWithinTopic), UniversalId::Type_JournalInfo);
-    addModel (new IdTable (&mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell);
+    addModel (new IdTree (&mCells, &mCells, IdTable::Feature_ViewId), UniversalId::Type_Cell);
     addModel (new IdTree (&mEnchantments, &mEnchantments), UniversalId::Type_Enchantment);
     addModel (new IdTable (&mBodyParts), UniversalId::Type_BodyPart);
     addModel (new IdTable (&mSoundGens), UniversalId::Type_SoundGen);
diff --git a/apps/opencs/model/world/data.hpp b/apps/opencs/model/world/data.hpp
index 8689b98c0c..060e47bd95 100644
--- a/apps/opencs/model/world/data.hpp
+++ b/apps/opencs/model/world/data.hpp
@@ -88,7 +88,7 @@ namespace CSMWorld
             IdCollection<ESM::StartScript> mStartScripts;
             NestedInfoCollection mTopicInfos;
             InfoCollection mJournalInfos;
-            IdCollection<Cell> mCells;
+            NestedIdCollection<Cell> mCells;
             IdCollection<LandTexture> mLandTextures;
             IdCollection<Land> mLand;
             RefIdCollection mReferenceables;
diff --git a/apps/opencs/model/world/nestedcoladapterimp.cpp b/apps/opencs/model/world/nestedcoladapterimp.cpp
index 7efe14dee2..a63ebe672d 100644
--- a/apps/opencs/model/world/nestedcoladapterimp.cpp
+++ b/apps/opencs/model/world/nestedcoladapterimp.cpp
@@ -481,7 +481,7 @@ namespace CSMWorld
 
     void InfoListAdapter::removeRow(Record<Info>& record, int rowToRemove) const
     {
-        throw std::logic_error ("cannot add a row to a fixed table");
+        throw std::logic_error ("cannot remove a row to a fixed table");
     }
 
     void InfoListAdapter::setTable(Record<Info>& record,
@@ -1033,4 +1033,163 @@ namespace CSMWorld
         // there are 7 skill bonuses
         return static_cast<int>(sizeof(record.get().mData.mBonus)/sizeof(record.get().mData.mBonus[0]));
     }
+
+    CellListAdapter::CellListAdapter () {}
+
+    void CellListAdapter::addRow(Record<CSMWorld::Cell>& record, int position) const
+    {
+        throw std::logic_error ("cannot add a row to a fixed table");
+    }
+
+    void CellListAdapter::removeRow(Record<CSMWorld::Cell>& record, int rowToRemove) const
+    {
+        throw std::logic_error ("cannot remove a row to a fixed table");
+    }
+
+    void CellListAdapter::setTable(Record<CSMWorld::Cell>& record,
+            const NestedTableWrapperBase& nestedTable) const
+    {
+        throw std::logic_error ("table operation not supported");
+    }
+
+    NestedTableWrapperBase* CellListAdapter::table(const Record<CSMWorld::Cell>& record) const
+    {
+        throw std::logic_error ("table operation not supported");
+    }
+
+    QVariant CellListAdapter::getData(const Record<CSMWorld::Cell>& record,
+            int subRowIndex, int subColIndex) const
+    {
+        CSMWorld::Cell cell = record.get();
+
+        bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0;
+        bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0;
+
+        switch (subColIndex)
+        {
+            case 0: return isInterior;
+            case 1: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mAmbient : QVariant();
+            case 2: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mSunlight : QVariant();
+            case 3: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFog : QVariant();
+            case 4: return (isInterior && !behaveLikeExterior) ? cell.mAmbi.mFogDensity : QVariant();
+            case 5: return (isInterior && !behaveLikeExterior) ? cell.mWaterInt==true : QVariant();
+            case 6:
+            {
+                if (isInterior && !behaveLikeExterior && cell.mWaterInt)
+                    return cell.mWater;
+                else
+                    return QVariant();
+            }
+            case 7: return isInterior ? QVariant() : cell.mMapColor; // TODO: how to select?
+            //case 8: return isInterior ? behaveLikeExterior : QVariant();
+            default: throw std::runtime_error("Cell subcolumn index out of range");
+        }
+    }
+
+    void CellListAdapter::setData(Record<CSMWorld::Cell>& record,
+            const QVariant& value, int subRowIndex, int subColIndex) const
+    {
+        CSMWorld::Cell cell = record.get();
+
+        bool isInterior = (cell.mData.mFlags & ESM::Cell::Interior) != 0;
+        bool behaveLikeExterior = (cell.mData.mFlags & ESM::Cell::QuasiEx) != 0;
+
+        switch (subColIndex)
+        {
+            case 0:
+            {
+                if (value.toBool())
+                    cell.mData.mFlags |= ESM::Cell::Interior;
+                else
+                    cell.mData.mFlags &= ~ESM::Cell::Interior;
+                break;
+            }
+            case 1:
+            {
+                if (isInterior && !behaveLikeExterior)
+                    cell.mAmbi.mAmbient = static_cast<int32_t>(value.toInt());
+                else
+                    return; // return without saving
+                break;
+            }
+            case 2:
+            {
+                if (isInterior && !behaveLikeExterior)
+                    cell.mAmbi.mSunlight = static_cast<int32_t>(value.toInt());
+                else
+                    return; // return without saving
+                break;
+            }
+            case 3:
+            {
+                if (isInterior && !behaveLikeExterior)
+                    cell.mAmbi.mFog = static_cast<int32_t>(value.toInt());
+                else
+                    return; // return without saving
+                break;
+            }
+            case 4:
+            {
+                if (isInterior && !behaveLikeExterior)
+                    cell.mAmbi.mFogDensity = value.toFloat();
+                else
+                    return; // return without saving
+                break;
+            }
+            case 5:
+            {
+                if (isInterior && !behaveLikeExterior)
+                    cell.mWaterInt = value.toBool();
+                else
+                    return; // return without saving
+                break;
+            }
+            case 6:
+            {
+                if (isInterior && !behaveLikeExterior && cell.mWaterInt)
+                    cell.mWater = value.toFloat();
+                else
+                    return; // return without saving
+                break;
+            }
+            case 7:
+            {
+                if (!isInterior)
+                    cell.mMapColor = value.toInt();
+                else
+                    return; // return without saving
+                break;
+            }
+#if 0
+            // redundant since this flag is shown in the main table as "Interior Sky"
+            // keep here for documenting the logic based on vanilla
+            case 8:
+            {
+                if (isInterior)
+                {
+                    if (value.toBool())
+                        cell.mData.mFlags |= ESM::Cell::QuasiEx;
+                    else
+                        cell.mData.mFlags &= ~ESM::Cell::QuasiEx;
+                }
+                else
+                    return; // return without saving
+                break;
+            }
+#endif
+            default: throw std::runtime_error("Cell subcolumn index out of range");
+        }
+
+        record.setModified (cell);
+    }
+
+    int CellListAdapter::getColumnsCount(const Record<CSMWorld::Cell>& record) const
+    {
+        return 8;
+    }
+
+    int CellListAdapter::getRowsCount(const Record<CSMWorld::Cell>& record) const
+    {
+        return 1; // fixed at size 1
+    }
 }
diff --git a/apps/opencs/model/world/nestedcoladapterimp.hpp b/apps/opencs/model/world/nestedcoladapterimp.hpp
index 4b1e2cd09e..81c52588bb 100644
--- a/apps/opencs/model/world/nestedcoladapterimp.hpp
+++ b/apps/opencs/model/world/nestedcoladapterimp.hpp
@@ -12,6 +12,7 @@
 
 #include "nestedcolumnadapter.hpp"
 #include "nestedtablewrapper.hpp"
+#include "cell.hpp"
 
 namespace ESM
 {
@@ -488,6 +489,31 @@ namespace CSMWorld
 
         virtual int getRowsCount(const Record<ESM::Race>& record) const;
     };
+
+    class CellListAdapter : public NestedColumnAdapter<CSMWorld::Cell>
+    {
+    public:
+        CellListAdapter ();
+
+        virtual void addRow(Record<CSMWorld::Cell>& record, int position) const;
+
+        virtual void removeRow(Record<CSMWorld::Cell>& record, int rowToRemove) const;
+
+        virtual void setTable(Record<CSMWorld::Cell>& record,
+                const NestedTableWrapperBase& nestedTable) const;
+
+        virtual NestedTableWrapperBase* table(const Record<CSMWorld::Cell>& record) const;
+
+        virtual QVariant getData(const Record<CSMWorld::Cell>& record,
+                int subRowIndex, int subColIndex) const;
+
+        virtual void setData(Record<CSMWorld::Cell>& record,
+                const QVariant& value, int subRowIndex, int subColIndex) const;
+
+        virtual int getColumnsCount(const Record<CSMWorld::Cell>& record) const;
+
+        virtual int getRowsCount(const Record<CSMWorld::Cell>& record) const;
+    };
 }
 
 #endif // CSM_WOLRD_NESTEDCOLADAPTERIMP_H