From a90f1d044167ee4cabd19b9445ad8980b6c02e88 Mon Sep 17 00:00:00 2001 From: Aruki Date: Mon, 9 Jul 2018 06:53:56 -0600 Subject: [PATCH] Reimplemented array resizing --- src/Editor/Editor.pro | 8 +- src/Editor/PropertyEdit/CPropertyDelegate.cpp | 18 ++- src/Editor/PropertyEdit/CPropertyModel.cpp | 98 +++++++++--- src/Editor/PropertyEdit/CPropertyModel.h | 2 + .../Undo/CEditScriptPropertyCommand.cpp | 56 ------- src/Editor/Undo/CEditScriptPropertyCommand.h | 150 +----------------- src/Editor/Undo/CResizeScriptArrayCommand.cpp | 72 --------- src/Editor/Undo/CResizeScriptArrayCommand.h | 65 +++++--- src/Editor/Undo/EUndoCommand.h | 2 +- src/Editor/Undo/IEditPropertyCommand.cpp | 127 +++++++++++++++ src/Editor/Undo/IEditPropertyCommand.h | 50 ++++++ 11 files changed, 320 insertions(+), 328 deletions(-) delete mode 100644 src/Editor/Undo/CEditScriptPropertyCommand.cpp delete mode 100644 src/Editor/Undo/CResizeScriptArrayCommand.cpp create mode 100644 src/Editor/Undo/IEditPropertyCommand.cpp create mode 100644 src/Editor/Undo/IEditPropertyCommand.h diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index cdc6ce27..2f5f78e7 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -199,7 +199,8 @@ HEADERS += \ CGeneratePropertyNamesDialog.h \ CProgressBarNotifier.h \ Widgets/CCheckableTreeWidgetItem.h \ - Widgets/CCheckableTreeWidget.h + Widgets/CCheckableTreeWidget.h \ + Undo/IEditPropertyCommand.h # Source Files SOURCES += \ @@ -237,8 +238,6 @@ SOURCES += \ PropertyEdit/CPropertyDelegate.cpp \ PropertyEdit/CPropertyView.cpp \ WorldEditor/CInstancesModel.cpp \ - Undo/CEditScriptPropertyCommand.cpp \ - Undo/CResizeScriptArrayCommand.cpp \ WorldEditor/WEditorProperties.cpp \ Undo/CChangeLayerCommand.cpp \ WorldEditor/CTemplateEditDialog.cpp \ @@ -275,7 +274,8 @@ SOURCES += \ ResourceBrowser/CVirtualDirectoryModel.cpp \ ResourceBrowser/CVirtualDirectoryTreeView.cpp \ CPropertyNameValidator.cpp \ - CGeneratePropertyNamesDialog.cpp + CGeneratePropertyNamesDialog.cpp \ + Undo/IEditPropertyCommand.cpp # UI Files FORMS += \ diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.cpp b/src/Editor/PropertyEdit/CPropertyDelegate.cpp index 43990ff7..8ce9cc44 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.cpp +++ b/src/Editor/PropertyEdit/CPropertyDelegate.cpp @@ -365,7 +365,7 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo if (Type != EPropertyTypeNew::Array) { // TODO: support this for non script object properties - pCommand = new CEditScriptPropertyCommand(mpEditor, mpModel->GetScriptObject(), pProp); + pCommand = new CEditScriptPropertyCommand(mpEditor, rkIndex, mpModel); pCommand->SaveOldData(); // Handle sub-properties of flags and animation sets @@ -478,18 +478,22 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo // Array else { - //FIXME - /* + pCommand = new CResizeScriptArrayCommand(mpEditor, rkIndex, mpModel); + pCommand->SaveOldData(); + WIntegralSpinBox* pSpinBox = static_cast(pEditor); CArrayProperty* pArray = static_cast(pProp); + int OldCount = pArray->ArrayCount(pData); int NewCount = pSpinBox->value(); - if (pArray->Count() != NewCount) + if (OldCount != NewCount) { - CResizeScriptArrayCommand *pCmd = new CResizeScriptArrayCommand(pProp, mpEditor, mpModel, NewCount); - mpEditor->UndoStack()->push(pCmd); + mpModel->ArrayAboutToBeResized(rkIndex, NewCount); + pArray->Resize(pData, NewCount); + mpModel->ArrayResized(rkIndex, OldCount); } - */ + + pCommand->SaveNewData(); } } diff --git a/src/Editor/PropertyEdit/CPropertyModel.cpp b/src/Editor/PropertyEdit/CPropertyModel.cpp index c55b0694..8999d717 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.cpp +++ b/src/Editor/PropertyEdit/CPropertyModel.cpp @@ -13,13 +13,25 @@ CPropertyModel::CPropertyModel(QObject *pParent /*= 0*/) , mpPropertyData(nullptr) , mBoldModifiedProperties(true) , mShowNameValidity(false) + , mFirstUnusedID(-1) { } int CPropertyModel::RecursiveBuildArrays(IPropertyNew* pProperty, int ParentID) { - int MyID = mProperties.size(); - mProperties << SProperty(); + // Insert into an unused slot if one exists. Otherwise, append to the end of the array. + int MyID = -1; + + if (mFirstUnusedID >= 0) + { + MyID = mFirstUnusedID; + mFirstUnusedID = mProperties[MyID].ParentID; // on unused slots ParentID stores the ID of the next unused slot + } + else + { + MyID = mProperties.size(); + mProperties << SProperty(); + } mProperties[MyID].pProperty = pProperty; mProperties[MyID].ParentID = ParentID; @@ -70,6 +82,7 @@ void CPropertyModel::ConfigureIntrinsic(CGameProject* pProject, IPropertyNew* pR mProperties.clear(); mPropertyToIDMap.clear(); + mFirstUnusedID = -1; if (pRootProperty) RecursiveBuildArrays(pRootProperty, -1); @@ -532,37 +545,80 @@ void CPropertyModel::NotifyPropertyModified(const QModelIndex& rkIndex) void CPropertyModel::ArrayAboutToBeResized(const QModelIndex& rkIndex, u32 NewSize) { - //FIXME - /*QModelIndex Index = rkIndex.sibling(rkIndex.row(), 0); - CArrayProperty *pArray = static_cast(PropertyForIndex(Index, false)); + QModelIndex Index = rkIndex.sibling(rkIndex.row(), 0); + IPropertyNew* pProperty = PropertyForIndex(Index, false); + CArrayProperty* pArray = TPropCast(pProperty); + ASSERT(pArray); - if (pArray && pArray->Type() == eArrayProperty) + void* pArrayData = DataPointerForIndex(Index); + u32 OldSize = pArray->ArrayCount(pArrayData); + + if (NewSize != OldSize) { - u32 OldSize = pArray->Count(); - - if (NewSize != OldSize) - { - if (NewSize > OldSize) - beginInsertRows(Index, OldSize, NewSize - 1); - else - beginRemoveRows(Index, NewSize, OldSize - 1); - } - }*/ + if (NewSize > OldSize) + beginInsertRows(Index, OldSize, NewSize - 1); + else + beginRemoveRows(Index, NewSize, OldSize - 1); + } } void CPropertyModel::ArrayResized(const QModelIndex& rkIndex, u32 OldSize) { - //FIXME - /*CArrayProperty *pArray = static_cast(PropertyForIndex(rkIndex, false)); - u32 NewSize = pArray->Count(); + QModelIndex Index = rkIndex.sibling(rkIndex.row(), 0); + IPropertyNew* pProperty = PropertyForIndex(Index, false); + CArrayProperty* pArray = TPropCast(pProperty); + ASSERT(pArray); + + void* pArrayData = DataPointerForIndex(Index); + u32 NewSize = pArray->ArrayCount(pArrayData); if (NewSize != OldSize) { - if (pArray->Count() > OldSize) + int ID = Index.internalId(); + + if (NewSize > OldSize) + { + // add new elements + void* pOldData = mpPropertyData; + + for (u32 ElementIdx = OldSize; ElementIdx < NewSize; ElementIdx++) + { + mpPropertyData = pArray->ItemPointer(pArrayData, ElementIdx); + int NewChildID = RecursiveBuildArrays( pArray->ItemArchetype(), ID ); + mProperties[ID].ChildIDs.push_back(NewChildID); + } + + mpPropertyData = pOldData; endInsertRows(); + } else + { + // remove old elements + for (u32 ElementIdx = NewSize; ElementIdx < OldSize; ElementIdx++) + { + int ChildID = mProperties[ID].ChildIDs[ElementIdx]; + ClearSlot(ChildID); + } + + mProperties[ID].ChildIDs.resize(NewSize); endRemoveRows(); - }*/ + } + } +} + + +void CPropertyModel::ClearSlot(int ID) +{ + for (int ChildIdx = 0; ChildIdx < mProperties[ID].ChildIDs.size(); ChildIdx++) + { + ClearSlot(mProperties[ID].ChildIDs[ChildIdx]); + } + + mProperties[ID].ChildIDs.clear(); + mProperties[ID].Index = QModelIndex(); + mProperties[ID].ParentID = mFirstUnusedID; + mProperties[ID].pProperty = nullptr; + mFirstUnusedID = ID; } void CPropertyModel::SetShowPropertyNameValidity(bool Enable) diff --git a/src/Editor/PropertyEdit/CPropertyModel.h b/src/Editor/PropertyEdit/CPropertyModel.h index 52893b03..dbd63a62 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.h +++ b/src/Editor/PropertyEdit/CPropertyModel.h @@ -18,6 +18,7 @@ class CPropertyModel : public QAbstractItemModel }; QVector mProperties; QMap mPropertyToIDMap; + int mFirstUnusedID; CGameProject* mpProject; CScriptObject* mpObject; // may be null @@ -49,6 +50,7 @@ public: void ArrayAboutToBeResized(const QModelIndex& rkIndex, u32 NewSize); void ArrayResized(const QModelIndex& rkIndex, u32 OldSize); void ResizeArray(const QModelIndex& rkIndex, u32 NewSize); + void ClearSlot(int ID); void SetShowPropertyNameValidity(bool Enable); diff --git a/src/Editor/Undo/CEditScriptPropertyCommand.cpp b/src/Editor/Undo/CEditScriptPropertyCommand.cpp deleted file mode 100644 index 3a80b74c..00000000 --- a/src/Editor/Undo/CEditScriptPropertyCommand.cpp +++ /dev/null @@ -1,56 +0,0 @@ -#include "CEditScriptPropertyCommand.h" -#include "EUndoCommand.h" - -/*CEditScriptPropertyCommand::CEditScriptPropertyCommand(IProperty *pProp, CWorldEditor *pEditor, IPropertyValue *pOldValue, bool IsDone, const QString& rkCommandName /*= "Edit Property") - : IUndoCommand(rkCommandName) - , mpProp(pProp) - , mpEditor(pEditor) - , mCommandEnded(IsDone) -{ - mpOldValue = pOldValue; - mpNewValue = pProp->RawValue()->Clone(); -} - -CEditScriptPropertyCommand::~CEditScriptPropertyCommand() -{ - delete mpOldValue; - delete mpNewValue; -} - -int CEditScriptPropertyCommand::id() const -{ - return eEditScriptPropertyCmd; -} - -bool CEditScriptPropertyCommand::mergeWith(const QUndoCommand *pkOther) -{ - if (!mCommandEnded && pkOther->id() == eEditScriptPropertyCmd) - { - const CEditScriptPropertyCommand *pkCmd = static_cast(pkOther); - - if (pkCmd->mpProp == mpProp) - { - mpNewValue->Copy(pkCmd->mpNewValue); - mCommandEnded = pkCmd->mCommandEnded; - return true; - } - } - - return false; -} - -void CEditScriptPropertyCommand::undo() -{ - IProperty *pProp = *mpProp; - pProp->RawValue()->Copy(mpOldValue); - mpEditor->OnPropertyModified(pProp); - mCommandEnded = true; -} - -void CEditScriptPropertyCommand::redo() -{ - IProperty *pProp = *mpProp; - pProp->RawValue()->Copy(mpNewValue); - mpEditor->OnPropertyModified(pProp); -} -*/ diff --git a/src/Editor/Undo/CEditScriptPropertyCommand.h b/src/Editor/Undo/CEditScriptPropertyCommand.h index 8b8fc909..7368c35f 100644 --- a/src/Editor/Undo/CEditScriptPropertyCommand.h +++ b/src/Editor/Undo/CEditScriptPropertyCommand.h @@ -1,167 +1,29 @@ #ifndef CEDITSCRIPTPROPERTYCOMMAND_H #define CEDITSCRIPTPROPERTYCOMMAND_H -#include "IUndoCommand.h" +#include "IEditPropertyCommand.h" #include "ObjReferences.h" -#include "EUndoCommand.h" -#include "Editor/PropertyEdit/CPropertyModel.h" #include "Editor/WorldEditor/CWorldEditor.h" -class IEditPropertyCommand : public IUndoCommand -{ - std::vector mOldData; - std::vector mNewData; - -protected: - IPropertyNew* mpProperty; - bool mCommandEnded; - bool mSavedOldData; - bool mSavedNewData; - - /** Save the current state of the object properties to the given data buffer */ - void SaveObjectStateToArray(std::vector& rVector) - { - CVectorOutStream MemStream(&rVector, IOUtil::kSystemEndianness); - CBasicBinaryWriter Writer(&MemStream, CSerialVersion(IArchive::skCurrentArchiveVersion, 0, mpProperty->Game())); - - std::vector DataPointers; - GetObjectDataPointers(DataPointers); - - for (int PtrIdx = 0; PtrIdx < DataPointers.size(); PtrIdx++) - { - void* pData = DataPointers[PtrIdx]; - mpProperty->SerializeValue(pData, Writer); - } - } - - /** Restore the state of the object properties from the given data buffer */ - void RestoreObjectStateFromArray(std::vector& rArray) - { - CBasicBinaryReader Reader(rArray.data(), rArray.size(), CSerialVersion(IArchive::skCurrentArchiveVersion, 0, mpProperty->Game())); - - std::vector DataPointers; - GetObjectDataPointers(DataPointers); - - for (int PtrIdx = 0; PtrIdx < DataPointers.size(); PtrIdx++) - { - void* pData = DataPointers[PtrIdx]; - mpProperty->SerializeValue(pData, Reader); - } - } - -public: - IEditPropertyCommand(IPropertyNew* pProperty, const QString& rkCommandName = "Edit Property") - : IUndoCommand(rkCommandName) - , mpProperty(pProperty) - , mSavedOldData(false) - , mSavedNewData(false) - {} - - void SaveOldData() - { - SaveObjectStateToArray(mOldData); - mSavedOldData = true; - } - - void SaveNewData() - { - SaveObjectStateToArray(mNewData); - mSavedNewData = true; - } - - bool IsNewDataDifferent() - { - if (mOldData.size() != mNewData.size()) return false; - return memcmp(mOldData.data(), mNewData.data(), mNewData.size()) != 0; - } - - void SetEditComplete(bool IsComplete) - { - mCommandEnded = IsComplete; - } - - /** Interface */ - virtual void GetObjectDataPointers(std::vector& rOutPointers) const = 0; - - /** IUndoCommand/QUndoCommand interface */ - int id() const - { - return eEditScriptPropertyCmd; - } - - bool mergeWith(const QUndoCommand *pkOther) - { - if (!mCommandEnded) - { - const IEditPropertyCommand* pkCmd = dynamic_cast(pkOther); - - if (pkCmd && pkCmd->mpProperty == mpProperty) - { - std::vector MyPointers; - GetObjectDataPointers(MyPointers); - - std::vector TheirPointers; - pkCmd->GetObjectDataPointers(TheirPointers); - - if (TheirPointers.size() == MyPointers.size()) - { - for (int PtrIdx = 0; PtrIdx < MyPointers.size(); PtrIdx++) - { - if (MyPointers[PtrIdx] != TheirPointers[PtrIdx]) - return false; - } - - // Match - mNewData = pkCmd->mNewData; - mCommandEnded = pkCmd->mCommandEnded; - return true; - } - } - } - - return false; - } - - void undo() - { - ASSERT(mSavedOldData && mSavedNewData); - RestoreObjectStateFromArray(mOldData); - mCommandEnded = true; - } - - void redo() - { - ASSERT(mSavedOldData && mSavedNewData); - RestoreObjectStateFromArray(mNewData); - } - - bool AffectsCleanState() const - { - return true; - } -}; - class CEditScriptPropertyCommand : public IEditPropertyCommand { std::vector mInstances; CWorldEditor* mpEditor; public: - CEditScriptPropertyCommand(CWorldEditor* pEditor, CScriptObject* pInstance, IPropertyNew* pProperty, const QString& rkCommandName = "Edit Property") - : IEditPropertyCommand(pProperty, rkCommandName) + CEditScriptPropertyCommand(CWorldEditor* pEditor, const QModelIndex& rkIndex, CPropertyModel* pInModel, const QString& rkCommandName = "Edit Property") + : IEditPropertyCommand(rkIndex, pInModel, rkCommandName) , mpEditor(pEditor) { - mInstances.push_back( CInstancePtr(pInstance) ); + mInstances.push_back( CInstancePtr(pInModel->GetScriptObject()) ); } virtual void GetObjectDataPointers(std::vector& rOutPointers) const override { rOutPointers.resize(mInstances.size()); - for (int InstanceIdx = 0; InstanceIdx < mInstances.size(); InstanceIdx++) - { - rOutPointers[InstanceIdx] = mInstances[InstanceIdx]->PropertyData(); - } + //@todo support multiple objects + rOutPointers[0] = mpModel->DataPointerForIndex(mIndex); } virtual void undo() override diff --git a/src/Editor/Undo/CResizeScriptArrayCommand.cpp b/src/Editor/Undo/CResizeScriptArrayCommand.cpp deleted file mode 100644 index 2d0e5a38..00000000 --- a/src/Editor/Undo/CResizeScriptArrayCommand.cpp +++ /dev/null @@ -1,72 +0,0 @@ -#include "CResizeScriptArrayCommand.h" -/* -CResizeScriptArrayCommand::CResizeScriptArrayCommand(IPropertyNew *pProp, CWorldEditor *pEditor, CPropertyModel *pModel, int NewSize) - : IUndoCommand("Edit Property") - , mpEditor(pEditor) - , mpArray(pProp) - , mpModel(pModel) - , mOldSize(static_cast(pProp)->Count()) - , mNewSize(NewSize) -{ - mNewSizeLarger = mNewSize > mOldSize; - - if (!mNewSizeLarger) - { - CArrayProperty *pArray = static_cast(pProp); - - for (int iSub = mNewSize; iSub < mOldSize; iSub++) - { - mDeletedProperties << pArray->PropertyByIndex(iSub)->Clone(nullptr); - } - } -} - -CResizeScriptArrayCommand::~CResizeScriptArrayCommand() -{ - foreach (IPropertyNew *pProp, mDeletedProperties) - delete pProp; -} - -void CResizeScriptArrayCommand::undo() -{ - if (mNewSize != mOldSize) - { - // Update parents - CArrayProperty *pArray = static_cast(*mpArray); - - foreach (IProperty *pProp, mDeletedProperties) - pProp->SetParent(pArray); - - // Resize array - QModelIndex Index = mpModel->IndexForProperty(pArray); - mpModel->ArrayAboutToBeResized(Index, (u32) mOldSize); - pArray->Resize(mOldSize); - - if (!mNewSizeLarger) - { - int NumNewElements = mOldSize - mNewSize; - - for (int iSub = 0; iSub < NumNewElements; iSub++) - { - u32 Idx = iSub + mNewSize; - pArray->PropertyByIndex(Idx)->Copy(mDeletedProperties[iSub]); - } - } - - mpModel->ArrayResized(Index, (u32) mNewSize); - } -} - -void CResizeScriptArrayCommand::redo() -{ - // Whether we're increasing or decreasing in size, there's no need to restore deleted properties on redo. - if (mNewSize != mOldSize) - { - CArrayProperty *pArray = static_cast(*mpArray); - QModelIndex Index = mpModel->IndexForProperty(pArray); - mpModel->ArrayAboutToBeResized(Index, (u32) mNewSize); - pArray->Resize(mNewSize); - mpModel->ArrayResized(Index, (u32) mOldSize); - } -} -*/ diff --git a/src/Editor/Undo/CResizeScriptArrayCommand.h b/src/Editor/Undo/CResizeScriptArrayCommand.h index 06efcdc2..fb700e22 100644 --- a/src/Editor/Undo/CResizeScriptArrayCommand.h +++ b/src/Editor/Undo/CResizeScriptArrayCommand.h @@ -1,31 +1,50 @@ #ifndef CRESIZESCRIPTARRAYCOMMAND_H #define CRESIZESCRIPTARRAYCOMMAND_H -#include "IUndoCommand.h" -#include "ObjReferences.h" -#include "Editor/PropertyEdit/CPropertyModel.h" -#include "Editor/WorldEditor/CWorldEditor.h" -#include +#include "CEditScriptPropertyCommand.h" -// todo: make this more general... it shouldn't be relying on a CPropertyModel pointer -//FIXME -/*class CResizeScriptArrayCommand : public IUndoCommand +class CResizeScriptArrayCommand : public CEditScriptPropertyCommand { - CWorldEditor* mpEditor; - IPropertyNew* mpArray; - QVector mDeletedProperties; - CPropertyModel *mpModel; - - int mOldSize; - int mNewSize; - bool mNewSizeLarger; - public: - CResizeScriptArrayCommand(IPropertyNew *pProp, CWorldEditor *pEditor, CPropertyModel *pModel, int NewSize); - ~CResizeScriptArrayCommand(); - void undo(); - void redo(); - bool AffectsCleanState() const { return true; } -};*/ + CResizeScriptArrayCommand(CWorldEditor* pEditor, const QModelIndex& rkIndex, CPropertyModel* pInModel, const QString& rkCommandName = "Resize Array") + : CEditScriptPropertyCommand(pEditor, rkIndex, pInModel, rkCommandName) + {} + + bool mergeWith(const QUndoCommand *pkOther) + { + return false; + } + + // Note in some cases undo/redo may be called when the change has already been applied outside of the undo command + // This is why we need to check the array's actual current size instead of assuming it will match one of the arrays + void undo() + { + // unpleasant cast, but easiest/fastest way to access the sizes + int NewSize = *((int*)mOldData.data()); + int OldSize = CurrentArrayCount(); + + mpModel->ArrayAboutToBeResized(mIndex, NewSize); + CEditScriptPropertyCommand::undo(); + mpModel->ArrayResized(mIndex, OldSize); + } + + void redo() + { + // unpleasant cast, but easiest/fastest way to access the sizes + int NewSize = *((int*)mNewData.data()); + int OldSize = CurrentArrayCount(); + + mpModel->ArrayAboutToBeResized(mIndex, NewSize); + CEditScriptPropertyCommand::redo(); + mpModel->ArrayResized(mIndex, OldSize); + } + + int CurrentArrayCount() + { + void* pData = mpModel->DataPointerForIndex(mIndex); + CArrayProperty* pArray = TPropCast(mpProperty); + return pArray->ArrayCount(pData); + } +}; #endif // CRESIZESCRIPTARRAYCOMMAND_H diff --git a/src/Editor/Undo/EUndoCommand.h b/src/Editor/Undo/EUndoCommand.h index b33e7ed0..41f1ae25 100644 --- a/src/Editor/Undo/EUndoCommand.h +++ b/src/Editor/Undo/EUndoCommand.h @@ -8,7 +8,7 @@ enum EUndoCommand eTranslateNodeCmd, eRotateNodeCmd, eScaleNodeCmd, - eEditScriptPropertyCmd + eEditPropertyCmd }; #endif // EUNDOCOMMAND diff --git a/src/Editor/Undo/IEditPropertyCommand.cpp b/src/Editor/Undo/IEditPropertyCommand.cpp new file mode 100644 index 00000000..353c69a2 --- /dev/null +++ b/src/Editor/Undo/IEditPropertyCommand.cpp @@ -0,0 +1,127 @@ +#include "IEditPropertyCommand.h" + +/** Save the current state of the object properties to the given data buffer */ +void IEditPropertyCommand::SaveObjectStateToArray(std::vector& rVector) +{ + CVectorOutStream MemStream(&rVector, IOUtil::kSystemEndianness); + CBasicBinaryWriter Writer(&MemStream, CSerialVersion(IArchive::skCurrentArchiveVersion, 0, mpProperty->Game())); + + std::vector DataPointers; + GetObjectDataPointers(DataPointers); + + for (int PtrIdx = 0; PtrIdx < DataPointers.size(); PtrIdx++) + { + void* pData = DataPointers[PtrIdx]; + mpProperty->SerializeValue(pData, Writer); + } +} + +/** Restore the state of the object properties from the given data buffer */ +void IEditPropertyCommand::RestoreObjectStateFromArray(std::vector& rArray) +{ + CBasicBinaryReader Reader(rArray.data(), rArray.size(), CSerialVersion(IArchive::skCurrentArchiveVersion, 0, mpProperty->Game())); + + std::vector DataPointers; + GetObjectDataPointers(DataPointers); + + for (int PtrIdx = 0; PtrIdx < DataPointers.size(); PtrIdx++) + { + void* pData = DataPointers[PtrIdx]; + mpProperty->SerializeValue(pData, Reader); + } +} + +IEditPropertyCommand::IEditPropertyCommand( + const QModelIndex& rkInIndex, + CPropertyModel* pInModel, + const QString& rkCommandName /*= "Edit Property"*/ + ) + : IUndoCommand(rkCommandName) + , mIndex(rkInIndex) + , mpModel(pInModel) + , mSavedOldData(false) + , mSavedNewData(false) +{ + mpProperty = mpModel->PropertyForIndex(rkInIndex, true); + ASSERT(mpModel && mpProperty); +} + +void IEditPropertyCommand::SaveOldData() +{ + SaveObjectStateToArray(mOldData); + mSavedOldData = true; +} + +void IEditPropertyCommand::SaveNewData() +{ + SaveObjectStateToArray(mNewData); + mSavedNewData = true; +} + +bool IEditPropertyCommand::IsNewDataDifferent() +{ + if (mOldData.size() != mNewData.size()) return true; + return memcmp(mOldData.data(), mNewData.data(), mNewData.size()) != 0; +} + +void IEditPropertyCommand::SetEditComplete(bool IsComplete) +{ + mCommandEnded = IsComplete; +} + +/** IUndoCommand/QUndoCommand interface */ +int IEditPropertyCommand::id() const +{ + return eEditPropertyCmd; +} + +bool IEditPropertyCommand::mergeWith(const QUndoCommand *pkOther) +{ + if (!mCommandEnded) + { + const IEditPropertyCommand* pkCmd = dynamic_cast(pkOther); + + if (pkCmd && pkCmd->mIndex == mIndex && pkCmd->mpProperty == mpProperty) + { + std::vector MyPointers; + GetObjectDataPointers(MyPointers); + + std::vector TheirPointers; + pkCmd->GetObjectDataPointers(TheirPointers); + + if (TheirPointers.size() == MyPointers.size()) + { + for (int PtrIdx = 0; PtrIdx < MyPointers.size(); PtrIdx++) + { + if (MyPointers[PtrIdx] != TheirPointers[PtrIdx]) + return false; + } + + // Match + mNewData = pkCmd->mNewData; + mCommandEnded = pkCmd->mCommandEnded; + return true; + } + } + } + + return false; +} + +void IEditPropertyCommand::undo() +{ + ASSERT(mSavedOldData && mSavedNewData); + RestoreObjectStateFromArray(mOldData); + mCommandEnded = true; +} + +void IEditPropertyCommand::redo() +{ + ASSERT(mSavedOldData && mSavedNewData); + RestoreObjectStateFromArray(mNewData); +} + +bool IEditPropertyCommand::AffectsCleanState() const +{ + return true; +} diff --git a/src/Editor/Undo/IEditPropertyCommand.h b/src/Editor/Undo/IEditPropertyCommand.h new file mode 100644 index 00000000..63793b30 --- /dev/null +++ b/src/Editor/Undo/IEditPropertyCommand.h @@ -0,0 +1,50 @@ +#ifndef IEDITPROPERTYCOMMAND_H +#define IEDITPROPERTYCOMMAND_H + +#include "IUndoCommand.h" +#include "EUndoCommand.h" +#include "Editor/PropertyEdit/CPropertyModel.h" + +class IEditPropertyCommand : public IUndoCommand +{ +protected: + std::vector mOldData; + std::vector mNewData; + + QModelIndex mIndex; + CPropertyModel* mpModel; + IPropertyNew* mpProperty; + bool mCommandEnded; + bool mSavedOldData; + bool mSavedNewData; + + /** Save the current state of the object properties to the given data buffer */ + void SaveObjectStateToArray(std::vector& rVector); + + /** Restore the state of the object properties from the given data buffer */ + void RestoreObjectStateFromArray(std::vector& rArray); + +public: + IEditPropertyCommand( + const QModelIndex& rkInIndex, + CPropertyModel* pInModel, + const QString& rkCommandName = "Edit Property" + ); + + void SaveOldData(); + void SaveNewData(); + bool IsNewDataDifferent(); + void SetEditComplete(bool IsComplete); + + /** Interface */ + virtual void GetObjectDataPointers(std::vector& rOutPointers) const = 0; + + /** IUndoCommand/QUndoCommand interface */ + int id() const; + bool mergeWith(const QUndoCommand *pkOther); + void undo(); + void redo(); + bool AffectsCleanState() const; +}; + +#endif // IEDITPROPERTYCOMMAND_H