diff --git a/src/Core/Resource/CAnimationParameters.h b/src/Core/Resource/CAnimationParameters.h index d225fa0a..762022b4 100644 --- a/src/Core/Resource/CAnimationParameters.h +++ b/src/Core/Resource/CAnimationParameters.h @@ -33,6 +33,18 @@ public: void SetResource(CResource *pRes); void SetNodeIndex(u32 Index); void SetUnknown(u32 Index, u32 Value); + + // Operators + inline bool operator==(const CAnimationParameters& rkOther) const + { + return ( (mGame == rkOther.mGame) && + (mpCharSet == rkOther.mpCharSet) && + (mNodeIndex == rkOther.mNodeIndex) && + (mUnknown1 == rkOther.mUnknown1) && + (mUnknown2 == rkOther.mUnknown2) && + (mUnknown3 == rkOther.mUnknown3) && + (mUnknown4 == rkOther.mUnknown4) ); + } }; #endif // CANIMATIONPARAMETERS_H diff --git a/src/Core/Resource/CResCache.cpp b/src/Core/Resource/CResCache.cpp index 031d11ae..e5c4362a 100644 --- a/src/Core/Resource/CResCache.cpp +++ b/src/Core/Resource/CResCache.cpp @@ -196,6 +196,7 @@ CResource* CResCache::GetResource(const TString& ResPath) else if (type == "CHAR") Res = CAnimSetLoader::LoadCHAR(file); else if (type == "MREA") Res = CAreaLoader::LoadMREA(file); else if (type == "MLVL") Res = CWorldLoader::LoadMLVL(file); + else if (type == "STRG") Res = CStringLoader::LoadSTRG(file); else if (type == "FONT") Res = CFontLoader::LoadFONT(file); else if (type == "SCAN") Res = CScanLoader::LoadSCAN(file); else if (type == "DCLN") Res = CCollisionLoader::LoadDCLN(file); diff --git a/src/Core/Resource/CResourceInfo.h b/src/Core/Resource/CResourceInfo.h index 3441bc3e..b4238dc9 100644 --- a/src/Core/Resource/CResourceInfo.h +++ b/src/Core/Resource/CResourceInfo.h @@ -31,10 +31,18 @@ public: inline CUniqueID ID() const { + TString FileName = mPath.GetFileName(); + if (!mIsPath) - return CUniqueID::FromString(mPath.GetFileName()); + return CUniqueID::FromString(FileName); + else - return CUniqueID::skInvalidID64; + { + if (FileName.IsHexString() && (FileName.Size() == 8 || FileName.Size() == 16)) + return CUniqueID::FromString(FileName); + else + return CUniqueID::skInvalidID64; + } } inline CFourCC Type() const @@ -64,6 +72,16 @@ public: else return mIsValidPath; } + + inline bool operator==(const CResourceInfo& rkOther) const + { + return (mPath.GetFileName() == rkOther.mPath.GetFileName()); + } + + inline bool operator!=(const CResourceInfo& rkOther) const + { + return (!(*this == rkOther)); + } }; #endif // CRESOURCEINFO diff --git a/src/Core/Resource/Factory/CScriptLoader.cpp b/src/Core/Resource/Factory/CScriptLoader.cpp index f0e9bf27..c60a579b 100644 --- a/src/Core/Resource/Factory/CScriptLoader.cpp +++ b/src/Core/Resource/Factory/CScriptLoader.cpp @@ -99,7 +99,7 @@ void CScriptLoader::ReadProperty(IProperty *pProp, u32 Size, IInputStream& SCLY) CUniqueID ResID = (mVersion < eCorruptionProto ? SCLY.ReadLong() : SCLY.ReadLongLong()); const TStringList& rkExtensions = static_cast(pTemp)->Extensions(); - CResourceInfo Info(ResID, "UNKN"); + CResourceInfo Info(ResID, CFourCC(!rkExtensions.empty() ? rkExtensions.front() : "UNKN")); if (ResID.IsValid()) { diff --git a/src/Core/Resource/Script/IProperty.h b/src/Core/Resource/Script/IProperty.h index 0ed11a74..ad97f9a2 100644 --- a/src/Core/Resource/Script/IProperty.h +++ b/src/Core/Resource/Script/IProperty.h @@ -40,6 +40,7 @@ public: virtual ~IProperty() {} virtual EPropertyType Type() const = 0; virtual TString ToString() const { return ""; } + virtual IPropertyValue* RawValue() { return nullptr; } inline CPropertyStruct* Parent() const { return mpParent; } @@ -68,6 +69,7 @@ public: ~TTypedProperty() {} virtual EPropertyType Type() const { return TypeEnum; } virtual TString ToString() const { return mValue.ToString(); } + virtual IPropertyValue* RawValue() { return &mValue; } inline PropType Get() const { return mValue.Get(); } inline void Set(PropType v) { mValue.Set(v); } @@ -77,7 +79,7 @@ typedef TTypedProperty typedef TTypedProperty TShortProperty; typedef TTypedProperty TLongProperty; typedef TTypedProperty TEnumProperty; -typedef TTypedProperty TBitfieldProperty; +typedef TTypedProperty TBitfieldProperty; typedef TTypedProperty TFloatProperty; typedef TTypedProperty TStringProperty; typedef TTypedProperty TVector3Property; diff --git a/src/Core/Resource/Script/IPropertyTemplate.h b/src/Core/Resource/Script/IPropertyTemplate.h index 77c03ab7..5e8bd99c 100644 --- a/src/Core/Resource/Script/IPropertyTemplate.h +++ b/src/Core/Resource/Script/IPropertyTemplate.h @@ -338,13 +338,11 @@ public: CEnumTemplate(u32 ID, CStructTemplate *pParent = 0) : TLongTemplate(ID, pParent) { - mDefaultValue.SetHexStringOutput(true); } CEnumTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CStructTemplate *pParent = 0) : TLongTemplate(ID, rkName, CookPreference, pParent) { - mDefaultValue.SetHexStringOutput(true); } virtual EPropertyType Type() const { return eEnumProperty; } @@ -393,7 +391,7 @@ public: // CBitfieldTemplate - Property template for bitfields, which can have multiple // distinct boolean parameters packed into one property. -class CBitfieldTemplate : public TLongTemplate +class CBitfieldTemplate : public TTypedPropertyTemplate { friend class CTemplateLoader; friend class CTemplateWriter; @@ -411,15 +409,13 @@ class CBitfieldTemplate : public TLongTemplate public: CBitfieldTemplate(u32 ID, CStructTemplate *pParent = 0) - : TLongTemplate(ID, pParent) + : TTypedPropertyTemplate(ID, pParent) { - mDefaultValue.SetHexStringOutput(true); } CBitfieldTemplate(u32 ID, const TString& rkName, ECookPreference CookPreference, CStructTemplate *pParent = 0) - : TLongTemplate(ID, rkName, CookPreference, pParent) + : TTypedPropertyTemplate(ID, rkName, CookPreference, pParent) { - mDefaultValue.SetHexStringOutput(true); } virtual EPropertyType Type() const { return eBitfieldProperty; } diff --git a/src/Core/Resource/Script/IPropertyValue.h b/src/Core/Resource/Script/IPropertyValue.h index 12be6c65..8f8b96ff 100644 --- a/src/Core/Resource/Script/IPropertyValue.h +++ b/src/Core/Resource/Script/IPropertyValue.h @@ -17,6 +17,9 @@ class IPropertyValue public: virtual TString ToString() const = 0; virtual void FromString(const TString& rkString) = 0; + virtual IPropertyValue* Clone() const = 0; + virtual void Copy(IPropertyValue *pValue) = 0; + virtual bool Matches(IPropertyValue *pValue) const = 0; }; template @@ -28,8 +31,20 @@ protected: public: TTypedPropertyValue() {} - TTypedPropertyValue(PropType Val) - : mValue(Val) {} + TTypedPropertyValue(PropType rkVal) + : mValue(rkVal) {} + + virtual void Copy(IPropertyValue *pValue) + { + TTypedPropertyValue *pOther = static_cast(pValue); + mValue = pOther->mValue; + } + + virtual bool Matches(IPropertyValue *pValue) const + { + TTypedPropertyValue *pOther = static_cast(pValue); + return (mValue == pOther->mValue); + } PropType Get() const { @@ -69,6 +84,11 @@ public: { mValue = (rkString == "true"); } + + IPropertyValue* Clone() const + { + return new CBoolValue(mValue); + } }; class CByteValue : public TTypedPropertyValue @@ -87,6 +107,11 @@ public: u32 base = (rkString.StartsWith("0x") ? 16 : 10); mValue = (s8) rkString.ToInt32(base); } + + IPropertyValue* Clone() const + { + return new CByteValue(mValue); + } }; class CShortValue : public TTypedPropertyValue @@ -105,28 +130,22 @@ public: u32 base = (rkString.StartsWith("0x") ? 16 : 10); mValue = (s16) rkString.ToInt32(base); } + + IPropertyValue* Clone() const + { + return new CShortValue(mValue); + } }; class CLongValue : public TTypedPropertyValue { -protected: - bool mShouldOutputHex; - public: - CLongValue() { mShouldOutputHex = false; mValue = 0; } - CLongValue(s32 Val) { mShouldOutputHex = false; mValue = Val; } - - void SetHexStringOutput(bool enable) - { - mShouldOutputHex = enable; - } + CLongValue() { mValue = 0; } + CLongValue(s32 Val) { mValue = Val; } TString ToString() const { - if (mShouldOutputHex) - return TString::HexString((u32) mValue, true, true, 8); - else - return TString::FromInt32(mValue, 0, 10); + return TString::FromInt32(mValue, 0, 10); } void FromString(const TString& rkString) @@ -134,6 +153,34 @@ public: u32 base = (rkString.StartsWith("0x") ? 16 : 10); mValue = (s32) rkString.ToInt32(base); } + + IPropertyValue* Clone() const + { + return new CLongValue(mValue); + } +}; + +class CHexLongValue : public TTypedPropertyValue +{ +public: + CHexLongValue() { mValue = 0; } + CHexLongValue(u32 Val) { mValue = Val; } + + TString ToString() const + { + return TString::HexString(mValue, true, true, 8); + } + + void FromString(const TString& rkString) + { + u32 base = (rkString.StartsWith("0x") ? 16 : 10); + mValue = (s32) rkString.ToInt32(base); + } + + IPropertyValue* Clone() const + { + return new CHexLongValue(mValue); + } }; class CFloatValue : public TTypedPropertyValue @@ -151,6 +198,11 @@ public: { mValue = rkString.ToFloat(); } + + IPropertyValue* Clone() const + { + return new CFloatValue(mValue); + } }; class CStringValue : public TTypedPropertyValue @@ -169,6 +221,11 @@ public: { mValue = rkString; } + + IPropertyValue* Clone() const + { + return new CStringValue(mValue); + } }; class CColorValue : public TTypedPropertyValue @@ -207,6 +264,11 @@ public: pPtr++; } } + + IPropertyValue* Clone() const + { + return new CColorValue(mValue); + } }; class CVector3Value : public TTypedPropertyValue @@ -243,15 +305,26 @@ public: pPtr++; } } + + IPropertyValue* Clone() const + { + return new CVector3Value(mValue); + } }; class CCharacterValue : public TTypedPropertyValue { public: CCharacterValue() {} + CCharacterValue(const CAnimationParameters& rkParams) { mValue = rkParams; } TString ToString() const { return ""; } void FromString(const TString&) { } + + IPropertyValue* Clone() const + { + return new CCharacterValue(mValue); + } }; class CFileValue : public TTypedPropertyValue @@ -262,15 +335,26 @@ public: TString ToString() const { return ""; } void FromString(const TString&) { } + + IPropertyValue* Clone() const + { + return new CFileValue(mValue); + } }; class CUnknownValue : public TTypedPropertyValue> { public: CUnknownValue(); + CUnknownValue(const std::vector& rkVec) { mValue = rkVec; } TString ToString() const { return ""; } void FromString(const TString&) {} + + IPropertyValue* Clone() const + { + return new CUnknownValue(mValue); + } }; #endif // IPROPERTYVALUE_H diff --git a/src/Editor/Editor.pro b/src/Editor/Editor.pro index 6046dd21..80d049ea 100644 --- a/src/Editor/Editor.pro +++ b/src/Editor/Editor.pro @@ -133,7 +133,8 @@ HEADERS += \ PropertyEdit/CPropertyView.h \ PropertyEdit/CPropertyRelay.h \ WorldEditor/CInstancesProxyModel.h \ - WorldEditor/CInstancesModel.h + WorldEditor/CInstancesModel.h \ + Undo/CEditScriptPropertyCommand.h # Source Files SOURCES += \ @@ -184,7 +185,8 @@ SOURCES += \ PropertyEdit/CPropertyModel.cpp \ PropertyEdit/CPropertyDelegate.cpp \ PropertyEdit/CPropertyView.cpp \ - WorldEditor/CInstancesModel.cpp + WorldEditor/CInstancesModel.cpp \ + Undo/CEditScriptPropertyCommand.cpp # UI Files FORMS += \ diff --git a/src/Editor/INodeEditor.cpp b/src/Editor/INodeEditor.cpp index c3c1247b..7ce4ef6d 100644 --- a/src/Editor/INodeEditor.cpp +++ b/src/Editor/INodeEditor.cpp @@ -233,6 +233,7 @@ void INodeEditor::ExitPickMode() void INodeEditor::NotifySelectionModified() { + RecalculateSelectionBounds(); UpdateSelectionUI(); emit SelectionModified(); } diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.cpp b/src/Editor/PropertyEdit/CPropertyDelegate.cpp index 94325ec6..6a7f0d16 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.cpp +++ b/src/Editor/PropertyEdit/CPropertyDelegate.cpp @@ -2,6 +2,7 @@ #include "CPropertyRelay.h" #include "Editor/UICommon.h" +#include "Editor/Undo/CEditScriptPropertyCommand.h" #include "Editor/Widgets/WColorPicker.h" #include "Editor/Widgets/WDraggableSpinBox.h" #include "Editor/Widgets/WIntegralSpinBox.h" @@ -23,16 +24,24 @@ CPropertyDelegate::CPropertyDelegate(QObject *pParent /*= 0*/) : QStyledItemDelegate(pParent) + , mpEditor(nullptr) , mpModel(nullptr) + , mInRelayWidgetEdit(false) + , mEditInProgress(false) , mRelaysBlocked(false) { } -void CPropertyDelegate::SetModel(CPropertyModel *pModel) +void CPropertyDelegate::SetPropertyModel(CPropertyModel *pModel) { mpModel = pModel; } +void CPropertyDelegate::SetEditor(CWorldEditor *pEditor) +{ + mpEditor = pEditor; +} + QWidget* CPropertyDelegate::createEditor(QWidget *pParent, const QStyleOptionViewItem& /*rkOption*/, const QModelIndex& rkIndex) const { if (!mpModel) return nullptr; @@ -116,6 +125,7 @@ QWidget* CPropertyDelegate::createEditor(QWidget *pParent, const QStyleOptionVie WResourceSelector *pSelector = new WResourceSelector(pParent); CFileTemplate *pTemp = static_cast(pProp->Template()); pSelector->SetAllowedExtensions(pTemp->Extensions()); + pSelector->setFont(qobject_cast(parent())->font()); // bit of a hack to stop the resource selector font from changing CONNECT_RELAY(pSelector, rkIndex, ResourceChanged(QString)) pOut = pSelector; @@ -191,90 +201,92 @@ void CPropertyDelegate::setEditorData(QWidget *pEditor, const QModelIndex &rkInd if (pProp) { - switch (pProp->Type()) + if (!mEditInProgress) { + switch (pProp->Type()) + { - case eBoolProperty: - { - QCheckBox *pCheckBox = static_cast(pEditor); - TBoolProperty *pBool = static_cast(pProp); - pCheckBox->setChecked(pBool->Get()); - break; - } + case eBoolProperty: + { + QCheckBox *pCheckBox = static_cast(pEditor); + TBoolProperty *pBool = static_cast(pProp); + pCheckBox->setChecked(pBool->Get()); + break; + } - case eShortProperty: - { - WIntegralSpinBox *pSpinBox = static_cast(pEditor); - TShortProperty *pShort = static_cast(pProp); - pSpinBox->setValue(pShort->Get()); - break; - } + case eShortProperty: + { + WIntegralSpinBox *pSpinBox = static_cast(pEditor); + TShortProperty *pShort = static_cast(pProp); + pSpinBox->setValue(pShort->Get()); + break; + } + case eLongProperty: + { + WIntegralSpinBox *pSpinBox = static_cast(pEditor); + TLongProperty *pLong = static_cast(pProp); + pSpinBox->setValue(pLong->Get()); + break; + } - case eLongProperty: - { - WIntegralSpinBox *pSpinBox = static_cast(pEditor); - TLongProperty *pLong = static_cast(pProp); - pSpinBox->setValue(pLong->Get()); - break; - } + case eFloatProperty: + { + WDraggableSpinBox *pSpinBox = static_cast(pEditor); + TFloatProperty *pFloat = static_cast(pProp); + pSpinBox->setValue(pFloat->Get()); + break; + } - case eFloatProperty: - { - WDraggableSpinBox *pSpinBox = static_cast(pEditor); - TFloatProperty *pFloat = static_cast(pProp); - pSpinBox->setValue(pFloat->Get()); - break; - } + case eColorProperty: + { + WColorPicker *pColorPicker = static_cast(pEditor); + TColorProperty *pColor = static_cast(pProp); - case eColorProperty: - { - WColorPicker *pColorPicker = static_cast(pEditor); - TColorProperty *pColor = static_cast(pProp); + CColor SrcColor = pColor->Get(); + QColor Color; + Color.setRed(SrcColor.r * 255); + Color.setGreen(SrcColor.g * 255); + Color.setBlue(SrcColor.b * 255); + Color.setAlpha(SrcColor.a * 255); - CColor SrcColor = pColor->Get(); - QColor Color; - Color.setRed(SrcColor.r * 255); - Color.setGreen(SrcColor.g * 255); - Color.setBlue(SrcColor.b * 255); - Color.setAlpha(SrcColor.a * 255); + pColorPicker->setColor(Color); + break; + } - pColorPicker->setColor(Color); - break; - } + case eStringProperty: + { + QLineEdit *pLineEdit = static_cast(pEditor); + TStringProperty *pString = static_cast(pProp); + pLineEdit->setText(TO_QSTRING(pString->Get())); + break; + } - case eStringProperty: - { - QLineEdit *pLineEdit = static_cast(pEditor); - TStringProperty *pString = static_cast(pProp); - pLineEdit->setText(TO_QSTRING(pString->Get())); - break; - } + case eEnumProperty: + { + QComboBox *pComboBox = static_cast(pEditor); + TEnumProperty *pEnum = static_cast(pProp); + pComboBox->setCurrentIndex(pEnum->Get()); + break; + } - case eEnumProperty: - { - QComboBox *pComboBox = static_cast(pEditor); - TEnumProperty *pEnum = static_cast(pProp); - pComboBox->setCurrentIndex(pEnum->Get()); - break; - } + case eFileProperty: + { + WResourceSelector *pSelector = static_cast(pEditor); + TFileProperty *pFile = static_cast(pProp); + pSelector->SetResource(pFile->Get()); + break; + } - case eFileProperty: - { - WResourceSelector *pSelector = static_cast(pEditor); - TFileProperty *pFile = static_cast(pProp); - pSelector->SetResource(pFile->Get()); - break; - } - - case eArrayProperty: - { - WIntegralSpinBox *pSpinBox = static_cast(pEditor); - CArrayProperty *pArray = static_cast(pProp); - pSpinBox->setValue(pArray->Count()); - break; - } + case eArrayProperty: + { + WIntegralSpinBox *pSpinBox = static_cast(pEditor); + CArrayProperty *pArray = static_cast(pProp); + pSpinBox->setValue(pArray->Count()); + break; + } + } } } @@ -335,9 +347,12 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo if (!pEditor) return; IProperty *pProp = mpModel->PropertyForIndex(rkIndex, false); + IPropertyValue *pOldValue = nullptr; if (pProp) { + pOldValue = pProp->RawValue()->Clone(); + switch (pProp->Type()) { @@ -385,9 +400,6 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo Color.b = SrcColor.blue() / 255.f; Color.a = SrcColor.alpha() / 255.f; pColor->Set(Color); - - // Make sure sub-properties update with the new color - mpModel->UpdateSubProperties(rkIndex); break; } @@ -430,6 +442,7 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo else if (rkIndex.internalId() & 0x1) { pProp = mpModel->PropertyForIndex(rkIndex, true); + pOldValue = pProp->RawValue()->Clone(); if (pProp->Type() == eCharacterProperty) SetCharacterModelData(pEditor, rkIndex); @@ -474,13 +487,33 @@ void CPropertyDelegate::setModelData(QWidget *pEditor, QAbstractItemModel* /*pMo pColor->Set(Value); } - - QModelIndex ParentWidgetIndex = mpModel->index(rkIndex.parent().row(), 1, rkIndex.parent().parent()); - mpModel->dataChanged(ParentWidgetIndex, ParentWidgetIndex); } } - emit PropertyEdited(rkIndex, true); + if (pProp) + { + // Check for edit in progress + bool Matches = pOldValue->Matches(pProp->RawValue()); + + if (!Matches && mInRelayWidgetEdit && pEditor->hasFocus()) + mEditInProgress = true; + + bool EditInProgress = mEditInProgress; + + // Check for edit finished + if (!mInRelayWidgetEdit || !pEditor->hasFocus()) + mEditInProgress = false; + + // Create undo command + if (!Matches || EditInProgress) + { + CEditScriptPropertyCommand *pCommand = new CEditScriptPropertyCommand(mpModel, rkIndex, pOldValue, !mEditInProgress); + mpEditor->UndoStack()->push(pCommand); + } + + else + delete pOldValue; + } } bool CPropertyDelegate::eventFilter(QObject *pObject, QEvent *pEvent) @@ -513,6 +546,7 @@ QWidget* CPropertyDelegate::CreateCharacterEditor(QWidget *pParent, const QModel if (Type == eFileProperty) { WResourceSelector *pSelector = new WResourceSelector(pParent); + pSelector->setFont(qobject_cast(parent())->font()); // hack to keep the selector font from changing if (Params.Version() <= eEchoes) pSelector->SetAllowedExtensions("ANCS"); @@ -636,6 +670,10 @@ void CPropertyDelegate::WidgetEdited(QWidget *pWidget, const QModelIndex& rkInde { // This slot is used to update property values as they're being updated so changes can be // reflected in realtime in other parts of the application. + mInRelayWidgetEdit = true; + if (!mRelaysBlocked) setModelData(pWidget, mpModel, rkIndex); + + mInRelayWidgetEdit = false; } diff --git a/src/Editor/PropertyEdit/CPropertyDelegate.h b/src/Editor/PropertyEdit/CPropertyDelegate.h index 8f9b16c9..867b8517 100644 --- a/src/Editor/PropertyEdit/CPropertyDelegate.h +++ b/src/Editor/PropertyEdit/CPropertyDelegate.h @@ -1,19 +1,25 @@ #ifndef CPROPERTYDELEGATE_H #define CPROPERTYDELEGATE_H -#include #include "CPropertyModel.h" +#include "Editor/WorldEditor/CWorldEditor.h" +#include class CPropertyDelegate : public QStyledItemDelegate { Q_OBJECT + CWorldEditor *mpEditor; CPropertyModel *mpModel; + bool mInRelayWidgetEdit; + mutable bool mEditInProgress; mutable bool mRelaysBlocked; public: CPropertyDelegate(QObject *pParent = 0); - void SetModel(CPropertyModel *pModel); + void SetPropertyModel(CPropertyModel *pModel); + void SetEditor(CWorldEditor *pEditor); + virtual QWidget* createEditor(QWidget *pParent, const QStyleOptionViewItem& rkOption, const QModelIndex &rkIndex) const; virtual void setEditorData(QWidget *pEditor, const QModelIndex &rkIndex) const; virtual void setModelData(QWidget *pEditor, QAbstractItemModel *pModel, const QModelIndex &rkIndex) const; @@ -29,9 +35,6 @@ public slots: protected: void BlockRelays(bool Block) const { mRelaysBlocked = Block; } - -signals: - void PropertyEdited(const QModelIndex& rkIndex, bool IsDone) const; }; #endif // CPROPERTYDELEGATE_H diff --git a/src/Editor/PropertyEdit/CPropertyModel.cpp b/src/Editor/PropertyEdit/CPropertyModel.cpp index be11d721..9af5b3c7 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.cpp +++ b/src/Editor/PropertyEdit/CPropertyModel.cpp @@ -292,7 +292,7 @@ QVariant CPropertyModel::data(const QModelIndex& rkIndex, int Role) const // Add uncooked notification if (pProp->Template()->CookPreference() == eNeverCook) { - Text += "
This is an uncooked property."; + Text.prepend("[uncooked]"); } // Add description @@ -393,19 +393,20 @@ Qt::ItemFlags CPropertyModel::flags(const QModelIndex& rkIndex) const else return (Qt::ItemIsEnabled | Qt::ItemIsEditable); } -void CPropertyModel::UpdateSubProperties(const QModelIndex& rkIndex) +void CPropertyModel::NotifyPropertyModified(const QModelIndex& rkIndex) { - IProperty *pProp = PropertyForIndex(rkIndex, false); + if (rowCount(rkIndex) != 0) + emit dataChanged( index(0, 1, rkIndex), index(rowCount(rkIndex) - 1, 1, rkIndex)); - if (pProp) + if (rkIndex.internalId() & 0x1) { - QVector Roles(Qt::DisplayRole); - - if (pProp->Type() == eVector3Property) - emit dataChanged( index(0, 1, rkIndex), index(2, 1, rkIndex), Roles); - else if (pProp->Type() == eColorProperty) - emit dataChanged( index(0, 1, rkIndex), index(3, 1, rkIndex), Roles); + QModelIndex Parent = rkIndex.parent(); + Parent = Parent.sibling(Parent.row(), 1); + emit dataChanged(Parent, Parent); } + + emit dataChanged(rkIndex, rkIndex); + emit PropertyModified(rkIndex); } void CPropertyModel::ResizeArray(const QModelIndex& rkIndex, u32 NewSize) diff --git a/src/Editor/PropertyEdit/CPropertyModel.h b/src/Editor/PropertyEdit/CPropertyModel.h index f6da0147..e672511d 100644 --- a/src/Editor/PropertyEdit/CPropertyModel.h +++ b/src/Editor/PropertyEdit/CPropertyModel.h @@ -22,8 +22,12 @@ public: QModelIndex index(int Row, int Column, const QModelIndex& rkParent) const; QModelIndex parent(const QModelIndex& rkChild) const; Qt::ItemFlags flags(const QModelIndex& rkIndex) const; - void UpdateSubProperties(const QModelIndex& rkIndex); + + void NotifyPropertyModified(const QModelIndex& rkIndex); void ResizeArray(const QModelIndex& rkIndex, u32 NewSize); + +signals: + void PropertyModified(const QModelIndex& rkIndex); }; #endif // CPROPERTYMODEL_H diff --git a/src/Editor/PropertyEdit/CPropertyView.cpp b/src/Editor/PropertyEdit/CPropertyView.cpp index afa229fd..b61e17a0 100644 --- a/src/Editor/PropertyEdit/CPropertyView.cpp +++ b/src/Editor/PropertyEdit/CPropertyView.cpp @@ -14,16 +14,16 @@ CPropertyView::CPropertyView(QWidget *pParent) setEditTriggers(AllEditTriggers); setModel(mpModel); - connect(mpModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(SetPersistentEditors(QModelIndex))); connect(this, SIGNAL(expanded(QModelIndex)), this, SLOT(SetPersistentEditors(QModelIndex))); - connect(mpDelegate, SIGNAL(PropertyEdited(QModelIndex,bool)), this, SLOT(OnPropertyModified(QModelIndex,bool))); + connect(mpModel, SIGNAL(rowsInserted(QModelIndex,int,int)), this, SLOT(SetPersistentEditors(QModelIndex))); + connect(mpModel, SIGNAL(PropertyModified(QModelIndex)), this, SLOT(OnPropertyModified(QModelIndex))); } void CPropertyView::setModel(QAbstractItemModel *pModel) { CPropertyModel *pPropModel = qobject_cast(pModel); mpModel = pPropModel; - mpDelegate->SetModel(pPropModel); + mpDelegate->SetPropertyModel(pPropModel); QTreeView::setModel(pPropModel); if (pPropModel) @@ -61,7 +61,13 @@ bool CPropertyView::event(QEvent *pEvent) else return QTreeView::event(pEvent); } -void CPropertyView::SetObject(CScriptObject *pObj) +void CPropertyView::SetEditor(CWorldEditor *pEditor) +{ + mpEditor = pEditor; + mpDelegate->SetEditor(pEditor); +} + +void CPropertyView::SetInstance(CScriptObject *pObj) { mpObject = pObj; mpModel->SetBaseStruct(pObj ? pObj->Properties() : nullptr); @@ -168,23 +174,21 @@ void CPropertyView::ClosePersistentEditors(const QModelIndex& rkIndex) } } -void CPropertyView::OnPropertyModified(const QModelIndex &rkIndex, bool IsDone) +void CPropertyView::OnPropertyModified(const QModelIndex& rkIndex) { - // Check for a resource being changed on a character property. If that's the case we need to remake the persistent editors. - if (rkIndex.internalId() & 0x1) - { - if (mpModel->PropertyForIndex(rkIndex, true)->Type() == eCharacterProperty) - { - EGame Game = static_cast(mpModel->PropertyForIndex(rkIndex, true))->Get().Version(); + // Check for a character resource being changed. If that's the case we need to remake the persistent editors. + IProperty *pProp = mpModel->PropertyForIndex(rkIndex, true); - if (mpDelegate->DetermineCharacterPropType(Game, rkIndex) == eFileProperty) - { - QModelIndex Parent = rkIndex.parent(); - ClosePersistentEditors(Parent); - SetPersistentEditors(Parent); - } + if (pProp->Type() == eCharacterProperty && rkIndex.internalId() & 0x1) + { + EGame Game = static_cast(pProp)->Get().Version(); + + if (mpDelegate->DetermineCharacterPropType(Game, rkIndex) == eFileProperty) + { + ClosePersistentEditors(rkIndex.parent()); + SetPersistentEditors(rkIndex.parent()); } } - emit PropertyModified(rkIndex, IsDone); + emit PropertyModified(pProp); } diff --git a/src/Editor/PropertyEdit/CPropertyView.h b/src/Editor/PropertyEdit/CPropertyView.h index 1721ed93..7ead912e 100644 --- a/src/Editor/PropertyEdit/CPropertyView.h +++ b/src/Editor/PropertyEdit/CPropertyView.h @@ -10,6 +10,7 @@ class CPropertyView : public QTreeView { Q_OBJECT + CWorldEditor *mpEditor; CPropertyModel *mpModel; CPropertyDelegate *mpDelegate; CScriptObject *mpObject; @@ -18,7 +19,8 @@ public: CPropertyView(QWidget *pParent = 0); void setModel(QAbstractItemModel *pModel); bool event(QEvent *pEvent); - void SetObject(CScriptObject *pObj); + void SetEditor(CWorldEditor *pEditor); + void SetInstance(CScriptObject *pObj); void UpdateEditorProperties(const QModelIndex& rkParent); inline CPropertyModel* PropertyModel() const { return mpModel; } @@ -26,10 +28,10 @@ public: public slots: void SetPersistentEditors(const QModelIndex& rkIndex); void ClosePersistentEditors(const QModelIndex& rkIndex); - void OnPropertyModified(const QModelIndex& rkIndex, bool IsDone); + void OnPropertyModified(const QModelIndex& rkIndex); signals: - void PropertyModified(const QModelIndex &rkIndex, bool IsDone); + void PropertyModified(IProperty *pProperty); }; #endif // CPROPERTYVIEW_H diff --git a/src/Editor/Undo/CEditScriptPropertyCommand.cpp b/src/Editor/Undo/CEditScriptPropertyCommand.cpp new file mode 100644 index 00000000..24624351 --- /dev/null +++ b/src/Editor/Undo/CEditScriptPropertyCommand.cpp @@ -0,0 +1,53 @@ +#include "CEditScriptPropertyCommand.h" +#include "EUndoCommand.h" + +CEditScriptPropertyCommand::CEditScriptPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, IPropertyValue *pOldValue, bool IsDone) + : QUndoCommand("Edit Property") + , mpModel(pModel) + , mIndex(rkIndex) + , mCommandEnded(IsDone) +{ + mpProperty = pModel->PropertyForIndex(rkIndex, true); + mpOldValue = pOldValue; + mpNewValue = mpProperty->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->mpProperty == mpProperty) + { + mpNewValue->Copy(pkCmd->mpNewValue); + mCommandEnded = pkCmd->mCommandEnded; + return true; + } + } + + return false; +} + +void CEditScriptPropertyCommand::undo() +{ + mpProperty->RawValue()->Copy(mpOldValue); + mpModel->NotifyPropertyModified(mIndex); +} + +void CEditScriptPropertyCommand::redo() +{ + mpProperty->RawValue()->Copy(mpNewValue); + mpModel->NotifyPropertyModified(mIndex); +} diff --git a/src/Editor/Undo/CEditScriptPropertyCommand.h b/src/Editor/Undo/CEditScriptPropertyCommand.h new file mode 100644 index 00000000..cbc94542 --- /dev/null +++ b/src/Editor/Undo/CEditScriptPropertyCommand.h @@ -0,0 +1,26 @@ +#ifndef CEDITSCRIPTPROPERTYCOMMAND_H +#define CEDITSCRIPTPROPERTYCOMMAND_H + +#include "Editor/PropertyEdit/CPropertyDelegate.h" +#include + +class CEditScriptPropertyCommand : public QUndoCommand +{ + CPropertyModel *mpModel; + IProperty *mpProperty; + QModelIndex mIndex; + + IPropertyValue *mpOldValue; + IPropertyValue *mpNewValue; + bool mCommandEnded; + +public: + CEditScriptPropertyCommand(CPropertyModel *pModel, const QModelIndex& rkIndex, IPropertyValue *pOldValue, bool IsDone); + ~CEditScriptPropertyCommand(); + int id() const; + bool mergeWith(const QUndoCommand *pkOther); + void undo(); + void redo(); +}; + +#endif // CEDITSCRIPTPROPERTYCOMMAND_H diff --git a/src/Editor/Undo/EUndoCommand.h b/src/Editor/Undo/EUndoCommand.h index 4f22114b..2ca6e3ba 100644 --- a/src/Editor/Undo/EUndoCommand.h +++ b/src/Editor/Undo/EUndoCommand.h @@ -5,7 +5,8 @@ enum EUndoCommand { eTranslateNodeCmd, eRotateNodeCmd, - eScaleNodeCmd + eScaleNodeCmd, + eEditScriptPropertyCmd }; #endif // EUNDOCOMMAND diff --git a/src/Editor/Undo/UndoCommands.h b/src/Editor/Undo/UndoCommands.h index ee84d186..26958150 100644 --- a/src/Editor/Undo/UndoCommands.h +++ b/src/Editor/Undo/UndoCommands.h @@ -9,6 +9,7 @@ #include "CClearSelectionCommand.h" #include "CSelectAllCommand.h" #include "CInvertSelectionCommand.h" +#include "CEditScriptPropertyCommand.h" #include "EUndoCommand.h" #endif // UNDOCOMMANDS diff --git a/src/Editor/Widgets/WResourceSelector.cpp b/src/Editor/Widgets/WResourceSelector.cpp index 37b69d4a..38ed56d8 100644 --- a/src/Editor/Widgets/WResourceSelector.cpp +++ b/src/Editor/Widgets/WResourceSelector.cpp @@ -106,6 +106,22 @@ bool WResourceSelector::HasSupportedExtension(const CResourceInfo& rkRes) return IsSupportedExtension(TO_QSTRING(rkRes.Type().ToString())); } +void WResourceSelector::UpdateFrameColor() +{ + bool RedFrame = false; + + // Red frame should only display if an incorrect resource path is entered. It shouldn't display on Invalid Asset ID. + if (!mResourceValid) + { + TString Name = mResource.ToString().GetFileName(false); + + if (!Name.IsHexString() || (Name.Size() != 8 && Name.Size() != 16) || mResource.ID().IsValid()) + RedFrame = true; + } + mUI.LineEdit->setStyleSheet(RedFrame ? "border: 1px solid red" : ""); + mUI.LineEdit->setFont(font()); +} + // ************ GETTERS ************ CResourceInfo WResourceSelector::GetResourceInfo() { @@ -147,24 +163,38 @@ void WResourceSelector::SetResource(CResource *pRes) SetResource(CResourceInfo()); } +void WResourceSelector::SetResource(const QString& rkRes) +{ + TString Res = TO_TSTRING(rkRes); + TString Name = Res.GetFileName(false); + TString Dir = Res.GetFileDirectory(); + TString Ext = Res.GetFileExtension(); + + if (Dir.IsEmpty() && Name.IsHexString() && (Name.Size() == 8 || Name.Size() == 16) && Ext.Size() == 4) + SetResource(CResourceInfo(Name.Size() == 8 ? Name.ToInt32() : Name.ToInt64(), Ext)); + else + SetResource(CResourceInfo(Res)); +} + void WResourceSelector::SetResource(const CResourceInfo& rkRes) { - mResource = rkRes; - - if (mResource.IsValid()) + if (mResource != rkRes) { - mResourceValid = HasSupportedExtension(rkRes); + mResource = rkRes; + + if (mResource.IsValid()) + mResourceValid = HasSupportedExtension(rkRes); + else + mResourceValid = false; + mUI.LineEdit->setText(TO_QSTRING(mResource.ToString())); - } - else - { - mResourceValid = false; - mUI.LineEdit->clear(); + UpdateFrameColor(); + CreatePreviewPanel(); + SetButtonsBasedOnResType(); + Q_ASSERT(!mUI.LineEdit->text().isEmpty()); + emit ResourceChanged(TO_QSTRING(mResource.ToString())); } - - CreatePreviewPanel(); - SetButtonsBasedOnResType(); } void WResourceSelector::SetAllowedExtensions(const QString& extension) @@ -183,7 +213,7 @@ void WResourceSelector::SetAllowedExtensions(const TStringList& extensions) void WResourceSelector::SetText(const QString& ResPath) { mUI.LineEdit->setText(ResPath); - LoadResource(ResPath); + SetResource(ResPath); } void WResourceSelector::SetEditButtonEnabled(bool Enabled) @@ -214,7 +244,7 @@ void WResourceSelector::AdjustPreviewToParent(bool adjust) // ************ SLOTS ************ void WResourceSelector::OnLineEditTextEdited() { - LoadResource(mUI.LineEdit->text()); + SetResource(mUI.LineEdit->text()); } void WResourceSelector::OnBrowseButtonClicked() @@ -246,7 +276,7 @@ void WResourceSelector::OnBrowseButtonClicked() if (!NewRes.isEmpty()) { mUI.LineEdit->setText(NewRes); - LoadResource(NewRes); + SetResource(NewRes); } } @@ -273,27 +303,6 @@ void WResourceSelector::Export() emit ExportResource(mResource); } -void WResourceSelector::LoadResource(const QString& ResPath) -{ - mResource = CResourceInfo(); - - TString PathStr = ResPath.toStdString(); - TString Ext = PathStr.GetFileExtension(); - - if (IsSupportedExtension(TO_QSTRING(Ext))) - { - mResource = CResourceInfo(TO_TSTRING(ResPath)); - mResourceValid = mResource.IsValid(); - - if (mPreviewPanelValid) mpPreviewPanel->SetResource(mResource.Load()); - } - else mResourceValid = false; - - SetButtonsBasedOnResType(); - CreatePreviewPanel(); - emit ResourceChanged(ResPath); -} - void WResourceSelector::CreatePreviewPanel() { delete mpPreviewPanel; diff --git a/src/Editor/Widgets/WResourceSelector.h b/src/Editor/Widgets/WResourceSelector.h index d8056ed2..62afb844 100644 --- a/src/Editor/Widgets/WResourceSelector.h +++ b/src/Editor/Widgets/WResourceSelector.h @@ -54,6 +54,7 @@ public: bool eventFilter(QObject *, QEvent *); bool IsSupportedExtension(const QString& extension); bool HasSupportedExtension(const CResourceInfo& rkRes); + void UpdateFrameColor(); // Getters CResourceInfo GetResourceInfo(); @@ -65,6 +66,7 @@ public: // Setters void SetResource(CResource *pRes); + void SetResource(const QString& rkRes); void SetResource(const CResourceInfo& rkRes); void SetAllowedExtensions(const QString& extension); void SetAllowedExtensions(const QStringList& extensions); @@ -85,7 +87,6 @@ public slots: private: void Edit(); void Export(); - void LoadResource(const QString& ResPath); void CreatePreviewPanel(); void ShowPreviewPanel(); void HidePreviewPanel(); diff --git a/src/Editor/Widgets/WVectorEditor.cpp b/src/Editor/Widgets/WVectorEditor.cpp index 5bc54426..021d2575 100644 --- a/src/Editor/Widgets/WVectorEditor.cpp +++ b/src/Editor/Widgets/WVectorEditor.cpp @@ -108,6 +108,7 @@ void WVectorEditor::SetX(double x) mpSpinBoxX->setValue((double) x); mpSpinBoxX->blockSignals(false); + mEditing = true; emit ValueChanged(mValue); } @@ -119,6 +120,7 @@ void WVectorEditor::SetY(double y) mpSpinBoxY->setValue((double) y); mpSpinBoxY->blockSignals(false); + mEditing = true; emit ValueChanged(mValue); } @@ -130,6 +132,7 @@ void WVectorEditor::SetZ(double z) mpSpinBoxZ->setValue((double) z); mpSpinBoxZ->blockSignals(false); + mEditing = true; emit ValueChanged(mValue); } @@ -181,5 +184,6 @@ void WVectorEditor::SetupUI() // ************ PRIVATE SLOTS ************ void WVectorEditor::OnSpinBoxEditingDone() { - emit EditingDone(mValue); + if (mEditing) emit EditingDone(mValue); + mEditing = false; } diff --git a/src/Editor/Widgets/WVectorEditor.h b/src/Editor/Widgets/WVectorEditor.h index c51bf0e2..150b5671 100644 --- a/src/Editor/Widgets/WVectorEditor.h +++ b/src/Editor/Widgets/WVectorEditor.h @@ -15,6 +15,7 @@ class WVectorEditor : public QWidget Q_OBJECT CVector3f mValue; + bool mEditing; Qt::Orientation mOrientation; QLayout *mpLayout; diff --git a/src/Editor/WorldEditor/WModifyTab.cpp b/src/Editor/WorldEditor/WModifyTab.cpp index a61c93e8..29bd2797 100644 --- a/src/Editor/WorldEditor/WModifyTab.cpp +++ b/src/Editor/WorldEditor/WModifyTab.cpp @@ -16,7 +16,7 @@ WModifyTab::WModifyTab(QWidget *pParent) : ui->PropertyView->header()->resizeSection(0, PropViewWidth * 0.3); ui->PropertyView->header()->resizeSection(1, PropViewWidth * 0.3); ui->PropertyView->header()->setSectionResizeMode(1, QHeaderView::Fixed); - connect(ui->PropertyView, SIGNAL(PropertyModified(QModelIndex,bool)), this, SLOT(OnPropertyModified(QModelIndex,bool))); + connect(ui->PropertyView, SIGNAL(PropertyModified(IProperty*)), this, SLOT(OnPropertyModified(IProperty*))); mpInLinkModel = new CLinkModel(this); mpInLinkModel->SetConnectionType(CLinkModel::eIncoming); @@ -41,6 +41,7 @@ WModifyTab::~WModifyTab() void WModifyTab::SetEditor(CWorldEditor *pEditor) { mpWorldEditor = pEditor; + ui->PropertyView->SetEditor(mpWorldEditor); connect(mpWorldEditor, SIGNAL(SelectionTransformed()), this, SLOT(OnWorldSelectionTransformed())); } @@ -60,7 +61,7 @@ void WModifyTab::GenerateUI(QList& Selection) CScriptObject *pObj = pScriptNode->Object(); // Set up UI - ui->PropertyView->SetObject(pObj); + ui->PropertyView->SetInstance(pObj); mpInLinkModel->SetObject(pObj); mpOutLinkModel->SetObject(pObj); ui->LightGroupBox->hide(); @@ -75,7 +76,7 @@ void WModifyTab::GenerateUI(QList& Selection) void WModifyTab::ClearUI() { ui->ObjectsTabWidget->hide(); - ui->PropertyView->SetObject(nullptr); + ui->PropertyView->SetInstance(nullptr); ui->LightGroupBox->hide(); mpSelectedNode = nullptr; } @@ -85,17 +86,27 @@ void WModifyTab::OnWorldSelectionTransformed() ui->PropertyView->UpdateEditorProperties(QModelIndex()); } -void WModifyTab::OnPropertyModified(const QModelIndex& rkIndex, bool /*IsDone*/) +void WModifyTab::OnPropertyModified(IProperty *pProp) { if (mpSelectedNode->NodeType() == eScriptNode) { CScriptNode *pNode = static_cast(mpSelectedNode); - IProperty *pProperty = ui->PropertyView->PropertyModel()->PropertyForIndex(rkIndex, true); - pNode->PropertyModified(pProperty); + pNode->PropertyModified(pProp); // If this is the instance name property, then other parts of the UI need to be updated to reflect the new name. - if (pNode->Object()->IsEditorProperty(pProperty) && pProperty->Type() == eStringProperty) + if (pNode->Object()->IsEditorProperty(pProp) && pProp->Type() == eStringProperty) mpWorldEditor->UpdateSelectionUI(); + + // If this is a model/character, then we'll treat it as a modified selection. This is to make sure the selection bounds updates. + if (pProp->Type() == eFileProperty) + { + CFileTemplate *pFile = static_cast(pProp->Template()); + + if (pFile->AcceptsExtension("CMDL") || pFile->AcceptsExtension("ANCS") || pFile->AcceptsExtension("CHAR")) + mpWorldEditor->NotifySelectionModified(); + } + else if (pProp->Type() == eCharacterProperty) + mpWorldEditor->NotifySelectionModified(); } } diff --git a/src/Editor/WorldEditor/WModifyTab.h b/src/Editor/WorldEditor/WModifyTab.h index 8c237043..3db5c816 100644 --- a/src/Editor/WorldEditor/WModifyTab.h +++ b/src/Editor/WorldEditor/WModifyTab.h @@ -36,7 +36,7 @@ public: public slots: void OnWorldSelectionTransformed(); - void OnPropertyModified(const QModelIndex& rkIndex, bool IsDone); + void OnPropertyModified(IProperty *pProp); private: Ui::WModifyTab *ui;