PrimeWorldEditor/src/Editor/WorldEditor/CInstancesModel.cpp

591 lines
18 KiB
C++

#include "CInstancesModel.h"
#include "Editor/UICommon.h"
#include <Core/Resource/Script/CScriptLayer.h>
#include <Core/Resource/Script/NGameList.h>
#include <Core/Scene/CScriptNode.h>
#include <QApplication>
#include <QIcon>
/*
* The tree has 3 levels:
* 1. Node Type (Script Object, Light, World Mesh, etc) - represented with ID of 0
* 2. Object Type (Actor, Platform, SpawnPoint, etc) - represented with flags
* 3. Instance - represented with pointer to instance (0x1 bit is guaranteed to be clear)
*
* Flags for Object Type tree items
* AAAAAAAAAAAAAAAAAAAAAAAAAAABBBBC
* A: Row index
* B: Node type row index
* C: Item type (ObjType, Instance)
*
* Also this class is kind of a mess, it would be nice to rewrite it at some point
*/
#define TYPES_ROW_INDEX_MASK 0xFFFFFFE0
#define TYPES_NODE_TYPE_MASK 0x0000001E
#define TYPES_ITEM_TYPE_MASK 0x00000001
#define TYPES_ROW_INDEX_SHIFT 5
#define TYPES_NODE_TYPE_SHIFT 1
#define TYPES_ITEM_TYPE_SHIFT 0
CInstancesModel::CInstancesModel(CWorldEditor *pEditor, QObject *pParent)
: QAbstractItemModel(pParent)
, mpEditor(pEditor)
, mpScene(pEditor->Scene())
, mpArea(nullptr)
, mpCurrentGame(nullptr)
, mModelType(EInstanceModelType::Layers)
, mShowColumnEnabled(true)
, mChangingLayout(false)
{
mBaseItems << "Script";
connect(gpEdApp, SIGNAL(ActiveProjectChanged(CGameProject*)), this, SLOT(OnActiveProjectChanged(CGameProject*)));
connect(mpEditor, SIGNAL(MapChanged(CWorld*,CGameArea*)), this, SLOT(OnMapChange()));
connect(mpEditor, SIGNAL(NodeAboutToBeSpawned()), this, SLOT(NodeAboutToBeCreated()));
connect(mpEditor, SIGNAL(NodeSpawned(CSceneNode*)), this, SLOT(NodeCreated(CSceneNode*)));
connect(mpEditor, SIGNAL(NodeAboutToBeDeleted(CSceneNode*)), this, SLOT(NodeAboutToBeDeleted(CSceneNode*)));
connect(mpEditor, SIGNAL(NodeDeleted()), this, SLOT(NodeDeleted()));
connect(mpEditor, SIGNAL(PropertyModified(IProperty*,CScriptObject*)), this, SLOT(PropertyModified(IProperty*,CScriptObject*)));
connect(mpEditor, SIGNAL(InstancesLayerAboutToChange()), this, SLOT(InstancesLayerPreChange()));
connect(mpEditor, SIGNAL(InstancesLayerChanged(QList<CScriptNode*>)), this, SLOT(InstancesLayerPostChange(QList<CScriptNode*>)));
}
CInstancesModel::~CInstancesModel()
{
}
QVariant CInstancesModel::headerData(int Section, Qt::Orientation Orientation, int Role) const
{
if ((Orientation == Qt::Horizontal) && (Role == Qt::DisplayRole))
{
switch (Section)
{
case 0: return "Name";
case 1: return (mModelType == EInstanceModelType::Layers ? "Type" : "Layer");
case 2: return "Show";
}
}
return QVariant::Invalid;
}
QModelIndex CInstancesModel::index(int Row, int Column, const QModelIndex& rkParent) const
{
if (!hasIndex(Row, Column, rkParent))
return QModelIndex();
EIndexType Type = IndexType(rkParent);
// Parent is root - child is Node type index
if (Type == EIndexType::Root)
{
if (Row < mBaseItems.count())
return createIndex(Row, Column, quint64(0));
else
return QModelIndex();
}
// Parent is node - child is Object type index
else if (Type == EIndexType::NodeType)
return createIndex(Row, Column, ((Row << TYPES_ROW_INDEX_SHIFT) | (rkParent.row() << TYPES_NODE_TYPE_SHIFT) | 1));
// Parent is object - child is Instance index
else if (Type == EIndexType::ObjectType)
{
uint32 RootRow = rkParent.parent().row();
// Object
if (RootRow == 0)
{
if (mModelType == EInstanceModelType::Layers)
{
CScriptLayer *pLayer = mpArea->ScriptLayer(rkParent.row());
if ((uint32) Row >= pLayer->NumInstances())
return QModelIndex();
else
return createIndex(Row, Column, (*pLayer)[Row]);
}
else if (mModelType == EInstanceModelType::Types)
{
const std::list<CScriptObject*>& list = mTemplateList[rkParent.row()]->ObjectList();
if ((uint32) Row >= list.size())
return QModelIndex();
else
{
auto it = std::next(list.begin(), Row);
return createIndex(Row, Column, *it);
}
}
}
// todo: implement getters for other types
}
return QModelIndex();
}
QModelIndex CInstancesModel::parent(const QModelIndex& rkChild) const
{
EIndexType Type = IndexType(rkChild);
// Root parent
if (Type == EIndexType::NodeType)
return QModelIndex();
// Node type parent
if (Type == EIndexType::ObjectType)
{
uint32 NodeTypeRow = (rkChild.internalId() & TYPES_NODE_TYPE_MASK) >> TYPES_NODE_TYPE_SHIFT;
return createIndex(NodeTypeRow, 0, quint64(0));
}
// Object type parent
else if (Type == EIndexType::Instance)
{
CScriptObject *pObj = static_cast<CScriptObject*> (rkChild.internalPointer());
if (mModelType == EInstanceModelType::Layers)
{
CScriptLayer *pLayer = pObj->Layer();
for (uint32 iLyr = 0; iLyr < mpArea->NumScriptLayers(); iLyr++)
{
if (mpArea->ScriptLayer(iLyr) == pLayer)
return createIndex(iLyr, 0, (iLyr << TYPES_ROW_INDEX_SHIFT) | 1);
}
}
else if (mModelType == EInstanceModelType::Types)
{
CScriptTemplate *pTemp = pObj->Template();
for (int iTemp = 0; iTemp < mTemplateList.size(); iTemp++)
{
if (mTemplateList[iTemp] == pTemp)
return createIndex(iTemp, 0, (iTemp << TYPES_ROW_INDEX_SHIFT) | 1);
}
}
}
return QModelIndex();
}
int CInstancesModel::rowCount(const QModelIndex& rkParent) const
{
EIndexType Type = IndexType(rkParent);
// Node types
if (Type == EIndexType::Root)
return mBaseItems.count();
// Object types
else if (Type == EIndexType::NodeType)
{
// Script Objects
if (rkParent.row() == 0)
{
if (mModelType == EInstanceModelType::Layers)
return (mpArea ? mpArea->NumScriptLayers() : 0);
else
return mTemplateList.size();
}
else
return 0;
}
// Instances
else if (Type == EIndexType::ObjectType)
{
uint32 RowIndex = ((rkParent.internalId() & TYPES_ROW_INDEX_MASK) >> TYPES_ROW_INDEX_SHIFT);
if (mModelType == EInstanceModelType::Layers)
return (mpArea ? mpArea->ScriptLayer(RowIndex)->NumInstances() : 0);
else
return mTemplateList[RowIndex]->NumObjects();
}
else
return 0;
}
int CInstancesModel::columnCount(const QModelIndex& /*rkParent*/) const
{
return (mShowColumnEnabled ? 3 : 2);
}
QVariant CInstancesModel::data(const QModelIndex& rkIndex, int Role) const
{
EIndexType Type = IndexType(rkIndex);
// Name/Layer
if ((Role == Qt::DisplayRole) || (Role == Qt::ToolTipRole))
{
// Node types
if (Type == EIndexType::NodeType)
{
if (rkIndex.column() == 0)
return mBaseItems[rkIndex.row()];
else
return QVariant::Invalid;
}
// Object types
else if (Type == EIndexType::ObjectType)
{
if (rkIndex.column() == 0)
{
if (mModelType == EInstanceModelType::Layers)
return TO_QSTRING(mpEditor->ActiveArea()->ScriptLayer(rkIndex.row())->Name());
else
return TO_QSTRING(mTemplateList[rkIndex.row()]->Name());
}
// todo: show/hide button in column 2
else
return QVariant::Invalid;
}
// Instances
else if (Type == EIndexType::Instance)
{
// todo: show/hide button
CScriptObject *pObj = static_cast<CScriptObject*>(rkIndex.internalPointer());
if (rkIndex.column() == 0)
return TO_QSTRING(pObj->InstanceName());
else if (rkIndex.column() == 1)
{
if (mModelType == EInstanceModelType::Layers)
return TO_QSTRING(pObj->Template()->Name());
else if (mModelType == EInstanceModelType::Types)
return TO_QSTRING(pObj->Layer()->Name());
}
else
return QVariant::Invalid;
}
}
// Show/Hide Buttons
else if ((Role == Qt::DecorationRole) && (rkIndex.column() == 2))
{
if (!mpScene) return QVariant::Invalid;
static QIcon Visible(":/icons/Show.svg");
static QIcon Invisible(":/icons/Hide.svg");
// Show/Hide Node Types
if (Type == EIndexType::NodeType)
{
// Commented out pending a proper implementation of turning node types on/off from the instance view
/*bool IsVisible;
switch (index.row())
{
case 0: IsVisible = mpScene->AreScriptObjectsEnabled();
case 1: IsVisible = mpScene->AreLightsEnabled();
default: IsVisible = false;
}
if (IsVisible) return Visible;
else return Invisible;*/
}
// Show/Hide Object Types
else if (Type == EIndexType::ObjectType)
{
if (mModelType == EInstanceModelType::Layers)
{
CScriptLayer *pLayer = IndexLayer(rkIndex);
if (pLayer->IsVisible()) return Visible;
else return Invisible;
}
else if (mModelType == EInstanceModelType::Types)
{
CScriptTemplate *pTemp = IndexTemplate(rkIndex);
if (pTemp->IsVisible()) return Visible;
else return Invisible;
}
}
// Show/Hide Instance
else if (Type == EIndexType::Instance)
{
CScriptObject *pObj = IndexObject(rkIndex);
CScriptNode *pNode = mpScene->NodeForInstance(pObj);
if (pNode->MarkedVisible()) return Visible;
else return Invisible;
}
}
return QVariant::Invalid;
}
void CInstancesModel::SetModelType(EInstanceModelType Type)
{
mModelType = Type;
}
void CInstancesModel::SetShowColumnEnabled(bool Enabled)
{
mShowColumnEnabled = Enabled;
emit layoutChanged();
}
CScriptLayer* CInstancesModel::IndexLayer(const QModelIndex& rkIndex) const
{
if ((mModelType != EInstanceModelType::Layers) || (IndexNodeType(rkIndex) != ENodeType::Script) || (IndexType(rkIndex) != EIndexType::ObjectType))
return nullptr;
uint32 RowIndex = ((rkIndex.internalId() & TYPES_ROW_INDEX_MASK) >> TYPES_ROW_INDEX_SHIFT);
return mpArea->ScriptLayer(RowIndex);
}
CScriptTemplate* CInstancesModel::IndexTemplate(const QModelIndex& rkIndex) const
{
if ((mModelType != EInstanceModelType::Types) || (IndexNodeType(rkIndex) != ENodeType::Script) || (IndexType(rkIndex) != EIndexType::ObjectType))
return nullptr;
uint32 RowIndex = ((rkIndex.internalId() & TYPES_ROW_INDEX_MASK) >> TYPES_ROW_INDEX_SHIFT);
return mTemplateList[RowIndex];
}
CScriptObject* CInstancesModel::IndexObject(const QModelIndex& rkIndex) const
{
if ((IndexNodeType(rkIndex) != ENodeType::Script) || (IndexType(rkIndex) != EIndexType::Instance))
return nullptr;
return static_cast<CScriptObject*>(rkIndex.internalPointer());
}
// ************ PUBLIC SLOTS ************
void CInstancesModel::OnActiveProjectChanged(CGameProject *pProj)
{
if (mModelType == EInstanceModelType::Types)
{
if (pProj)
mpCurrentGame = NGameList::GetGameTemplate( pProj->Game() );
else
mpCurrentGame = nullptr;
GenerateList();
}
}
void CInstancesModel::OnMapChange()
{
if (mModelType == EInstanceModelType::Types)
GenerateList();
else
{
beginResetModel();
mpArea = mpEditor->ActiveArea();
endResetModel();
}
}
void CInstancesModel::NodeAboutToBeCreated()
{
if (!mChangingLayout)
{
emit layoutAboutToBeChanged();
mChangingLayout = true;
}
}
void CInstancesModel::NodeCreated(CSceneNode *pNode)
{
if (mChangingLayout)
{
emit layoutChanged();
mChangingLayout = false;
}
if (mModelType == EInstanceModelType::Types)
{
if (pNode->NodeType() == ENodeType::Script)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Instance();
pObj->Template()->SortObjects();
if (pObj->Template()->NumObjects() == 1)
{
QModelIndex ScriptRootIdx = index(0, 0, QModelIndex());
int NewIndex = 0;
for (; NewIndex < mTemplateList.size(); NewIndex++)
{
if (mTemplateList[NewIndex]->Name() > pObj->Template()->Name())
break;
}
beginInsertRows(ScriptRootIdx, NewIndex, NewIndex);
mTemplateList.insert(NewIndex, pObj->Template());
endInsertRows();
}
}
}
}
void CInstancesModel::NodeAboutToBeDeleted(CSceneNode *pNode)
{
if (pNode->NodeType() == ENodeType::Script)
{
if (mModelType == EInstanceModelType::Types)
{
CScriptNode *pScript = static_cast<CScriptNode*>(pNode);
CScriptObject *pObj = pScript->Instance();
if (pObj->Template()->NumObjects() <= 1)
{
QModelIndex ScriptRootIdx = index(0, 0, QModelIndex());
int TempIdx = mTemplateList.indexOf(pObj->Template());
beginRemoveRows(ScriptRootIdx, TempIdx, TempIdx);
mTemplateList.removeOne(pObj->Template());
endRemoveRows();
}
else if (!mChangingLayout)
{
emit layoutAboutToBeChanged();
mChangingLayout = true;
}
}
else if (!mChangingLayout)
{
emit layoutAboutToBeChanged();
mChangingLayout = true;
}
}
}
void CInstancesModel::NodeDeleted()
{
if (mChangingLayout)
{
emit layoutChanged();
mChangingLayout = false;
}
}
void CInstancesModel::PropertyModified(IProperty *pProp, CScriptObject *pInst)
{
if (pProp->Name() == "Name")
{
QModelIndex ScriptRoot = index(0, 0, QModelIndex());
if (mModelType == EInstanceModelType::Layers)
{
uint32 Index = pInst->Layer()->AreaIndex();
QModelIndex LayerIndex = index(Index, 0, ScriptRoot);
QModelIndex InstIndex = index(pInst->LayerIndex(), 0, LayerIndex);
emit dataChanged(InstIndex, InstIndex);
}
else
{
uint32 Index = mTemplateList.indexOf(pInst->Template());
QModelIndex TempIndex = index(Index, 0, ScriptRoot);
QList<CScriptObject*> InstList = QList<CScriptObject*>::fromStdList(pInst->Template()->ObjectList());
uint32 InstIdx = InstList.indexOf(pInst);
QModelIndex InstIndex = index(InstIdx, 0, TempIndex);
emit dataChanged(InstIndex, InstIndex);
}
}
}
void CInstancesModel::InstancesLayerPreChange()
{
// This is only really needed on layers, which have rows moved.
// Types just need to update column 1 so we can handle that when the change is finished.
if (mModelType == EInstanceModelType::Layers)
emit layoutAboutToBeChanged();
}
void CInstancesModel::InstancesLayerPostChange(const QList<CScriptNode*>& rkInstanceList)
{
QList<CScriptObject*> InstanceList;
foreach (CScriptNode *pNode, rkInstanceList)
InstanceList << pNode->Instance();
QModelIndex ScriptIdx = index(0, 0, QModelIndex());
// For types, just find the instances that have changed layers and emit dataChanged for column 1.
if (mModelType == EInstanceModelType::Types)
{
for (int iType = 0; iType < rowCount(ScriptIdx); iType++)
{
QModelIndex TypeIdx = index(iType, 0, ScriptIdx);
for (int iInst = 0; iInst < rowCount(TypeIdx); iInst++)
{
QModelIndex InstIdx = index(iInst, 1, TypeIdx);
CScriptObject *pInst = IndexObject(InstIdx);
if (InstanceList.contains(pInst))
emit dataChanged(InstIdx, InstIdx);
}
}
}
// For layers we just need to emit layoutChanged() and done
else
emit layoutChanged();
}
// ************ STATIC ************
CInstancesModel::EIndexType CInstancesModel::IndexType(const QModelIndex& rkIndex)
{
if (!rkIndex.isValid()) return EIndexType::Root;
else if (rkIndex.internalId() == 0) return EIndexType::NodeType;
else if (((rkIndex.internalId() & TYPES_ITEM_TYPE_MASK) >> TYPES_ITEM_TYPE_SHIFT) == 1) return EIndexType::ObjectType;
else return EIndexType::Instance;
}
ENodeType CInstancesModel::IndexNodeType(const QModelIndex& rkIndex)
{
EIndexType type = IndexType(rkIndex);
const ENodeType kTypes[] = { ENodeType::Script, ENodeType::Light };
switch (type)
{
case EIndexType::Root: return ENodeType::None;
case EIndexType::NodeType: return (ENodeType) kTypes[ rkIndex.row() ];
case EIndexType::ObjectType: return (ENodeType) kTypes[ rkIndex.parent().row() ];
case EIndexType::Instance: return (ENodeType) kTypes[ rkIndex.parent().parent().row() ];
default: return ENodeType::None;
}
}
// ************ PRIVATE ************
void CInstancesModel::GenerateList()
{
beginResetModel();
mTemplateList.clear();
if (mpCurrentGame)
{
uint32 NumTemplates = mpCurrentGame->NumScriptTemplates();
for (uint32 iTemp = 0; iTemp < NumTemplates; iTemp++)
{
CScriptTemplate *pTemp = mpCurrentGame->TemplateByIndex(iTemp);
if (pTemp->NumObjects() > 0)
mTemplateList << pTemp;
}
std::sort(mTemplateList.begin(), mTemplateList.end(), [](CScriptTemplate *pLeft, CScriptTemplate *pRight) -> bool {
return (pLeft->Name() < pRight->Name());
});
}
endResetModel();
}