mirror of
https://github.com/AxioDL/PrimeWorldEditor.git
synced 2025-05-25 08:41:26 +00:00
493 lines
16 KiB
C++
493 lines
16 KiB
C++
#include "CPropertyModel.h"
|
|
#include "Editor/UICommon.h"
|
|
#include <Core/Resource/Script/IProperty.h>
|
|
#include <Core/Resource/Script/IPropertyTemplate.h>
|
|
#include <QSize>
|
|
|
|
CPropertyModel::CPropertyModel(QObject *pParent /*= 0*/)
|
|
: QAbstractItemModel(pParent)
|
|
, mpBaseStruct(nullptr)
|
|
{
|
|
}
|
|
|
|
void CPropertyModel::SetBaseStruct(CPropertyStruct *pBaseStruct)
|
|
{
|
|
beginResetModel();
|
|
mpBaseStruct = pBaseStruct;
|
|
endResetModel();
|
|
}
|
|
|
|
IProperty* CPropertyModel::PropertyForIndex(const QModelIndex& rkIndex, bool HandleFlaggedPointers) const
|
|
{
|
|
if (!rkIndex.isValid()) return mpBaseStruct;
|
|
|
|
if (rkIndex.internalId() & 0x1)
|
|
{
|
|
if (HandleFlaggedPointers)
|
|
{
|
|
void *pID = (void*) (rkIndex.internalId() & ~0x1);
|
|
return static_cast<IProperty*>(pID);
|
|
}
|
|
else
|
|
return nullptr;
|
|
}
|
|
|
|
return static_cast<IProperty*>(rkIndex.internalPointer());
|
|
}
|
|
|
|
QModelIndex CPropertyModel::IndexForProperty(IProperty *pProp) const
|
|
{
|
|
if (pProp == mpBaseStruct) return QModelIndex();
|
|
|
|
QVector<u32> RowNumbers;
|
|
IProperty *pChild = pProp;
|
|
CPropertyStruct *pParent = pProp->Parent();
|
|
|
|
while (pParent)
|
|
{
|
|
// Check for array with one sub-property
|
|
CPropertyStruct *pGrandparent = pParent->Parent();
|
|
if (pGrandparent && pGrandparent->Type() == eArrayProperty && pParent->Count() == 1)
|
|
{
|
|
pChild = pParent;
|
|
pParent = pGrandparent;
|
|
continue;
|
|
}
|
|
|
|
// Find row index for this child property
|
|
for (u32 iChild = 0; iChild < pParent->Count(); iChild++)
|
|
{
|
|
if (pParent->PropertyByIndex(iChild) == pChild)
|
|
{
|
|
RowNumbers << iChild;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pChild = pParent;
|
|
pParent = pGrandparent;
|
|
}
|
|
|
|
// Find the corresponding QModelIndex in the same spot
|
|
QModelIndex Index = QModelIndex();
|
|
|
|
for (int iChild = RowNumbers.size() - 1; iChild >= 0; iChild--)
|
|
Index = index(RowNumbers[iChild], 0, Index);
|
|
|
|
return Index;
|
|
}
|
|
|
|
int CPropertyModel::columnCount(const QModelIndex& /*rkParent*/) const
|
|
{
|
|
return 2;
|
|
}
|
|
|
|
int CPropertyModel::rowCount(const QModelIndex& rkParent) const
|
|
{
|
|
if (!mpBaseStruct) return 0;
|
|
if (!rkParent.isValid()) return mpBaseStruct->Count();
|
|
if (rkParent.column() != 0) return 0;
|
|
if (rkParent.internalId() & 0x1) return 0;
|
|
|
|
IProperty *pProp = PropertyForIndex(rkParent, false);
|
|
|
|
switch (pProp->Type())
|
|
{
|
|
case eStructProperty:
|
|
case eArrayProperty:
|
|
return static_cast<CPropertyStruct*>(pProp)->Count();
|
|
|
|
case eBitfieldProperty:
|
|
return static_cast<CBitfieldTemplate*>(pProp->Template())->NumFlags();
|
|
|
|
case eVector3Property:
|
|
return 3;
|
|
|
|
case eColorProperty:
|
|
return 4;
|
|
|
|
case eCharacterProperty:
|
|
{
|
|
CAnimationParameters Params = static_cast<TCharacterProperty*>(pProp)->Get();
|
|
if (Params.Version() <= eEchoes) return 3;
|
|
if (Params.Version() <= eCorruption) return 2;
|
|
return 5;
|
|
}
|
|
|
|
default:
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
QVariant CPropertyModel::headerData(int Section, Qt::Orientation Orientation, int Role) const
|
|
{
|
|
if (Orientation == Qt::Horizontal && Role == Qt::DisplayRole)
|
|
{
|
|
if (Section == 0) return "Name";
|
|
if (Section == 1) return "Value";
|
|
}
|
|
return QVariant::Invalid;
|
|
}
|
|
|
|
QVariant CPropertyModel::data(const QModelIndex& rkIndex, int Role) const
|
|
{
|
|
if (!rkIndex.isValid())
|
|
return QVariant::Invalid;
|
|
|
|
if (Role == Qt::DisplayRole || (Role == Qt::ToolTipRole && rkIndex.column() == 1) )
|
|
{
|
|
if (rkIndex.internalId() & 0x1)
|
|
{
|
|
IProperty *pProp = PropertyForIndex(rkIndex, true);
|
|
|
|
if (pProp->Type() == eColorProperty)
|
|
{
|
|
if (rkIndex.column() == 0)
|
|
{
|
|
if (rkIndex.row() == 0) return "R";
|
|
if (rkIndex.row() == 1) return "G";
|
|
if (rkIndex.row() == 2) return "B";
|
|
if (rkIndex.row() == 3) return "A";
|
|
}
|
|
|
|
else if (rkIndex.column() == 1)
|
|
{
|
|
TStringList Strings = pProp->ToString().Split(" ,");
|
|
|
|
int i = 0;
|
|
for (auto it = Strings.begin(); it != Strings.end(); it++)
|
|
{
|
|
if (i == rkIndex.row()) return TO_QSTRING(*it);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (pProp->Type() == eVector3Property)
|
|
{
|
|
if (rkIndex.column() == 0)
|
|
{
|
|
if (rkIndex.row() == 0) return "X";
|
|
if (rkIndex.row() == 1) return "Y";
|
|
if (rkIndex.row() == 2) return "Z";
|
|
}
|
|
|
|
else if (rkIndex.column() == 1)
|
|
{
|
|
TStringList Strings = pProp->ToString().Split(" ,");
|
|
|
|
int i = 0;
|
|
for (auto it = Strings.begin(); it != Strings.end(); it++)
|
|
{
|
|
if (i == rkIndex.row()) return TO_QSTRING(*it);
|
|
i++;
|
|
}
|
|
}
|
|
}
|
|
|
|
else if (pProp->Type() == eBitfieldProperty)
|
|
{
|
|
CBitfieldTemplate *pBitfield = static_cast<CBitfieldTemplate*>(pProp->Template());
|
|
|
|
if (rkIndex.column() == 0)
|
|
return TO_QSTRING(pBitfield->FlagName(rkIndex.row()));
|
|
|
|
if (rkIndex.column() == 1)
|
|
{
|
|
if (Role == Qt::DisplayRole)
|
|
return "";
|
|
else
|
|
return TO_QSTRING(TString::HexString(pBitfield->FlagMask(rkIndex.row()), true, true, 8));
|
|
}
|
|
}
|
|
|
|
else if (pProp->Type() == eCharacterProperty)
|
|
{
|
|
TCharacterProperty *pChar = static_cast<TCharacterProperty*>(pProp);
|
|
CAnimationParameters Params = pChar->Get();
|
|
|
|
// There are three different layouts for this property - one for MP1/2, one for MP3, and one for DKCR
|
|
if (Params.Version() <= eEchoes)
|
|
{
|
|
if (rkIndex.column() == 0)
|
|
{
|
|
if (rkIndex.row() == 0) return "AnimSet";
|
|
if (rkIndex.row() == 1) return "Character";
|
|
if (rkIndex.row() == 2) return "Default Anim";
|
|
}
|
|
|
|
// For column 1, rows 0/1 have persistent editors so we only handle 2
|
|
if (rkIndex.column() == 1 && rkIndex.row() == 2)
|
|
return QString::number(Params.Unknown(0));
|
|
}
|
|
|
|
else if (Params.Version() <= eCorruption)
|
|
{
|
|
if (rkIndex.column() == 0)
|
|
{
|
|
if (rkIndex.row() == 0) return "Character";
|
|
if (rkIndex.row() == 1) return "Default Anim";
|
|
}
|
|
|
|
// Same deal here, only handle row 1
|
|
if (rkIndex.column() == 1 && rkIndex.row() == 1)
|
|
return QString::number(Params.Unknown(0));
|
|
}
|
|
|
|
else
|
|
{
|
|
if (rkIndex.column() == 0)
|
|
{
|
|
if (rkIndex.row() == 0) return "Character";
|
|
else return "Unknown " + QString::number(rkIndex.row());
|
|
}
|
|
|
|
if (rkIndex.column() == 1 && rkIndex.row() > 0)
|
|
return QString::number(Params.Unknown(rkIndex.row() - 1));
|
|
}
|
|
}
|
|
}
|
|
|
|
else
|
|
{
|
|
IProperty *pProp = PropertyForIndex(rkIndex, false);
|
|
|
|
if (rkIndex.column() == 0)
|
|
{
|
|
// Check for arrays
|
|
IProperty *pParent = pProp->Parent();
|
|
|
|
if (pParent)
|
|
{
|
|
// For direct array sub-properties, display the element name instead of the property name (the property name is the array name)
|
|
if (pProp->Type() == eStructProperty && pParent->Type() == eArrayProperty)
|
|
{
|
|
TString ElementName = static_cast<CArrayProperty*>(pParent)->ElementName();
|
|
return QString("%1 %2").arg(TO_QSTRING(ElementName)).arg(rkIndex.row() + 1);
|
|
}
|
|
|
|
// Check whether the parent struct is an array element with one sub-property
|
|
if (pParent->Type() == eStructProperty && pParent->Parent() && pParent->Parent()->Type() == eArrayProperty)
|
|
{
|
|
if (static_cast<CPropertyStruct*>(pParent)->Count() == 1)
|
|
return QString("%1 %2").arg(TO_QSTRING(pProp->Name())).arg(rkIndex.row() + 1);
|
|
}
|
|
}
|
|
|
|
// Display property name for everything else
|
|
return TO_QSTRING(pProp->Name());
|
|
}
|
|
|
|
if (rkIndex.column() == 1)
|
|
{
|
|
switch (pProp->Type())
|
|
{
|
|
// Enclose vector property text in parentheses
|
|
case eVector3Property:
|
|
return "(" + TO_QSTRING(pProp->ToString()) + ")";
|
|
|
|
// Display character name for characters
|
|
case eCharacterProperty:
|
|
return TO_QSTRING(static_cast<TCharacterProperty*>(pProp)->Get().GetCurrentCharacterName());
|
|
|
|
// Display enumerator name for enums (but only on ToolTipRole)
|
|
case eEnumProperty:
|
|
if (Role == Qt::ToolTipRole)
|
|
{
|
|
TEnumProperty *pEnum = static_cast<TEnumProperty*>(pProp);
|
|
CEnumTemplate *pTemp = static_cast<CEnumTemplate*>(pEnum->Template());
|
|
return TO_QSTRING(pTemp->EnumeratorName(pEnum->Get()));
|
|
}
|
|
else return "";
|
|
|
|
// Display the element count for arrays
|
|
case eArrayProperty:
|
|
{
|
|
u32 Count = static_cast<CArrayProperty*>(pProp)->Count();
|
|
return QString("%1 element%2").arg(Count).arg(Count != 1 ? "s" : "");
|
|
}
|
|
|
|
// No display text on properties with persistent editors
|
|
case eBoolProperty:
|
|
case eFileProperty:
|
|
case eColorProperty:
|
|
if (Role == Qt::DisplayRole)
|
|
return "";
|
|
// fall through
|
|
// Display property value to string for everything else
|
|
default:
|
|
return TO_QSTRING(pProp->ToString());
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (Role == Qt::ToolTipRole && rkIndex.column() == 0)
|
|
{
|
|
if (!(rkIndex.internalId() & 0x1))
|
|
{
|
|
// Add name
|
|
IProperty *pProp = PropertyForIndex(rkIndex, false);
|
|
QString DisplayText = data(rkIndex, Qt::DisplayRole).toString();
|
|
QString Text = QString("<b>%1</b> <i>(%2)</i>").arg(DisplayText).arg(TO_QSTRING(PropEnumToPropString(pProp->Type())));
|
|
|
|
// Add uncooked notification
|
|
if (pProp->Template()->CookPreference() == eNeverCook)
|
|
{
|
|
Text.prepend("<i>[uncooked]</i>");
|
|
}
|
|
|
|
// Add description
|
|
TString Desc = pProp->Template()->Description();
|
|
if (!Desc.IsEmpty()) Text += "<br/>" + TO_QSTRING(Desc);
|
|
return Text;
|
|
}
|
|
}
|
|
|
|
if (Role == Qt::SizeHintRole)
|
|
{
|
|
return QSize(0, 23);
|
|
}
|
|
|
|
return QVariant::Invalid;
|
|
}
|
|
|
|
QModelIndex CPropertyModel::index(int Row, int Column, const QModelIndex& rkParent) const
|
|
{
|
|
// Invalid index
|
|
if (!hasIndex(Row, Column, rkParent))
|
|
return QModelIndex();
|
|
|
|
// Check property for children
|
|
IProperty *pParent = (rkParent.isValid() ? PropertyForIndex(rkParent, false) : mpBaseStruct);
|
|
|
|
// Struct
|
|
if (pParent->Type() == eStructProperty)
|
|
{
|
|
IProperty *pProp = static_cast<CPropertyStruct*>(pParent)->PropertyByIndex(Row);
|
|
return createIndex(Row, Column, pProp);
|
|
}
|
|
|
|
// Array
|
|
if (pParent->Type() == eArrayProperty)
|
|
{
|
|
IProperty *pProp = static_cast<CArrayProperty*>(pParent)->PropertyByIndex(Row);
|
|
|
|
// If this array element only has one sub-property then let's just skip the redundant tree node and show the sub-property directly.
|
|
CPropertyStruct *pStruct = static_cast<CPropertyStruct*>(pProp);
|
|
if (pStruct->Count() == 1)
|
|
pProp = pStruct->PropertyByIndex(0);
|
|
|
|
return createIndex(Row, Column, pProp);
|
|
}
|
|
|
|
// Other property
|
|
if (pParent->Type() == eColorProperty || pParent->Type() == eVector3Property || pParent->Type() == eBitfieldProperty || pParent->Type() == eCharacterProperty)
|
|
return createIndex(Row, Column, u32(pParent) | 0x1);
|
|
|
|
return QModelIndex();
|
|
}
|
|
|
|
QModelIndex CPropertyModel::parent(const QModelIndex& rkChild) const
|
|
{
|
|
// Invalid index
|
|
if (!rkChild.isValid())
|
|
return QModelIndex();
|
|
|
|
// Find parent property
|
|
IProperty *pParent;
|
|
|
|
if (rkChild.internalId() & 0x1)
|
|
pParent = PropertyForIndex(rkChild, true);
|
|
else
|
|
pParent = PropertyForIndex(rkChild, false)->Parent();
|
|
|
|
if (pParent == mpBaseStruct)
|
|
return QModelIndex();
|
|
|
|
// Iterate over grandfather properties until we find the row
|
|
CPropertyStruct *pGrandparent = pParent->Parent();
|
|
|
|
// Check for array with one sub-property
|
|
if (pGrandparent->Type() == eArrayProperty)
|
|
{
|
|
CPropertyStruct *pStruct = static_cast<CPropertyStruct*>(pParent);
|
|
|
|
if (pStruct->Count() == 1)
|
|
{
|
|
pParent = pGrandparent;
|
|
pGrandparent = pGrandparent->Parent();
|
|
}
|
|
}
|
|
|
|
for (u32 iProp = 0; iProp < pGrandparent->Count(); iProp++)
|
|
{
|
|
if (pGrandparent->PropertyByIndex(iProp) == pParent)
|
|
return createIndex(iProp, 0, pParent);
|
|
}
|
|
|
|
return QModelIndex();
|
|
}
|
|
|
|
Qt::ItemFlags CPropertyModel::flags(const QModelIndex& rkIndex) const
|
|
{
|
|
if (rkIndex.column() == 0) return Qt::ItemIsEnabled;
|
|
else return (Qt::ItemIsEnabled | Qt::ItemIsEditable);
|
|
}
|
|
|
|
void CPropertyModel::NotifyPropertyModified(IProperty *pProp)
|
|
{
|
|
NotifyPropertyModified(IndexForProperty(pProp));
|
|
}
|
|
|
|
void CPropertyModel::NotifyPropertyModified(const QModelIndex& rkIndex)
|
|
{
|
|
if (rowCount(rkIndex) != 0)
|
|
emit dataChanged( index(0, 1, rkIndex), index(rowCount(rkIndex) - 1, 1, rkIndex));
|
|
|
|
if (rkIndex.internalId() & 0x1)
|
|
{
|
|
QModelIndex Parent = rkIndex.parent();
|
|
Parent = Parent.sibling(Parent.row(), 1);
|
|
emit dataChanged(Parent, Parent);
|
|
}
|
|
|
|
QModelIndex IndexC1 = rkIndex.sibling(rkIndex.row(), 1);
|
|
emit dataChanged(IndexC1, IndexC1);
|
|
|
|
emit PropertyModified(rkIndex);
|
|
}
|
|
|
|
void CPropertyModel::ArrayAboutToBeResized(const QModelIndex& rkIndex, u32 NewSize)
|
|
{
|
|
QModelIndex Index = rkIndex.sibling(rkIndex.row(), 0);
|
|
CArrayProperty *pArray = static_cast<CArrayProperty*>(PropertyForIndex(Index, false));
|
|
|
|
if (pArray && pArray->Type() == eArrayProperty)
|
|
{
|
|
u32 OldSize = pArray->Count();
|
|
|
|
if (NewSize != OldSize)
|
|
{
|
|
if (NewSize > OldSize)
|
|
beginInsertRows(Index, OldSize, NewSize - 1);
|
|
else
|
|
beginRemoveRows(Index, NewSize, OldSize - 1);
|
|
}
|
|
}
|
|
}
|
|
|
|
void CPropertyModel::ArrayResized(const QModelIndex& rkIndex, u32 OldSize)
|
|
{
|
|
CArrayProperty *pArray = static_cast<CArrayProperty*>(PropertyForIndex(rkIndex, false));
|
|
u32 NewSize = pArray->Count();
|
|
|
|
if (NewSize != OldSize)
|
|
{
|
|
if (pArray->Count() > OldSize)
|
|
endInsertRows();
|
|
else
|
|
endRemoveRows();
|
|
}
|
|
}
|