summaryrefslogtreecommitdiffstats
path: root/src/Runtime/Source/stateapplication/editor
diff options
context:
space:
mode:
Diffstat (limited to 'src/Runtime/Source/stateapplication/editor')
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSSceneGraphArchitectDebugger.cpp574
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateDebuggerMaster.cpp222
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.cpp1880
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.h315
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorEditorsImpl.h1772
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorFoundation.h81
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorImpl.h406
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorProperties.h599
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.cpp239
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.h195
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.cpp813
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.h467
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorValue.h271
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.cpp2527
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.h174
-rw-r--r--src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodelValue.h291
16 files changed, 10826 insertions, 0 deletions
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSSceneGraphArchitectDebugger.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSSceneGraphArchitectDebugger.cpp
new file mode 100644
index 00000000..e15f587d
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSSceneGraphArchitectDebugger.cpp
@@ -0,0 +1,574 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSSceneGraphDebugger.h"
+#include "Qt3DSSceneGraphDebuggerValue.h"
+#include "Qt3DSSceneGraphDebuggerProtocol.h"
+#include "Qt3DSUIADatamodel.h"
+#include "Qt3DSStateEditorValue.h"
+#include "Qt3DSUIADatamodelValue.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/Qt3DSContainers.h"
+#include "Qt3DSKernelTypes.h"
+#include "Qt3DSHash.h" //we need to duplicate the hash attribute calls
+#include "EASTL/set.h"
+
+using namespace qt3ds;
+using namespace qt3ds::state;
+using namespace qt3ds::app;
+using namespace qt3ds::state::debugger;
+
+namespace {
+
+struct SAppElemVectorSet
+{
+ nvvector<SAppElement *> m_Elements;
+ eastl::hash_set<SAppElement *, eastl::hash<SAppElement *>, eastl::equal_to<SAppElement *>,
+ ForwardingAllocator>
+ m_Set;
+ SAppElemVectorSet(NVAllocatorCallback &alloc)
+ : m_Elements(alloc, "AppElemVectorSet")
+ , m_Set(ForwardingAllocator(alloc, "AppElemVectorSet"))
+ {
+ }
+
+ void insert(SAppElement *elem)
+ {
+ if (m_Set.insert(elem).second)
+ m_Elements.push_back(elem);
+ }
+
+ void clear()
+ {
+ m_Elements.clear();
+ m_Set.clear();
+ }
+ NVConstDataRef<SAppElement *> ToDataRef() { return m_Elements; }
+};
+
+struct SValueIndex
+{
+ QT3DSU32 m_ValueIndex;
+ QT3DSU32 m_Component;
+ SValueIndex(QT3DSU32 vi, QT3DSU32 component)
+ : m_ValueIndex(vi)
+ , m_Component(component)
+ {
+ }
+};
+
+struct SAppElementEntry
+{
+ Q3DStudio::TAttOrArgList m_AttList;
+ eastl::hash_map<QT3DSI32, SValueIndex> m_HashToIndexMap;
+ eastl::vector<app::SDatamodelValue> m_RuntimeValues;
+};
+
+struct SPresentationEntry
+{
+ CRegisteredString m_Id;
+ eastl::hash_map<CRegisteredString, SAppElement *> m_ElementMap;
+};
+
+Option<float> ValueToFloat(const SSGValue &val)
+{
+ if (val.getType() == SGPropertyValueTypes::Float)
+ return val.getData<float>();
+ return Empty();
+}
+
+Option<QT3DSI32> ValueToInteger(const SSGValue &val)
+{
+ if (val.getType() == SGPropertyValueTypes::I32)
+ return val.getData<QT3DSI32>();
+ return Empty();
+}
+
+Option<CRegisteredString> ValueToString(const SSGValue &val)
+{
+ if (val.getType() == SGPropertyValueTypes::String)
+ return val.getData<CRegisteredString>();
+ return Empty();
+}
+
+template <typename TDtype>
+struct SValueSetter
+{
+};
+
+template <>
+struct SValueSetter<float>
+{
+ static void SetValue(const SSGValue &incoming, float &outgoing, QT3DSU32 /*component*/)
+ {
+ Option<float> val = ValueToFloat(incoming);
+ if (val.hasValue())
+ outgoing = *val;
+ }
+};
+
+template <>
+struct SValueSetter<QT3DSVec2>
+{
+ static void SetValue(const SSGValue &incoming, QT3DSVec2 &outgoing, QT3DSU32 component)
+ {
+ Option<float> val = ValueToFloat(incoming);
+ if (val.hasValue()) {
+ switch (component) {
+ case 0:
+ outgoing.x = *val;
+ break;
+ case 1:
+ outgoing.y = *val;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ }
+};
+
+template <>
+struct SValueSetter<QT3DSVec3>
+{
+ static void SetValue(const SSGValue &incoming, QT3DSVec3 &outgoing, QT3DSU32 component)
+ {
+ Option<float> val = ValueToFloat(incoming);
+ if (val.hasValue()) {
+ switch (component) {
+ case 0:
+ outgoing.x = *val;
+ break;
+ case 1:
+ outgoing.y = *val;
+ break;
+ case 2:
+ outgoing.z = *val;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ }
+ }
+};
+
+template <>
+struct SValueSetter<QT3DSI32>
+{
+ static void SetValue(const SSGValue &incoming, QT3DSI32 &outgoing, QT3DSU32 /*component*/)
+ {
+ Option<QT3DSI32> val = ValueToInteger(incoming);
+ if (val.hasValue())
+ outgoing = *val;
+ }
+};
+
+template <>
+struct SValueSetter<eastl::string>
+{
+ static void SetValue(const SSGValue &incoming, eastl::string &outgoing, QT3DSU32 /*component*/)
+ {
+ Option<CRegisteredString> val = ValueToString(incoming);
+ if (val.hasValue())
+ outgoing = eastl::string(val->c_str());
+ }
+};
+
+template <>
+struct SValueSetter<bool>
+{
+ static void SetValue(const SSGValue &incoming, bool &outgoing, QT3DSU32 /*component*/)
+ {
+ Option<QT3DSI32> val = ValueToInteger(incoming);
+ if (val.hasValue())
+ outgoing = (*val) != 0 ? true : false;
+ }
+};
+
+// Element ref. Not sure what to do about this right now
+template <>
+struct SValueSetter<SGuid>
+{
+ static void SetValue(const SSGValue & /*incoming*/, SGuid & /*outgoing*/, QT3DSU32 /*component*/)
+ {
+ }
+};
+
+template <>
+struct SValueSetter<CRegisteredString>
+{
+ static void SetValue(const SSGValue & /*incoming*/, CRegisteredString & /*outgoing*/,
+ QT3DSU32 /*component*/)
+ {
+ QT3DS_ASSERT(false);
+ }
+};
+
+template <>
+struct SValueSetter<SObjectRef>
+{
+ static void SetValue(const SSGValue & /*incoming*/, SObjectRef & /*outgoing*/,
+ QT3DSU32 /*component*/)
+ {
+ }
+};
+
+template <>
+struct SValueSetter<CStringOrInt>
+{
+ static void SetValue(const SSGValue & /*incoming*/, CStringOrInt & /*outgoing*/,
+ QT3DSU32 /*component*/)
+ {
+ }
+};
+
+struct SValueSetterVisitor
+{
+ const SSGValue &m_Incoming;
+ QT3DSU32 m_Component;
+ SValueSetterVisitor &operator=(const SValueSetterVisitor &);
+ SValueSetterVisitor(const SSGValue &i, QT3DSU32 c)
+ : m_Incoming(i)
+ , m_Component(c)
+ {
+ }
+ template <typename TDataType>
+ void operator()(TDataType &dt)
+ {
+ SValueSetter<TDataType>::SetValue(m_Incoming, dt, m_Component);
+ }
+ // object refs can generate this response.
+ void operator()() {}
+};
+
+void SetComponentValue(const SSGValue &incoming, app::SDatamodelValue &outgoing, QT3DSU32 component)
+{
+ SValueSetterVisitor visitor(incoming, component);
+ outgoing.visit<void>(visitor);
+}
+
+struct SArchitectDebuggerImpl : public ISceneGraphArchitectDebugger
+{
+ NVFoundationBase &m_Foundation;
+ NVScopedRefCounted<IDatamodel> m_Datamodel;
+ NVScopedRefCounted<IDebugOutStream> m_Stream;
+ ISceneGraphArchitectDebuggerListener *m_Listener;
+ nvhash_map<QT3DSU64, SAppElement *> m_ElemAppElemMap;
+ nvhash_map<SAppElement *, SAppElementEntry> m_RuntimeValues;
+ SAppElemVectorSet m_DirtySet;
+ eastl::vector<SPresentationEntry> m_Presentations;
+ bool m_Valid;
+ QT3DSI32 mRefCount;
+
+ SArchitectDebuggerImpl(IDatamodel &dm)
+ : m_Foundation(dm.GetFoundation())
+ , m_Datamodel(dm)
+ , m_Stream(NULL)
+ , m_Listener(NULL)
+ , m_ElemAppElemMap(dm.GetFoundation().getAllocator(), "ElemAppMap")
+ , m_RuntimeValues(dm.GetFoundation().getAllocator(), "RuntimeValues")
+ , m_DirtySet(dm.GetFoundation().getAllocator())
+ , m_Valid(true)
+ , mRefCount(0)
+ {
+ }
+
+ void addRef() { atomicIncrement(&mRefCount); }
+ void release()
+ {
+ // ensure the datamodel sticks around until after we are destroyed
+ NVScopedRefCounted<IDatamodel> dm(m_Datamodel);
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ NVDelete(m_Foundation.getAllocator(), this);
+ }
+ }
+
+ virtual IDatamodel &GetDatamodel() { return *m_Datamodel; }
+
+ static QT3DSI32 GetHashValue(const char *name, const char *component, eastl::string &ioWorkspace)
+ {
+ ioWorkspace.assign(nonNull(name));
+ ioWorkspace.append(".");
+ ioWorkspace.append(component);
+ return Q3DStudio::CHash::HashAttribute(ioWorkspace.c_str());
+ }
+
+ void AppendAttribute(const char *name, const char *formalName, ERuntimeDataModelDataType dtype,
+ SAppElementEntry &theEntry)
+ {
+ SAttOrArg newValue;
+ newValue.m_Name = name;
+ newValue.m_FormalName = formalName;
+ newValue.m_DataType = dtype;
+ theEntry.m_AttList.push_back(newValue);
+ theEntry.m_RuntimeValues.push_back(qt3ds::app::SDatamodelValue());
+ }
+
+ void LoadAppElement(SAppElement &elem, SPresentationEntry &entry)
+ {
+ entry.m_ElementMap.insert(eastl::make_pair(
+ m_Datamodel->GetStringTable().RegisterStr(m_Datamodel->GetElementId(elem).c_str()),
+ &elem));
+ SAppElementEntry &theEntry = m_RuntimeValues[&elem];
+ theEntry.m_AttList = m_Datamodel->GetElementAttributes(elem);
+ theEntry.m_RuntimeValues = m_Datamodel->GetElementAttributeInitialValues(elem);
+ AppendAttribute("active", "Active", ERuntimeDataModelDataTypeBool, theEntry);
+
+ if (m_Datamodel->IsComponent(elem)) {
+ AppendAttribute("slide", "(Slide)", ERuntimeDataModelDataTypeLong, theEntry);
+ AppendAttribute("time", "(Time)", ERuntimeDataModelDataTypeLong, theEntry);
+ AppendAttribute("paused", "(Mode)", ERuntimeDataModelDataTypeBool, theEntry);
+ }
+ eastl::string hashTemp;
+ for (QT3DSU32 idx = 0, end = (QT3DSU32)theEntry.m_AttList.size(); idx < end; ++idx) {
+ // Build out the component hash names.
+ const Q3DStudio::SAttOrArg &theProp(theEntry.m_AttList[idx]);
+ switch (theProp.m_DataType) {
+ // one component, one hash, just hash the name
+ default:
+ theEntry.m_HashToIndexMap.insert(
+ eastl::make_pair((QT3DSI32)Q3DStudio::CHash::HashAttribute(theProp.m_Name.c_str()),
+ SValueIndex(idx, 0)));
+ break;
+ case ERuntimeDataModelDataTypeFloat2: {
+ theEntry.m_HashToIndexMap.insert(eastl::make_pair(
+ GetHashValue(theProp.m_Name.c_str(), "x", hashTemp), SValueIndex(idx, 0)));
+ theEntry.m_HashToIndexMap.insert(eastl::make_pair(
+ GetHashValue(theProp.m_Name.c_str(), "y", hashTemp), SValueIndex(idx, 1)));
+ } break;
+ case ERuntimeDataModelDataTypeFloat3: {
+ const char *compNames[3] = { "x", "y", "z" };
+ if (theProp.m_MetaType == ERuntimeAdditionalMetaDataTypeColor) {
+ compNames[0] = "r";
+ compNames[1] = "g";
+ compNames[2] = "b";
+ }
+
+ theEntry.m_HashToIndexMap.insert(
+ eastl::make_pair(GetHashValue(theProp.m_Name.c_str(), compNames[0], hashTemp),
+ SValueIndex(idx, 0)));
+ theEntry.m_HashToIndexMap.insert(
+ eastl::make_pair(GetHashValue(theProp.m_Name.c_str(), compNames[1], hashTemp),
+ SValueIndex(idx, 1)));
+ theEntry.m_HashToIndexMap.insert(
+ eastl::make_pair(GetHashValue(theProp.m_Name.c_str(), compNames[2], hashTemp),
+ SValueIndex(idx, 2)));
+ } break;
+ }
+ // Set a value
+ if (theEntry.m_RuntimeValues[idx].getType() == ERuntimeDataModelDataTypeNone) {
+ switch (theProp.m_DataType) {
+ case ERuntimeDataModelDataTypeFloat:
+ theEntry.m_RuntimeValues[idx] = 0.0f;
+ break;
+ case ERuntimeDataModelDataTypeFloat2:
+ theEntry.m_RuntimeValues[idx] = QT3DSVec2(0, 0);
+ break;
+ case ERuntimeDataModelDataTypeFloat3:
+ theEntry.m_RuntimeValues[idx] = QT3DSVec3(0, 0, 0);
+ break;
+ case ERuntimeDataModelDataTypeLong:
+ theEntry.m_RuntimeValues[idx] = (QT3DSI32)0;
+ break;
+ case ERuntimeDataModelDataTypeString:
+ theEntry.m_RuntimeValues[idx] = eastl::string();
+ break;
+ case ERuntimeDataModelDataTypeBool:
+ theEntry.m_RuntimeValues[idx] = false;
+ break;
+ case ERuntimeDataModelDataTypeStringRef:
+ theEntry.m_RuntimeValues[idx] = eastl::string();
+ break;
+ // object references are stored as string values.
+ case ERuntimeDataModelDataTypeObjectRef:
+ theEntry.m_RuntimeValues[idx] = eastl::string();
+ break;
+ case ERuntimeDataModelDataTypeStringOrInt:
+ theEntry.m_RuntimeValues[idx] = eastl::string();
+ break;
+ }
+ }
+ }
+ eastl::vector<SAppElement *> children = m_Datamodel->GetElementChildren(elem);
+ for (size_t childIdx = 0, childEnd = children.size(); childIdx < childEnd; ++childIdx) {
+ LoadAppElement(*children[childIdx], entry);
+ }
+ }
+
+ void LoadDatamodel()
+ {
+ eastl::vector<app::SPresentation> presentations = m_Datamodel->GetPresentations();
+ m_Presentations.clear();
+ m_RuntimeValues.clear();
+ m_ElemAppElemMap.clear();
+ m_Presentations.resize(presentations.size());
+ for (size_t idx = 0, end = presentations.size(); idx < end; ++idx) {
+ app::SPresentation &incoming(presentations[idx]);
+ SPresentationEntry &pres = m_Presentations[idx];
+ pres.m_Id = m_Datamodel->GetStringTable().RegisterStr(incoming.m_Id.c_str());
+ if (incoming.m_Scene)
+ LoadAppElement(*incoming.m_Scene, pres);
+ }
+ }
+
+ SAppElementEntry *GetRuntimeValues(app::SAppElement &elem)
+ {
+ nvhash_map<SAppElement *, SAppElementEntry>::iterator iter = m_RuntimeValues.find(&elem);
+ if (iter != m_RuntimeValues.end())
+ return &iter->second;
+ return NULL;
+ }
+
+ SPresentationEntry *FindPresentation(CRegisteredString &str)
+ {
+ for (size_t idx = 0, end = m_Presentations.size(); idx < end; ++idx)
+ if (m_Presentations[idx].m_Id == str)
+ return &m_Presentations[idx];
+ return NULL;
+ }
+
+ /*External interface for clients*/
+
+ void SetListener(ISceneGraphArchitectDebuggerListener *listener) { m_Listener = listener; }
+
+ Q3DStudio::TAttOrArgList GetElementAttributes(app::SAppElement &elem)
+ {
+ SAppElementEntry *entry = GetRuntimeValues(elem);
+ if (entry)
+ return entry->m_AttList;
+ return Q3DStudio::TAttOrArgList();
+ }
+
+ eastl::vector<app::SDatamodelValue> GetElementAttributeValues(app::SAppElement &elem)
+ {
+ SAppElementEntry *entry = GetRuntimeValues(elem);
+ if (entry)
+ return entry->m_RuntimeValues;
+ return eastl::vector<app::SDatamodelValue>();
+ }
+
+ void OnMessageReceived(const SDebugStreamMessage &inMessage)
+ {
+ if (!m_Valid)
+ return;
+ SSGProtocolReader theReader(inMessage.m_Data, m_Datamodel->GetStringTable());
+ while (theReader.Finished() == false && m_Valid) {
+ SSGProtocolMessageTypes::Enum theMessageType = theReader.MessageType();
+ switch (theMessageType) {
+ case SSGProtocolMessageTypes::Initialization: {
+ QT3DSU32 version = theReader.ReadInitialization();
+ if (version > GetSceneGraphProtocolVersion()) {
+ QT3DS_ASSERT(false);
+ m_Foundation.error(QT3DS_INVALID_OPERATION,
+ "Invalid scene graph debugger protocol version");
+ m_Valid = false;
+ }
+ } break;
+ case SSGProtocolMessageTypes::IdUpdate: {
+ SIdUpdate theUpdate = theReader.ReadIdUpdate();
+ SPresentationEntry *theEntry = FindPresentation(theUpdate.m_PresentationId);
+ if (theEntry) {
+ for (size_t idx = 0, end = theUpdate.m_IdUpdates.size(); idx < end; ++idx) {
+ const SElemMap &elemMap(theUpdate.m_IdUpdates[idx]);
+ eastl::hash_map<CRegisteredString, SAppElement *>::iterator iter =
+ theEntry->m_ElementMap.find(elemMap.m_Id);
+ if (iter != theEntry->m_ElementMap.end())
+ m_ElemAppElemMap[elemMap.m_Elem] = iter->second;
+ else {
+ QT3DS_ASSERT(false);
+ m_Foundation.error(QT3DS_WARN, "Failed to map element");
+ }
+ }
+ }
+ } break;
+ case SSGProtocolMessageTypes::ElemUpdate: {
+ SElemUpdate theUpdate = theReader.ReadElemUpdate();
+ nvhash_map<QT3DSU64, SAppElement *>::iterator ptrToElem =
+ m_ElemAppElemMap.find(theUpdate.m_Elem);
+ if (ptrToElem != m_ElemAppElemMap.end()) {
+ nvhash_map<SAppElement *, SAppElementEntry>::iterator elemToEntry =
+ m_RuntimeValues.find(ptrToElem->second);
+ if (elemToEntry != m_RuntimeValues.end()) {
+ SAppElementEntry &theEntry(elemToEntry->second);
+ m_DirtySet.insert(elemToEntry->first);
+ for (size_t idx = 0, end = theUpdate.m_Updates.size(); idx < end; ++idx) {
+ const SValueUpdate &theValue(theUpdate.m_Updates[idx]);
+ eastl::hash_map<QT3DSI32, SValueIndex>::iterator hashIndex =
+ theEntry.m_HashToIndexMap.find(theValue.m_Hash);
+ if (hashIndex != theEntry.m_HashToIndexMap.end()) {
+ const SValueIndex theIdx = hashIndex->second;
+ SetComponentValue(theValue.m_Value,
+ theEntry.m_RuntimeValues[theIdx.m_ValueIndex],
+ theIdx.m_Component);
+ }
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ m_Foundation.error(QT3DS_WARN, "Failed to map element");
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ m_Foundation.error(QT3DS_WARN, "Failed to map element");
+ }
+ } break;
+ case SSGProtocolMessageTypes::Frame: {
+ NVConstDataRef<SAppElement *> dirtyItems = m_DirtySet.ToDataRef();
+ if (dirtyItems.size() && m_Listener) {
+ m_Listener->OnItemsDirty(dirtyItems);
+ m_DirtySet.clear();
+ }
+ } break;
+ }
+ } // end while
+ }
+
+ void AttachToStream(IDebugOutStream &inStream) { m_Stream = &inStream; }
+
+ void RefreshData(bool inNeedReloadData)
+ {
+ if (inNeedReloadData)
+ m_Datamodel->RefreshFile();
+ LoadDatamodel();
+ }
+};
+}
+
+ISceneGraphArchitectDebugger &
+ISceneGraphArchitectDebugger::Create(qt3ds::app::IDatamodel &inDatamodel)
+{
+ SArchitectDebuggerImpl &retval =
+ *QT3DS_NEW(inDatamodel.GetFoundation().getAllocator(), SArchitectDebuggerImpl)(inDatamodel);
+ retval.LoadDatamodel();
+ return retval;
+}
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateDebuggerMaster.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSStateDebuggerMaster.cpp
new file mode 100644
index 00000000..83fe016f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateDebuggerMaster.cpp
@@ -0,0 +1,222 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSStateDebugger.h"
+#include "Qt3DSStateDebuggerProtocol.h"
+#include "foundation/IOStreams.h"
+#include "foundation/Qt3DSAtomic.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::debugger;
+
+namespace {
+MallocAllocator g_MallocAlloc;
+
+struct SDebugStrInStream : public IInStream
+{
+ size_t m_Pos;
+ const TDebugStr &m_Str;
+ SDebugStrInStream(const TDebugStr &inStr)
+ : m_Pos(0)
+ , m_Str(inStr)
+ {
+ }
+ virtual QT3DSU32 Read(NVDataRef<QT3DSU8> data)
+ {
+ size_t available = NVMin((size_t)data.size(), m_Str.size() - m_Pos);
+ if (available) {
+ memCopy(data.begin(), m_Str.data() + m_Pos, (QT3DSU32)available);
+ m_Pos += available;
+ }
+ return (QT3DSU32)available;
+ }
+};
+
+struct SDebuggerMaster : public IDebuggerMaster
+{
+ typedef eastl::hash_map<QT3DSI32, NVScopedRefCounted<IDebuggedInterpreter>> TIdInterpreterMap;
+
+ QT3DSI32 mRefCount;
+ NVScopedRefCounted<IDebugOutStream> m_OutStream;
+ TIdInterpreterMap m_Interpreters;
+ IDebuggerMasterListener &m_Listener;
+ SMessageSerializer m_Serializer;
+ SMessageParser<SDebuggerMaster> m_Parser;
+ TDebugStr m_LogStr;
+ TDebugStr m_MessageStr;
+ bool m_Invalid;
+
+ SDebuggerMaster(IDebugOutStream &inStream, IDebuggerMasterListener &listener)
+ : mRefCount(0)
+ , m_OutStream(inStream)
+ , m_Listener(listener)
+ , m_Invalid(false)
+ {
+ }
+
+ virtual ~SDebuggerMaster()
+ {
+ // Disconnect();
+ }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(g_MallocAlloc);
+
+ template <typename TMessageType>
+ void SendMessage(QT3DSI32 inStreamId, const TMessageType &inMessage)
+ {
+ m_Serializer.Serialize(inStreamId, inMessage, 0);
+ m_OutStream->Write(m_Serializer.ToRawMessage());
+ }
+
+ virtual void OnMessageReceived(const SDebugStreamMessage &inMessage)
+ {
+ if (m_Invalid)
+ return;
+ m_Parser.Parse(inMessage.m_Data, *this);
+ }
+
+ void ReleaseAllInterpreters()
+ {
+ for (TIdInterpreterMap::iterator iter = m_Interpreters.begin(), end = m_Interpreters.end();
+ iter != end; ++iter) {
+ iter->second->Disconnect();
+ m_Listener.OnInterpreterDisconnected(*iter->second.mPtr);
+ }
+ m_Interpreters.clear();
+ }
+
+ virtual void Disconnect()
+ {
+ SendMessage(-1, SDisconnect());
+ ReleaseAllInterpreters();
+ }
+
+ IDebuggedInterpreter *FindInterpreter(QT3DSI32 inStreamId)
+ {
+ TIdInterpreterMap::iterator iter = m_Interpreters.find(inStreamId);
+ if (iter != m_Interpreters.end())
+ return iter->second.mPtr;
+ return NULL;
+ }
+
+// Set of ignored messages
+#define IGNORE_DEBUG_MESSAGE_TYPE(tname) \
+ void OnMessage(QT3DSU32, QT3DSU64, const S##tname &) { QT3DS_ASSERT(false); }
+
+ // this are outgoing messages; we shouldn't be receiving them.
+ IGNORE_DEBUG_MESSAGE_TYPE(SetBreakpoint)
+ IGNORE_DEBUG_MESSAGE_TYPE(ClearBreakpoint)
+ IGNORE_DEBUG_MESSAGE_TYPE(BreakOnMicrostep)
+ IGNORE_DEBUG_MESSAGE_TYPE(ClearAllBreakpoints)
+ IGNORE_DEBUG_MESSAGE_TYPE(Continue)
+
+ void OnMessage(QT3DSU32 sid, QT3DSU64, const SConnect &inMessage)
+ {
+ SDebugStrInStream theStream(inMessage.m_SCXMLData);
+ TEditorPtr theEditor(IEditor::CreateEditor(inMessage.m_Filename.c_str(), theStream));
+ if (theEditor) {
+ IDebuggedInterpreter &theInterpreter = IDebuggedInterpreter::Create(
+ g_MallocAlloc, *m_OutStream.mPtr, theEditor, inMessage.m_Filename, sid,
+ toConstDataRef(inMessage.m_Configuration.data(),
+ (QT3DSU32)inMessage.m_Configuration.size()),
+ m_Listener);
+ m_Interpreters.insert(eastl::make_pair(sid, &theInterpreter));
+ m_Listener.OnInterpreterConnected(theInterpreter);
+ } else {
+ m_LogStr.assign("Connection failed: ");
+ m_LogStr.append(inMessage.m_Filename);
+ // log the failure somehow.
+ m_Listener.OnLog(NULL, m_LogStr);
+ }
+ }
+
+ void OnMessage(QT3DSU32 sid, QT3DSU64, const SBreakpointHit &inMessage)
+ {
+ IDebuggedInterpreter *interp = FindInterpreter(sid);
+ if (interp)
+ interp->BreakpointHit(inMessage.m_Breakpoint);
+ }
+
+ void OnMessage(QT3DSU32, QT3DSU64, const SInitialization &inMessage)
+ {
+ if (inMessage.m_Version != SInitialization::GetCurrentVersion()) {
+ QT3DS_ASSERT(false);
+ m_Invalid = true;
+ }
+ }
+
+ void OnMessage(QT3DSU32 sid, QT3DSU64, const SDebugLog &inMessage)
+ {
+ IDebuggedInterpreter *interp = FindInterpreter(sid);
+ m_Listener.OnLog(interp, inMessage.m_Message);
+ }
+
+#define FORWARD_INTERPRETER_EVENT(evnType, interpFun) \
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const evnType &inMsg) \
+ { \
+ IDebuggedInterpreter *interp = FindInterpreter(sid); \
+ if (interp) \
+ interp->interpFun(inMsg); \
+ }
+
+ FORWARD_INTERPRETER_EVENT(SEventQueued, OnEventQueued);
+ FORWARD_INTERPRETER_EVENT(SBeginStep, OnBeginStep);
+ FORWARD_INTERPRETER_EVENT(SBeginMicrostep, OnBeginMicrostep);
+ FORWARD_INTERPRETER_EVENT(SMicrostepEvent, OnMicrostepEvent);
+ FORWARD_INTERPRETER_EVENT(SMicrostepData, OnMicrostepData);
+ FORWARD_INTERPRETER_EVENT(SEndMicrostep, OnEndMicrostep);
+ FORWARD_INTERPRETER_EVENT(SModifyTableValues, OnModifyTable);
+
+ void OnMessage(QT3DSI32 sid, QT3DSU64, const SDisconnect &)
+ {
+ if (sid > 0) {
+ IDebuggedInterpreter *interp = FindInterpreter(sid);
+ if (interp) {
+ m_Listener.OnInterpreterDisconnected(*interp);
+ m_Interpreters.erase(sid);
+ }
+ } else {
+ ReleaseAllInterpreters();
+ }
+ }
+
+ void error(const char8_t *inPrefix, const char8_t *inSuffix)
+ {
+ m_LogStr.assign(nonNull(inPrefix));
+ m_LogStr.append(nonNull(inSuffix));
+ m_Listener.OnLog(NULL, m_LogStr);
+ }
+};
+}
+
+IDebuggerMaster &IDebuggerMaster::CreateMaster(IDebugOutStream &outStream,
+ IDebuggerMasterListener &inListener)
+{
+ return *QT3DS_NEW(g_MallocAlloc, SDebuggerMaster)(outStream, inListener);
+} \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.cpp
new file mode 100644
index 00000000..6891fab0
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.cpp
@@ -0,0 +1,1880 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSState.h"
+#include "Qt3DSStateEditorEditorsImpl.h"
+#include "Qt3DSStateExecutionContext.h"
+#include "EASTL/sort.h"
+
+using namespace qt3ds::state;
+using namespace qt3ds::state::editor;
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+#pragma warning(disable : 4355)
+
+ SEditorImpl::SEditorImpl(TFoundationPtr inFoundation,
+ NVScopedRefCounted<IStringTable> inStringTable)
+ : m_EditorFoundation(inFoundation)
+ , m_StringTable(inStringTable)
+ , m_AutoAllocator(m_EditorFoundation->getFoundation())
+ , m_StateContext(IStateContext::Create(m_EditorFoundation->getFoundation()))
+ , m_Editors(m_EditorFoundation->getAllocator(), "SEditorImpl::m_Editors")
+ , mRefCount(0)
+ , m_TransactionManager(
+ QT3DS_NEW(inFoundation->getAllocator(), STransactionManagerImpl)(inFoundation))
+ , m_Accessors(m_EditorFoundation->getAllocator(), "SEditorImpl::m_Accessors")
+ , m_CopyPasteListener(NULL)
+ {
+ }
+
+ void SEditorImpl::addRef() { atomicIncrement(&mRefCount); }
+
+ void SEditorImpl::release()
+ {
+ QT3DSI32 count = atomicDecrement(&mRefCount);
+ if (count <= 0) {
+ TFoundationPtr theFoundation(m_EditorFoundation);
+ NVDelete(theFoundation->getAllocator(), this);
+ }
+ }
+
+ TObjPtr SEditorImpl::InsertEditor(void *inData, IEditorObject *inEditor)
+ {
+ if (inEditor) {
+ bool insertResult = m_Editors.insert(eastl::make_pair(inData, inEditor)).second;
+ QT3DS_ASSERT(insertResult);
+ (void)insertResult;
+ }
+ return inEditor;
+ }
+
+ template <typename TStateType>
+ TObjPtr SEditorImpl::ToEditor(TStateType &inItem)
+ {
+ TStateEditorMap::iterator iter = m_Editors.find(&inItem);
+ if (iter != m_Editors.end())
+ return iter->second;
+
+ TObjPtr retval = SStateEditorMap<TStateType>::CreateEditor(inItem, *this, m_Accessors);
+ if (retval)
+ InsertEditor(&inItem, retval.mPtr);
+ return retval;
+ }
+
+ template <typename TStateType>
+ TObjPtr SEditorImpl::ToEditor(TStateType *inItem)
+ {
+ if (inItem == NULL)
+ return TObjPtr();
+
+ return ToEditor(*inItem);
+ }
+
+ TObjPtr SEditorImpl::ToEditor(SStateNode &inItem)
+ {
+ switch (inItem.m_Type) {
+ case StateNodeTypes::State:
+ return ToEditor(static_cast<SState &>(inItem));
+ case StateNodeTypes::Parallel:
+ return ToEditor(static_cast<SParallel &>(inItem));
+ case StateNodeTypes::Transition:
+ return ToEditor(static_cast<STransition &>(inItem));
+ case StateNodeTypes::Final:
+ return ToEditor(static_cast<SFinal &>(inItem));
+ case StateNodeTypes::SCXML:
+ return ToEditor(static_cast<SSCXML &>(inItem));
+ case StateNodeTypes::History:
+ return ToEditor(static_cast<SHistory &>(inItem));
+ default:
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ }
+
+ TObjPtr SEditorImpl::ExecutableContentToEditor(SExecutableContent &inItem)
+ {
+ TStateEditorMap::iterator iter = m_Editors.find(&inItem);
+ if (iter != m_Editors.end())
+ return iter->second;
+
+ switch (inItem.m_Type) {
+ case ExecutableContentTypes::Send:
+ return ToEditor(static_cast<SSend &>(inItem));
+ case ExecutableContentTypes::Raise:
+ return ToEditor(static_cast<SRaise &>(inItem));
+ case ExecutableContentTypes::Log:
+ return ToEditor(static_cast<SLog &>(inItem));
+ case ExecutableContentTypes::Assign:
+ return ToEditor(static_cast<SAssign &>(inItem));
+ case ExecutableContentTypes::If:
+ return ToEditor(static_cast<SIf &>(inItem));
+ case ExecutableContentTypes::ElseIf:
+ return ToEditor(static_cast<SElseIf &>(inItem));
+ case ExecutableContentTypes::Else:
+ return ToEditor(static_cast<SElse &>(inItem));
+ case ExecutableContentTypes::Script:
+ return ToEditor(static_cast<SScript &>(inItem));
+ case ExecutableContentTypes::Cancel:
+ return ToEditor(static_cast<SCancel &>(inItem));
+ default:
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ }
+
+ template <typename TStateType>
+ TStateType *SEditorImpl::FromEditor(TObjPtr inPtr)
+ {
+ if (inPtr.mPtr == NULL)
+ return NULL;
+ const char8_t *typeName = inPtr->TypeName();
+ typedef typename SStateEditorMap<TStateType>::TEditorType TProspectiveType;
+ if (AreEqual(typeName, TProspectiveType::GetTypeStr())) {
+ return reinterpret_cast<TStateType *>(
+ &static_cast<TProspectiveType *>(inPtr.mPtr)->m_Data);
+ }
+ return NULL;
+ }
+
+ SStateNode *SEditorImpl::StateNodeFromEditor(TObjPtr inPtr)
+ {
+ if (inPtr.mPtr == NULL)
+ return NULL;
+ const char8_t *typeName = inPtr->TypeName();
+ if (AreEqual(typeName, SSCXMLEditor::GetTypeStr()))
+ return FromEditor<SSCXML>(inPtr);
+ if (AreEqual(typeName, SStateEditor::GetTypeStr()))
+ return FromEditor<SState>(inPtr);
+ if (AreEqual(typeName, STransitionEditor::GetTypeStr()))
+ return FromEditor<STransition>(inPtr);
+ if (AreEqual(typeName, SParallelEditor::GetTypeStr()))
+ return FromEditor<SParallel>(inPtr);
+ if (AreEqual(typeName, SFinalEditor::GetTypeStr()))
+ return FromEditor<SFinal>(inPtr);
+ if (AreEqual(typeName, SHistoryEditor::GetTypeStr()))
+ return FromEditor<SHistory>(inPtr);
+ return NULL;
+ }
+
+ SExecutableContent *SEditorImpl::ExecutableContentFromEditor(TObjPtr inPtr)
+ {
+ if (inPtr.mPtr == NULL)
+ return NULL;
+ const char8_t *typeName = inPtr->TypeName();
+ if (AreEqual(typeName, SSendEditor::GetTypeStr())) {
+ return FromEditor<SSend>(inPtr);
+ }
+ if (AreEqual(typeName, SRaiseEditor::GetTypeStr())) {
+ return FromEditor<SRaise>(inPtr);
+ }
+ if (AreEqual(typeName, SLogEditor::GetTypeStr())) {
+ return FromEditor<SLog>(inPtr);
+ }
+ if (AreEqual(typeName, SAssignEditor::GetTypeStr())) {
+ return FromEditor<SAssign>(inPtr);
+ }
+ if (AreEqual(typeName, SIfEditor::GetTypeStr())) {
+ return FromEditor<SIf>(inPtr);
+ }
+ if (AreEqual(typeName, SElseIfEditor::GetTypeStr())) {
+ return FromEditor<SElseIf>(inPtr);
+ }
+ if (AreEqual(typeName, SElseEditor::GetTypeStr())) {
+ return FromEditor<SElse>(inPtr);
+ }
+ if (AreEqual(typeName, SScriptEditor::GetTypeStr())) {
+ return FromEditor<SScript>(inPtr);
+ }
+ if (AreEqual(typeName, SCancelEditor::GetTypeStr())) {
+ return FromEditor<SCancel>(inPtr);
+ }
+
+ return NULL;
+ }
+
+ void SEditorImpl::GenerateUniqueId(SStateNode &inNode, const char8_t *inStem)
+ {
+ QT3DS_ASSERT(inNode.m_Id.IsValid() == false);
+ CXMLIO::GenerateUniqueId(inNode, inStem, *m_StateContext, *m_StringTable);
+ }
+
+ void SEditorImpl::GenerateUniqueId(SSend &inNode, const char8_t *inStem)
+ {
+ CXMLIO::GenerateUniqueId(inNode, inStem, *m_StateContext, *m_StringTable);
+ }
+
+ TObjPtr SEditorImpl::GetRoot() { return ToEditor(m_StateContext->GetRoot()); }
+
+ template <typename TStateType>
+ eastl::pair<TStateType *, TObjPtr> SEditorImpl::CreateEditorAndObject()
+ {
+ typedef typename SStateEditorMap<TStateType>::TEditorType TEditorType;
+ TObjPtr newEditor = SStateEditorMap<TStateType>::CreateEditor(*this, m_Accessors);
+ TStateType *theState = &static_cast<TEditorType *>(newEditor.mPtr)->m_Data;
+ eastl::pair<TStateType *, TObjPtr> retval = eastl::make_pair(theState, newEditor);
+ InsertEditor(retval.first, retval.second.mPtr);
+ return retval;
+ }
+
+ TObjPtr SEditorImpl::DoCreate(const char8_t *inTypeName, const char8_t *inId)
+ {
+ if (AreEqual(inTypeName, SSCXMLEditor::GetTypeStr())) {
+ QT3DS_ASSERT(m_StateContext->GetRoot() == NULL);
+ eastl::pair<SSCXML *, TObjPtr> theNewEditor = CreateEditorAndObject<SSCXML>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "scxml" : inId);
+ m_StateContext->SetRoot(*theNewEditor.first);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SStateEditor::GetTypeStr())) {
+ eastl::pair<SState *, TObjPtr> theNewEditor = CreateEditorAndObject<SState>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "state" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SParallelEditor::GetTypeStr())) {
+ eastl::pair<SParallel *, TObjPtr> theNewEditor = CreateEditorAndObject<SParallel>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "parallel" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SFinalEditor::GetTypeStr())) {
+ eastl::pair<SFinal *, TObjPtr> theNewEditor = CreateEditorAndObject<SFinal>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "final" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SHistoryEditor::GetTypeStr())) {
+ eastl::pair<SHistory *, TObjPtr> theNewEditor = CreateEditorAndObject<SHistory>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "history" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, STransitionEditor::GetTypeStr())) {
+ eastl::pair<STransition *, TObjPtr> theNewEditor =
+ CreateEditorAndObject<STransition>();
+ GenerateUniqueId(*theNewEditor.first, inId == NULL ? "transition" : inId);
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SDataModelEditor::GetTypeStr())) {
+ eastl::pair<SDataModel *, TObjPtr> theNewEditor =
+ CreateEditorAndObject<SDataModel>();
+ return theNewEditor.second;
+ } else if (AreEqual(inTypeName, SDataEditor::GetTypeStr())) {
+ eastl::pair<SData *, TObjPtr> theNewEditor = CreateEditorAndObject<SData>();
+ return theNewEditor.second;
+ }
+
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ TObjPtr SEditorImpl::Create(const char8_t *inTypeName, const char8_t *inId)
+ {
+ TObjPtr retval = DoCreate(inTypeName, inId);
+ m_TransactionManager->OnObjectCreated(retval);
+ return retval;
+ }
+
+ TObjPtr SEditorImpl::GetOrCreate(SSCXML &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SState &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(STransition &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SParallel &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SFinal &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SHistory &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SDataModel &inData) { return ToEditor(inData); }
+ TObjPtr SEditorImpl::GetOrCreate(SData &inData) { return ToEditor(inData); }
+
+ TObjPtr SEditorImpl::GetObjectById(const char8_t *inId)
+ {
+ if (isTrivial(inId))
+ return TObjPtr();
+ SStateNode *theNode = m_StateContext->FindStateNode(RegisterStr(inId));
+ if (theNode)
+ return ToEditor(*theNode);
+ return TObjPtr();
+ }
+
+ TObjPtr SEditorImpl::GetEditor(void *inGraphData)
+ {
+ TStateEditorMap::iterator iter = m_Editors.find(inGraphData);
+ if (iter != m_Editors.end())
+ return iter->second;
+ return TObjPtr();
+ }
+
+ TSignalConnectionPtr SEditorImpl::AddChangeListener(IEditorChangeListener &inListener)
+ {
+ return m_TransactionManager->AddChangeListener(inListener);
+ }
+
+ TTransactionPtr SEditorImpl::BeginTransaction(const TEditorStr &inName)
+ {
+ return m_TransactionManager->BeginTransaction(inName);
+ }
+
+ TTransactionPtr SEditorImpl::GetOpenTransaction()
+ {
+ return m_TransactionManager->GetOpenTransaction();
+ }
+
+ void SEditorImpl::RollbackTransaction() { m_TransactionManager->RollbackTransaction(); }
+
+ void SEditorImpl::EndTransaction() { m_TransactionManager->EndTransaction(); }
+
+ TEditorStr SEditorImpl::Copy(TObjList inObjects, const QT3DSVec2 &inMousePos)
+ {
+ eastl::vector<SStateNode *> theNodeList;
+ for (size_t idx = 0, end = inObjects.size(); idx < end; ++idx) {
+ SStateNode *theNode = StateNodeFromEditor(inObjects[idx]);
+ if (theNode && theNode->m_Type != StateNodeTypes::Transition
+ && theNode->m_Type != StateNodeTypes::SCXML) {
+ theNodeList.push_back(theNode);
+ }
+ }
+ eastl::vector<SNamespacePair> theNamespaces;
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_EditorFoundation->getAllocator(), m_StringTable));
+ NVScopedRefCounted<IDOMWriter> theWriter(
+ IDOMWriter::CreateDOMWriter(m_EditorFoundation->getAllocator(), "scxml_fragment",
+ m_StringTable)
+ .first);
+
+ eastl::vector<SStateNode *> rootNodes = CXMLIO::SaveSCXMLFragment(
+ *m_StateContext, *m_EditorFoundation->m_Foundation, *m_StringTable, *theWriter,
+ toDataRef(theNodeList.data(), theNodeList.size()), *this, inMousePos,
+ theNamespaces);
+
+ if (m_CopyPasteListener)
+ m_CopyPasteListener->OnCopy(this, rootNodes, *theWriter, theNamespaces);
+
+ SEditorImplStrIOStream theStream;
+ CDOMSerializer::WriteXMLHeader(theStream);
+ CDOMSerializer::Write(m_EditorFoundation->getAllocator(), *theWriter->GetTopElement(),
+ theStream, *m_StringTable,
+ toDataRef(theNamespaces.data(), theNamespaces.size()), false);
+ return theStream.m_Str;
+ }
+
+ bool SEditorImpl::CanPaste(TObjPtr inTarget)
+ {
+ SStateNode *theNode = StateNodeFromEditor(inTarget);
+ if (theNode) {
+ return theNode->m_Type != StateNodeTypes::Transition
+ && theNode->m_Type != StateNodeTypes::History
+ && theNode->m_Type != StateNodeTypes::Final;
+ }
+
+ return false;
+ }
+
+ void SEditorImpl::AddNewPasteObjectToTransaction(SStateNode &inNode)
+ {
+ TObjPtr editorPtr = ToEditor(inNode);
+ if (editorPtr) {
+ m_TransactionManager->m_Transaction->m_Changes.push_back(new SChange(*editorPtr));
+ m_TransactionManager->OnObjectCreated(editorPtr);
+ Option<SValue> children = editorPtr->GetPropertyValue("children");
+ if (children.hasValue()) {
+ TObjList childList = children->getData<TObjList>();
+ for (size_t idx = 0, end = childList.size(); idx < end; ++idx) {
+ SStateNode *childNode = StateNodeFromEditor(childList[idx]);
+ if (childNode)
+ AddNewPasteObjectToTransaction(*childNode);
+ }
+ }
+ }
+ }
+
+ void RecurseAndCheckForValidHistoryAfterPaste(TObjPtr inNode, SEditorImpl &inEditor)
+ {
+ if (AreEqual(inNode->TypeName(), "history")) {
+ inEditor.CheckAndSetValidHistoryDefault(inNode);
+ } else {
+ Option<SValue> childrenOpt = inNode->GetPropertyValue("children");
+ if (childrenOpt.hasValue() == false) {
+ return;
+ }
+ TObjList children = childrenOpt->getData<TObjList>();
+ for (size_t childIdx = 0, childEnd = children.size(); childIdx < childEnd;
+ ++childIdx) {
+ RecurseAndCheckForValidHistoryAfterPaste(children[childIdx], inEditor);
+ }
+ }
+ }
+
+ void SEditorImpl::Paste(const TEditorStr &inCopiedObjects, TObjPtr inTarget,
+ const QT3DSVec2 &inMousePos)
+ {
+ Option<SValue> childrenOpt = inTarget->GetPropertyValue("children");
+ if (childrenOpt.hasValue() == false) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ TObjList children = childrenOpt->getData<TObjList>();
+
+ SEditorImplStrInStream inStream(inCopiedObjects);
+ NVScopedRefCounted<IDOMFactory> theFactory =
+ IDOMFactory::CreateDOMFactory(m_EditorFoundation->getAllocator(), m_StringTable);
+ eastl::pair<SNamespacePairNode *, SDOMElement *> readResult =
+ CDOMSerializer::Read(*theFactory, inStream);
+ SDOMElement *elem = readResult.second;
+ if (elem == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ NVScopedRefCounted<IDOMReader> theReader = IDOMReader::CreateDOMReader(
+ m_EditorFoundation->getAllocator(), *elem, m_StringTable, *theFactory);
+ eastl::pair<eastl::vector<SStateNode *>, CXMLIO::TIdRemapMap> theRootsPair;
+ {
+ IDOMReader::Scope __readerScope(*theReader);
+ theRootsPair =
+ CXMLIO::LoadSCXMLFragment(m_AutoAllocator, *m_EditorFoundation->m_Foundation,
+ *theReader, *m_StringTable, *m_StateContext, *this);
+ }
+ eastl::vector<SStateNode *> &theObjects(theRootsPair.first);
+ if (m_CopyPasteListener)
+ m_CopyPasteListener->OnPaste(this, *theReader, theRootsPair.second);
+ // Merge any namespaces into the context's namespace list.
+ if (m_StateContext->GetDOMFactory()) {
+ for (SNamespacePairNode *fragNode = readResult.first; fragNode;
+ fragNode = fragNode->m_NextNode) {
+ bool found = false;
+ for (SNamespacePairNode *ctxNode = m_StateContext->GetFirstNSNode();
+ ctxNode && !found; ctxNode = ctxNode->m_NextNode) {
+ if (ctxNode->m_Namespace == fragNode->m_Namespace)
+ found = true;
+ }
+ if (!found) {
+ SNamespacePairNode *newNode =
+ m_StateContext->GetDOMFactory()->NextNSPairNode(
+ fragNode->m_Namespace, fragNode->m_Abbreviation);
+ newNode->m_NextNode = m_StateContext->GetFirstNSNode();
+ m_StateContext->SetFirstNSNode(*newNode);
+ }
+ }
+ }
+ QT3DSVec2 globalPos(0, 0);
+ if (inTarget) {
+ for (TObjPtr posObj = inTarget; posObj; posObj = posObj->Parent()) {
+ Option<SValue> theData = posObj->GetPropertyValue("position");
+ if (theData.hasValue())
+ globalPos += theData->getData<QT3DSVec2>();
+ }
+ }
+ QT3DSVec2 theMousePos = inMousePos - globalPos;
+ for (size_t idx = 0, end = theObjects.size(); idx < end; ++idx) {
+ SStateNode *currentObject = theObjects[idx];
+ QT3DSVec2 objPos(0, 0);
+ if (currentObject->m_StateNodeFlags.HasPosition())
+ objPos = currentObject->m_Position;
+ currentObject->SetPosition(objPos + theMousePos);
+ if (m_TransactionManager->m_Transaction)
+ AddNewPasteObjectToTransaction(*currentObject);
+ TObjPtr editorPtr = ToEditor(*currentObject);
+ if (editorPtr.mPtr)
+ children.push_back(editorPtr);
+ }
+ // This sets up the valid parents. Without that, checking if a history node is valid
+ // is meaningless.
+ inTarget->SetPropertyValue("children", children);
+ for (size_t idx = 0, end = children.size(); idx < end; ++idx) {
+ RecurseAndCheckForValidHistoryAfterPaste(children[idx], *this);
+ }
+ }
+
+ bool SEditorImpl::IsDerivedFrom(SStateNode &child, SStateNode &parent)
+ {
+ if (&child == &parent)
+ return true;
+ if (child.m_Parent)
+ return IsDerivedFrom(*child.m_Parent, parent);
+ return false;
+ }
+
+ // This method should always return a value because the scxml root item
+ // is the parent of everyone else.
+ SStateNode &SEditorImpl::GetLeastCommonAncestor(SStateNode &lhs, SStateNode &rhs)
+ {
+ // If lhs is derived from rhs, return rhs
+ if (IsDerivedFrom(lhs, rhs))
+ return rhs;
+ // vice versa
+ else if (IsDerivedFrom(rhs, lhs))
+ return lhs;
+
+ // Else wander up the tree till we find a common ancestor.
+ QT3DS_ASSERT(lhs.m_Parent != NULL);
+ if (lhs.m_Parent != NULL)
+ return GetLeastCommonAncestor(*lhs.m_Parent, rhs);
+
+ return lhs;
+ }
+
+ TObjPtr SEditorImpl::GetLeastCommonAncestor(NVConstDataRef<TObjPtr> inObjects)
+ {
+ SStateNode *lastAncestor = NULL;
+ if (inObjects.size() == 0)
+ return TObjPtr();
+
+ for (QT3DSU32 idx = 0, end = inObjects.size(); idx < end; ++idx) {
+ if (lastAncestor == NULL) {
+ lastAncestor = StateNodeFromEditor(inObjects[idx]);
+ } else {
+ SStateNode *nextNode = StateNodeFromEditor(inObjects[idx]);
+ if (nextNode != NULL) {
+ lastAncestor = &GetLeastCommonAncestor(*lastAncestor, *nextNode);
+ }
+ }
+ }
+
+ if (lastAncestor)
+ return ToEditor(*lastAncestor);
+ else
+ return TObjPtr();
+ }
+
+ void GetCancelableSendIds(TExecutableContentList &inList, TObjList &outList,
+ SEditorImpl &inEditor);
+
+ void RecursiveGetCancelableSendIds(SExecutableContent &inNode, TObjList &outList,
+ SEditorImpl &inEditor)
+ {
+ if (inNode.m_Type == ExecutableContentTypes::Send) {
+ SSend &theSend = static_cast<SSend &>(inNode);
+ if (IExecutionContext::ParseTimeStrToMilliseconds(theSend.m_Delay)
+ || !isTrivial(theSend.m_DelayExpr)) {
+ outList.push_back(inEditor.ExecutableContentToEditor(theSend));
+ }
+ }
+ GetCancelableSendIds(inNode.m_Children, outList, inEditor);
+ }
+
+ void GetCancelableSendIds(TExecutableContentList &inList, TObjList &outList,
+ SEditorImpl &inEditor)
+ {
+ for (TExecutableContentList::iterator iter = inList.begin(), end = inList.end();
+ iter != end; ++iter)
+ RecursiveGetCancelableSendIds(*iter, outList, inEditor);
+ }
+
+ void RecursiveGetCancelableSendIds(SStateNode &inNode, TObjList &outList,
+ SEditorImpl &inEditor)
+ {
+ if (inNode.m_Type == StateNodeTypes::Transition) {
+ STransition &transition = static_cast<STransition &>(inNode);
+ GetCancelableSendIds(transition.m_ExecutableContent, outList, inEditor);
+ } else {
+ TOnEntryList *entryList = inNode.GetOnEntryList();
+ TOnExitList *exitList = inNode.GetOnExitList();
+ if (entryList) {
+ for (TOnEntryList::iterator iter = entryList->begin(), end = entryList->end();
+ iter != end; ++iter)
+ GetCancelableSendIds(iter->m_ExecutableContent, outList, inEditor);
+ }
+ if (exitList) {
+ for (TOnExitList::iterator iter = exitList->begin(), end = exitList->end();
+ iter != end; ++iter)
+ GetCancelableSendIds(iter->m_ExecutableContent, outList, inEditor);
+ }
+ }
+ TStateNodeList *childList = inNode.GetChildren();
+ if (childList) {
+ for (TStateNodeList::iterator childIter = childList->begin(),
+ childEnd = childList->end();
+ childIter != childEnd; ++childIter)
+ RecursiveGetCancelableSendIds(*childIter, outList, inEditor);
+ }
+ }
+
+ // TODO - think about a fast and easy way to cache things (and keep the cache correct).
+ TObjList SEditorImpl::GetCancelableSendIds()
+ {
+ TObjList retval;
+ if (m_StateContext->GetRoot())
+ RecursiveGetCancelableSendIds(*m_StateContext->GetRoot(), retval, *this);
+ return retval;
+ }
+
+ static const char8_t *whitespace = "\n\r\t ";
+
+ void StripIfLastChar(TEditorStr &str, char data)
+ {
+ if (str.size() && str.back() == data)
+ str.resize(str.size() - 1);
+ }
+
+ void AddUnique(TEditorStrList &list, const TEditorStr &str)
+ {
+ if (eastl::find(list.begin(), list.end(), str) == list.end())
+ list.push_back(str);
+ }
+
+ void RecursiveScanExecutableContentForEvents(TExecutableContentList &contentList,
+ TEditorStrList &ioStrList)
+ {
+ for (TExecutableContentList::iterator iter = contentList.begin(),
+ end = contentList.end();
+ iter != end; ++iter) {
+ if (iter->m_Type == ExecutableContentTypes::Send) {
+ SSend &theSend = static_cast<SSend &>(*iter);
+ if (!isTrivial(theSend.m_Event)) {
+ AddUnique(ioStrList, theSend.m_Event.c_str());
+ }
+ } else if (iter->m_Type == ExecutableContentTypes::Raise) {
+ SRaise &theRaise = static_cast<SRaise &>(*iter);
+ if (!isTrivial(theRaise.m_Event)) {
+ AddUnique(ioStrList, theRaise.m_Event.c_str());
+ }
+ }
+ RecursiveScanExecutableContentForEvents(iter->m_Children, ioStrList);
+ }
+ }
+
+ template <typename TListType>
+ void ScanItemListForEvents(TListType *itemList, TEditorStrList &ioEvents)
+ {
+ if (itemList) {
+ typedef typename TListType::iterator iterator;
+ for (iterator iter = itemList->begin(), end = itemList->end(); iter != end;
+ ++iter) {
+ RecursiveScanExecutableContentForEvents(iter->m_ExecutableContent, ioEvents);
+ }
+ }
+ }
+
+ void RecursiveGetStateMachineEvents(SStateNode &inNode, TEditorStrList &ioList)
+ {
+ if (inNode.m_Type == StateNodeTypes::Transition) {
+ STransition &theTransition = static_cast<STransition &>(inNode);
+ TEditorStr transStr(theTransition.m_Event);
+ for (size_t pos = transStr.find_first_not_of(whitespace); pos != TEditorStr::npos;
+ pos = transStr.find_first_not_of(whitespace, pos)) {
+ size_t end = transStr.find_first_of(whitespace, pos);
+ if (end == TEditorStr::npos)
+ end = transStr.size();
+ TEditorStr subStr = transStr.substr(pos, end - pos);
+ pos = end;
+ StripIfLastChar(subStr, '*');
+ StripIfLastChar(subStr, '.');
+ AddUnique(ioList, subStr);
+ }
+ RecursiveScanExecutableContentForEvents(theTransition.m_ExecutableContent, ioList);
+ } else {
+ ScanItemListForEvents(inNode.GetOnEntryList(), ioList);
+ ScanItemListForEvents(inNode.GetOnExitList(), ioList);
+ }
+ TStateNodeList *children = inNode.GetChildren();
+ if (children) {
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter)
+ RecursiveGetStateMachineEvents(*iter, ioList);
+ }
+ }
+
+ TEditorStrList SEditorImpl::GetStateMachineEvents()
+ {
+ TEditorStrList retval;
+ if (m_StateContext->GetRoot())
+ RecursiveGetStateMachineEvents(*m_StateContext->GetRoot(), retval);
+ eastl::sort(retval.begin(), retval.end());
+ return retval;
+ }
+
+ eastl::pair<SExecutableContent *, TObjPtr>
+ SEditorImpl::CreateExecutableContent(const char8_t *inTypeName)
+ {
+ SExecutableContent *newExecutable = NULL;
+ TObjPtr newObj;
+ if (AreEqual(inTypeName, SSendEditor::GetTypeStr())) {
+ eastl::pair<SSend *, TObjPtr> theNewEditor = CreateEditorAndObject<SSend>();
+ GenerateUniqueId(*theNewEditor.first, "send");
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else if (AreEqual(inTypeName, SScriptEditor::GetTypeStr())) {
+ eastl::pair<SScript *, TObjPtr> theNewEditor = CreateEditorAndObject<SScript>();
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else if (AreEqual(inTypeName, SCancelEditor::GetTypeStr())) {
+ eastl::pair<SCancel *, TObjPtr> theNewEditor = CreateEditorAndObject<SCancel>();
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ return eastl::make_pair(newExecutable, newObj);
+ }
+
+ struct SExecListAddChange : public IChange
+ {
+ TExecutableContentList &parentList;
+ SExecutableContent *prevSibling;
+ SExecutableContent &item;
+ TObjPtr editorObj;
+ QT3DSI32 mRefCount;
+ bool m_AddOnDo;
+ SExecListAddChange(TExecutableContentList &plist, SExecutableContent *prev,
+ SExecutableContent &_item, TObjPtr objPtr, bool addOnDo)
+ : parentList(plist)
+ , prevSibling(prev)
+ , item(_item)
+ , editorObj(objPtr)
+ , mRefCount(0)
+ , m_AddOnDo(addOnDo)
+ {
+ }
+
+ void Add()
+ {
+ if (prevSibling)
+ parentList.insert_after(*prevSibling, item);
+ else
+ parentList.push_front(item);
+ }
+ void Remove() { parentList.remove(item); }
+
+ virtual void Do()
+ {
+ if (m_AddOnDo)
+ Add();
+ else
+ Remove();
+ }
+
+ virtual void Undo()
+ {
+ if (m_AddOnDo)
+ Remove();
+ else
+ Add();
+ }
+
+ virtual TObjPtr GetEditor() { return editorObj; }
+
+ virtual void addRef() { ++mRefCount; }
+
+ virtual void release()
+ {
+ --mRefCount;
+ if (mRefCount <= 0)
+ delete this;
+ }
+ };
+
+ void SEditorImpl::ReplaceExecutableContent(SExecutableContent &oldContent,
+ SExecutableContent &newContent)
+ {
+ SStateNode *stateNodeParent = oldContent.m_StateNodeParent;
+ SExecutableContent *execContentParent = oldContent.m_Parent;
+ SExecutableContentParentInfo parentInfo(oldContent, *this);
+ SExecutableContent *prevSibling = oldContent.m_PreviousSibling;
+
+ // Can't use remove object from graph here because it is too aggressive.
+ NVScopedRefCounted<SExecListAddChange> oldChange = new SExecListAddChange(
+ *parentInfo.parentList, prevSibling, oldContent, parentInfo.editorObj, false);
+ oldChange->Do();
+ newContent.m_StateNodeParent = stateNodeParent;
+ newContent.m_Parent = execContentParent;
+ NVScopedRefCounted<SExecListAddChange> newChange = new SExecListAddChange(
+ *parentInfo.parentList, prevSibling, newContent, parentInfo.editorObj, true);
+ newChange->Do();
+ if (GetOpenTransactionImpl()) {
+ GetOpenTransactionImpl()->m_Changes.push_back(oldChange.mPtr);
+ GetOpenTransactionImpl()->m_Changes.push_back(newChange.mPtr);
+ }
+ }
+
+ TObjPtr SEditorImpl::ChangeExecutableContentType(TObjPtr inContent, const char8_t *newType)
+ {
+ SExecutableContent *theContent = ExecutableContentFromEditor(inContent);
+ if (!theContent) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ SExecutableContentParentInfo parentInfo(*theContent, *this);
+ // find the index of the item in the parent list.
+ if (parentInfo.parentList == NULL) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ eastl::pair<SExecutableContent *, TObjPtr> theNewContent =
+ CreateExecutableContent(newType);
+ if (!theNewContent.first) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ ReplaceExecutableContent(*theContent, *theNewContent.first);
+ return theNewContent.second;
+ }
+
+ TEditorStr SEditorImpl::ToXML(TObjPtr inContent)
+ {
+ SExecutableContent *theContent = ExecutableContentFromEditor(inContent);
+ if (!theContent) {
+ QT3DS_ASSERT(false);
+ return TEditorStr();
+ }
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_EditorFoundation->getAllocator(), m_StringTable));
+ NVScopedRefCounted<IDOMWriter> theWriter(
+ IDOMWriter::CreateDOMWriter(m_EditorFoundation->getAllocator(), "innerXML",
+ m_StringTable, 0)
+ .first);
+ CXMLIO::ToEditableXml(*m_StateContext, m_EditorFoundation->getFoundation(),
+ *m_StringTable, *theWriter, *theContent, *this);
+ SDOMElement *theTopElem = theWriter->GetTopElement();
+ SDOMElement *xmlElem = &theTopElem->m_Children.front();
+ SEditorImplStrIOStream theStream;
+ if (xmlElem) {
+
+ eastl::vector<SNamespacePair> theNamespaces;
+ for (SNamespacePairNode *theNode = m_StateContext->GetFirstNSNode(); theNode;
+ theNode = theNode->m_NextNode)
+ theNamespaces.push_back(*theNode);
+ if (theNamespaces.empty()) {
+ theNamespaces.push_back(SNamespacePair(
+ m_StringTable->RegisterStr("http://www.w3.org/2005/07/scxml")));
+ theNamespaces.push_back(
+ SNamespacePair(m_StringTable->RegisterStr("http://qt.io/qt3dstudio/uicstate"),
+ m_StringTable->RegisterStr("Qt3DS")));
+ }
+ CDOMSerializer::Write(
+ m_EditorFoundation->getAllocator(), *xmlElem, theStream, *m_StringTable,
+ toDataRef(theNamespaces.data(), theNamespaces.size()), false, 0, false);
+ }
+ return theStream.m_Str;
+ }
+
+ struct SStrXMLErrorHandler : public CXmlErrorHandler
+ {
+ TEditorStr m_Error;
+ virtual void OnXmlError(TXMLCharPtr errorName, int line, int /*column*/)
+ {
+ char buf[256];
+ sprintf(buf, "Error on line %d: ", line);
+ m_Error.assign(buf);
+ m_Error.append(nonNull(errorName));
+ }
+ };
+
+ eastl::pair<TEditorStr, TObjPtr> SEditorImpl::FromXML(TObjPtr inContent,
+ const TEditorStr &ioEditedXML)
+ {
+ TEditorStr wrappedXML("<snippet ");
+ eastl::vector<SNamespacePair> theNamespaces;
+ for (SNamespacePairNode *theNode = m_StateContext->GetFirstNSNode(); theNode;
+ theNode = theNode->m_NextNode)
+ theNamespaces.push_back(*theNode);
+ if (theNamespaces.empty()) {
+ theNamespaces.push_back(
+ SNamespacePair(m_StringTable->RegisterStr("http://www.w3.org/2005/07/scxml")));
+ theNamespaces.push_back(
+ SNamespacePair(m_StringTable->RegisterStr("http://qt.io/qt3dstudio/uicstate"),
+ m_StringTable->RegisterStr("Qt3DS")));
+ }
+ for (size_t idx = 0, end = theNamespaces.size(); idx < end; ++idx) {
+ SNamespacePair &theNode(theNamespaces[idx]);
+ wrappedXML.append(" xmlns");
+ if (theNode.m_Abbreviation.IsValid()) {
+ wrappedXML.append(":");
+ wrappedXML.append(theNode.m_Abbreviation.c_str());
+ }
+ wrappedXML.append("=\"");
+ wrappedXML.append(theNode.m_Namespace.c_str());
+ wrappedXML.append("\"");
+ }
+ wrappedXML.append(">");
+ wrappedXML.append(ioEditedXML);
+ wrappedXML.append("</snippet>");
+ SEditorImplStrInStream theStream(wrappedXML);
+
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_EditorFoundation->getAllocator(), m_StringTable));
+ SStrXMLErrorHandler errorHandler;
+ SDOMElement *theElem =
+ CDOMSerializer::Read(*theFactory, theStream, &errorHandler).second;
+ TObjPtr retval;
+ if (theElem) {
+ NVScopedRefCounted<IDOMReader> theReader(IDOMReader::CreateDOMReader(
+ m_EditorFoundation->getAllocator(), theElem->m_Children.front(), m_StringTable,
+ theFactory));
+ SExecutableContent *oldContent = ExecutableContentFromEditor(inContent);
+ SExecutableContent *theContent =
+ CXMLIO::FromEditableXML(*theReader, m_EditorFoundation->getFoundation(),
+ *m_StateContext, *m_StringTable, m_AutoAllocator, *this,
+ oldContent->m_StateNodeParent, oldContent->m_Parent);
+ if (theContent) {
+ ReplaceExecutableContent(*oldContent, *theContent);
+ retval = ExecutableContentToEditor(*theContent);
+ } else {
+ errorHandler.m_Error.assign("The \"");
+ errorHandler.m_Error.append(theReader->GetElementName().c_str());
+ errorHandler.m_Error.append("\" action is not recognized. It must be one of:");
+ eastl::vector<eastl::string> contentNames =
+ CXMLIO::GetSupportedExecutableContentNames();
+ for (size_t idx = 0, end = contentNames.size(); idx < end; ++idx) {
+ if (idx != 0)
+ errorHandler.m_Error.append(",");
+
+ errorHandler.m_Error.append(" \"");
+ errorHandler.m_Error.append(contentNames[idx].c_str());
+ errorHandler.m_Error.append("\"");
+ }
+ errorHandler.m_Error.append(".");
+ }
+ }
+
+ return eastl::make_pair(errorHandler.m_Error, retval);
+ }
+
+ void SEditorImpl::ReleaseEditor(void *inData)
+ {
+ TStateEditorMap::iterator iter = m_Editors.find(inData);
+ if (iter != m_Editors.end())
+ m_Editors.erase(iter);
+ }
+
+ bool SEditorImpl::Save(const char8_t *inFileName)
+ {
+ CFileSeekableIOStream theFile(inFileName, FileWriteFlags());
+ if (theFile.IsOpen() == false) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+
+ Save(theFile);
+ return true;
+ }
+
+ void SEditorImpl::Save(IOutStream &inStream) { m_StateContext->Save(inStream, this); }
+
+ bool SEditorImpl::Load(IInStream &inStream, const char8_t *inFilename)
+ {
+ bool theRetval = false;
+ m_StateContext = IStateContext::Load(m_AutoAllocator, *m_EditorFoundation->m_Foundation,
+ inStream, inFilename, m_StringTable.mPtr, this);
+ if (m_StateContext.mPtr == 0)
+ m_StateContext = IStateContext::Create(*m_EditorFoundation->m_Foundation);
+ else
+ theRetval = true;
+ return theRetval;
+ }
+
+ STransaction *SEditorImpl::GetOpenTransactionImpl()
+ {
+ return m_TransactionManager->m_Transaction.mPtr;
+ }
+
+ void SEditorImpl::SetIdProperty(SStateNode &inNode, const SValue &inData,
+ CRegisteredString & /*inTarget*/)
+ {
+ IStateContext &theContext = *m_StateContext;
+ theContext.EraseId(inNode.m_Id);
+ TEditorStr potentialId = inData.getData<TEditorStr>();
+ if (potentialId.size() == 0) {
+ switch (inNode.m_Type) {
+ case StateNodeTypes::State:
+ potentialId = "state";
+ break;
+ case StateNodeTypes::Parallel:
+ potentialId = "parallel";
+ break;
+ case StateNodeTypes::Transition:
+ potentialId = "transition";
+ break;
+ case StateNodeTypes::Final:
+ potentialId = "final";
+ break;
+ case StateNodeTypes::History:
+ potentialId = "history";
+ break;
+ case StateNodeTypes::SCXML:
+ potentialId = "scxml";
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ potentialId = "id";
+ break;
+ }
+ }
+ CRegisteredString oldId = inNode.m_Id;
+ inNode.m_Id = CRegisteredString();
+ GenerateUniqueId(inNode, potentialId.c_str());
+ // This is a bad hack to get around:
+ // 1. user changes id calling this function
+ if (m_CopyPasteListener) {
+ m_CopyPasteListener->OnIDChange(this, inNode, oldId);
+ }
+ }
+
+ void SEditorImpl::SetIdProperty(SSend &inNode, const SValue &inData,
+ CRegisteredString & /*inTarget*/)
+ {
+ IStateContext &theContext = *m_StateContext;
+ theContext.EraseId(inNode.m_Id);
+ TEditorStr potentialId = inData.getData<TEditorStr>();
+ if (potentialId.size() == 0)
+ potentialId = "send";
+
+ inNode.m_Id = CRegisteredString();
+ GenerateUniqueId(inNode, potentialId.c_str());
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, NVConstDataRef<SStateNode *> &inTarget)
+ {
+ if (inTarget.mData != NULL) {
+ m_AutoAllocator.deallocate((void *)inTarget.mData);
+ inTarget.mData = NULL;
+ inTarget.mSize = 0;
+ }
+ if (inData.hasValue()) {
+ TObjList inList(inData->getData<TObjList>());
+ eastl::vector<SStateNode *> validObjects;
+ for (size_t idx = 0, end = inList.size(); idx < end; ++idx) {
+ TObjPtr theObj = inList[idx];
+ SStateNode *theNode = StateNodeFromEditor(theObj);
+ if (theNode) {
+ validObjects.push_back(theNode);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ if (validObjects.empty() == false) {
+ NVConstDataRef<SStateNode *> &outList(inTarget);
+ size_t byteCount = validObjects.size() * sizeof(SStateNode *);
+ outList.mData = (SStateNode * const *)m_AutoAllocator.allocate(
+ byteCount, "graph ref list data", __FILE__, __LINE__);
+ memCopy((void *)outList.mData, validObjects.data(), byteCount);
+ outList.mSize = validObjects.size();
+ }
+ }
+ }
+
+ SValue SEditorImpl::Get(NVConstDataRef<SStateNode *> &inTarget)
+ {
+ TObjList retval;
+ NVConstDataRef<SStateNode *> inRefList(inTarget);
+ for (QT3DSU32 idx = 0, end = inRefList.size(); idx < end; ++idx) {
+ TObjPtr obj = ToEditor(inRefList[idx]);
+ if (obj)
+ retval.push_back(obj);
+ else {
+ // This shouldn't ever happen, but it might when loading
+ // an invalid file.
+ QT3DS_ASSERT(false);
+ }
+ }
+ return retval;
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, STransition *&inTarget)
+ {
+ inTarget = NULL;
+ if (inData.hasValue()) {
+ TObjPtr theData(inData->getData<TObjPtr>());
+ inTarget = FromEditor<STransition>(theData);
+ }
+ }
+
+ Option<SValue> SEditorImpl::Get(STransition *&inTarget)
+ {
+ return SValue(ToEditor(inTarget));
+ }
+
+ void SEditorImpl::SetInitial(const TObjList &inList, STransition *&outTransitionPtr)
+ {
+ if (outTransitionPtr) {
+ Set(SValue(inList), outTransitionPtr->m_Target);
+ if (outTransitionPtr->m_Target.mSize == 0
+ && outTransitionPtr->m_ExecutableContent.empty())
+ outTransitionPtr = NULL;
+ } else {
+ NVConstDataRef<SStateNode *> theTemp;
+ Set(SValue(inList), theTemp);
+ if (theTemp.mSize) {
+ outTransitionPtr = QT3DS_NEW(m_AutoAllocator, STransition)();
+ outTransitionPtr->m_Target = theTemp;
+ }
+ }
+ }
+
+ SValue SEditorImpl::GetInitial(STransition *inInitialTrans)
+ {
+ if (inInitialTrans)
+ return Get(inInitialTrans->m_Target);
+
+ return TObjList();
+ }
+ static inline STransition **GetInitialTransitionPtr(SStateNode &inParent)
+ {
+ StateNodeTypes::Enum typeEnum(inParent.m_Type);
+ STransition **theParentTransPtr = NULL;
+ if (typeEnum == StateNodeTypes::SCXML)
+ theParentTransPtr = &static_cast<SSCXML &>(inParent).m_Initial;
+ else if (typeEnum == StateNodeTypes::State)
+ theParentTransPtr = &static_cast<SState &>(inParent).m_Initial;
+ return theParentTransPtr;
+ }
+
+ static inline bool IsFirstChild(SStateNode &inChild, SStateNode &inParent)
+ {
+ TStateNodeList *childList = inParent.GetChildren();
+ SStateNode *firstViableChild = NULL;
+ if (childList) {
+ for (TStateNodeList::iterator iter = childList->begin(), end = childList->end();
+ iter != end && firstViableChild == NULL; ++iter) {
+ StateNodeTypes::Enum typeEnum = iter->m_Type;
+ if (typeEnum == StateNodeTypes::State || typeEnum == StateNodeTypes::Parallel
+ || typeEnum == StateNodeTypes::Final) {
+ firstViableChild = &(*iter);
+ }
+ }
+ }
+ return firstViableChild == &inChild;
+ }
+
+ struct SInitialTargetChange : public IChange
+ {
+ TObjPtr m_EditorObj;
+ SStateNode &m_Parent;
+ STransition *m_InitialTransition;
+ STransition *m_FinalTransition;
+ QT3DSI32 m_RefCount;
+ // Implicitly setting c to be the initial target.
+ SInitialTargetChange(TObjPtr editorObj, SStateNode &p, SStateNode &c,
+ SEditorImpl &editor)
+ : m_EditorObj(editorObj)
+ , m_Parent(p)
+ , m_InitialTransition(NULL)
+ , m_FinalTransition(NULL)
+ , m_RefCount(0)
+ {
+ STransition **theTransitionPtr = GetInitialTransitionPtr(m_Parent);
+ QT3DS_ASSERT(theTransitionPtr);
+ if (theTransitionPtr) {
+ m_InitialTransition = *theTransitionPtr;
+ theTransitionPtr[0] = NULL;
+ bool firstChild = IsFirstChild(c, p);
+ if (!firstChild) {
+ TObjPtr editorObj = editor.Create("transition", "");
+ TObjList targets;
+ targets.push_back(editor.ToEditor(c));
+ editorObj->SetPropertyValue("target", targets);
+ SStateNode *stateNode = editor.StateNodeFromEditor(editorObj);
+ m_FinalTransition = static_cast<STransition *>(stateNode);
+ m_FinalTransition->m_Parent = &p;
+ *theTransitionPtr = m_FinalTransition;
+ }
+ }
+ }
+ virtual void addRef() { ++m_RefCount; }
+ virtual void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObj; }
+
+ template <typename TNodeType>
+ void Apply(STransition *trans, TNodeType &node)
+ {
+ node.m_Initial = trans;
+ }
+
+ virtual void Do()
+ {
+ if (m_Parent.m_Type == StateNodeTypes::SCXML)
+ Apply(m_FinalTransition, static_cast<SSCXML &>(m_Parent));
+ else if (m_Parent.m_Type == StateNodeTypes::State)
+ Apply(m_FinalTransition, static_cast<SState &>(m_Parent));
+ }
+
+ virtual void Undo()
+ {
+ if (m_Parent.m_Type == StateNodeTypes::SCXML)
+ Apply(m_InitialTransition, static_cast<SSCXML &>(m_Parent));
+ else if (m_Parent.m_Type == StateNodeTypes::State)
+ Apply(m_InitialTransition, static_cast<SState &>(m_Parent));
+ }
+ };
+
+ void SEditorImpl::SetInitialTarget(const Option<SValue> &inData, SStateNode &inNode)
+ {
+ if (inNode.m_Type == StateNodeTypes::Transition
+ /**|| inNode.m_Type == StateNodeTypes::History **/) {
+ QT3DS_ASSERT(false);
+ }
+ SStateNode *theParent = inNode.m_Parent;
+ if (theParent) {
+ STransition **theParentTransPtr = GetInitialTransitionPtr(*theParent);
+
+ if (theParentTransPtr) {
+ bool newValue = false;
+ if (inData.hasValue())
+ newValue = inData->getData<bool>();
+
+ NVScopedRefCounted<SInitialTargetChange> theChange =
+ new SInitialTargetChange(ToEditor(*theParent), *theParent, inNode, *this);
+ theChange->Do();
+ STransaction *theTransaction = GetOpenTransactionImpl();
+ if (theTransaction) {
+ theTransaction->m_Changes.push_back(theChange.mPtr);
+ TStateNodeList *children = theParent->GetChildren();
+ for (TStateNodeList::iterator iter = children->begin(),
+ end = children->end();
+ iter != end; ++iter) {
+ theTransaction->m_Changes.push_back(new SChange(*ToEditor(*iter)));
+ }
+ }
+ }
+ }
+ }
+
+ SValue SEditorImpl::IsInitialTarget(SStateNode &inNode)
+ {
+ if (inNode.m_Type == StateNodeTypes::Transition
+ /** || inNode.m_Type == StateNodeTypes::History **/)
+ return SValue(false);
+
+ SStateNode *theParent(inNode.m_Parent);
+ if (theParent) {
+ if (!isTrivial(theParent->GetInitialExpression()))
+ return false;
+
+ STransition *theTransition = theParent->GetInitialTransition();
+ if (theTransition) {
+ for (QT3DSU32 idx = 0, end = theTransition->m_Target.size(); idx < end; ++idx)
+ if (&inNode == theTransition->m_Target[idx])
+ return SValue(true);
+ } else {
+ if (GetInitialTransitionPtr(*theParent))
+ return SValue(IsFirstChild(inNode, *theParent));
+ }
+ }
+ return SValue(false);
+ }
+
+ TEditorStr SEditorImpl::GetDefaultInitialValue(SStateNode &inNode)
+ {
+ TStateNodeList *children = inNode.GetChildren();
+ if (children == NULL)
+ return TEditorStr();
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ if (iter->m_Type != StateNodeTypes::History
+ && iter->m_Type != StateNodeTypes::Transition)
+ return TEditorStr(iter->m_Id.c_str());
+ }
+
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ if (iter->m_Type != StateNodeTypes::Transition)
+ return TEditorStr(iter->m_Id.c_str());
+ }
+
+ return TEditorStr();
+ }
+
+ void SEditorImpl::GetLegalInitialValues(SStateNode &inNode,
+ eastl::vector<CRegisteredString> &outValues)
+ {
+ TStateNodeList *children = inNode.GetChildren();
+ if (children == NULL)
+ return;
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ if (/** iter->m_Type != StateNodeTypes::History && **/ iter->m_Type
+ != StateNodeTypes::Transition)
+ outValues.push_back(iter->m_Id);
+ }
+ }
+
+ struct SInitialComboChange : public IChange
+ {
+ SStateNode &m_Node;
+ TObjPtr m_EditorObj;
+ const TEditorStr m_PreEditorComboValue;
+ const TEditorStr m_PostEditorComboValue;
+ const char8_t *m_PreInitialExpression;
+ const char8_t *m_PostInitialExpression;
+ STransition *m_PreComboTransition;
+ STransition *m_PostComboTransition;
+ QT3DSI32 m_RefCount;
+ SInitialComboChange(SStateNode &node, TObjPtr editObj, const char8_t *preExpr,
+ const char8_t *postExpr, STransition *preCombo,
+ STransition *postCombo, const TEditorStr &preEditorComboValue,
+ const TEditorStr &postEditorComboValue)
+ : m_Node(node)
+ , m_EditorObj(editObj)
+ , m_RefCount(0)
+ , m_PreInitialExpression(preExpr)
+ , m_PostInitialExpression(postExpr)
+ , m_PreComboTransition(preCombo)
+ , m_PostComboTransition(postCombo)
+ , m_PreEditorComboValue(preEditorComboValue)
+ , m_PostEditorComboValue(postEditorComboValue)
+ {
+ }
+
+ virtual void addRef() { ++m_RefCount; }
+ virtual void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObj; }
+
+ template <typename TNodeType, typename TEditorType>
+ void Apply(const char8_t *expr, STransition *trans, const TEditorStr &comboValue,
+ TNodeType &node, TEditorType &editor)
+ {
+ node.m_Initial = trans;
+ node.m_InitialExpr = expr;
+ editor.ApplyInitial(comboValue);
+ // editor.m_InitialComboValue = comboValue;
+ }
+
+ virtual void Do()
+ {
+ if (m_Node.m_Type == StateNodeTypes::SCXML)
+ Apply(m_PostInitialExpression, m_PostComboTransition, m_PostEditorComboValue,
+ static_cast<SSCXML &>(m_Node), static_cast<SSCXMLEditor &>(*m_EditorObj));
+ else if (m_Node.m_Type == StateNodeTypes::State)
+ Apply(m_PostInitialExpression, m_PostComboTransition, m_PostEditorComboValue,
+ static_cast<SState &>(m_Node), static_cast<SStateEditor &>(*m_EditorObj));
+ }
+
+ virtual void Undo()
+ {
+ if (m_Node.m_Type == StateNodeTypes::SCXML)
+ Apply(m_PreInitialExpression, m_PreComboTransition, m_PreEditorComboValue,
+ static_cast<SSCXML &>(m_Node), static_cast<SSCXMLEditor &>(*m_EditorObj));
+ else if (m_Node.m_Type == StateNodeTypes::State)
+ Apply(m_PreInitialExpression, m_PreComboTransition, m_PreEditorComboValue,
+ static_cast<SState &>(m_Node), static_cast<SStateEditor &>(*m_EditorObj));
+ }
+ };
+
+ void SEditorImpl::SetInitialAttribute(const Option<SValue> &inData, SStateNode &inNode)
+ {
+ TObjPtr theEditor = ToEditor(inNode);
+ STransition **targetPtr = NULL;
+ const char8_t **exprPtr = NULL;
+ TEditorStr comboValue;
+ if (inNode.m_Type == StateNodeTypes::State) {
+ targetPtr = &static_cast<SState &>(inNode).m_Initial;
+ exprPtr = &static_cast<SState &>(inNode).m_InitialExpr;
+ comboValue = static_cast<SStateEditor &>(*theEditor).m_InitialComboValue;
+ } else if (inNode.m_Type == StateNodeTypes::SCXML) {
+ targetPtr = &static_cast<SSCXML &>(inNode).m_Initial;
+ exprPtr = &static_cast<SSCXML &>(inNode).m_InitialExpr;
+ comboValue = static_cast<SSCXMLEditor &>(*theEditor).m_InitialComboValue;
+ }
+
+ if (targetPtr == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ STransition *initialTrans = *targetPtr;
+ const char8_t *initialExpr = *exprPtr;
+ TEditorStr initialComboValue = comboValue;
+
+ if (inData.isEmpty()) {
+ *targetPtr = NULL;
+ comboValue = "(script expression)";
+ } else {
+ TEditorStr stateId = inData->getData<TEditorStr>();
+ SStateNode *theNode =
+ this->m_StateContext->FindStateNode(RegisterStr(stateId.c_str()));
+ if (theNode) {
+ *targetPtr = QT3DS_NEW(this->m_AutoAllocator, STransition)();
+ SStateNode **newNode =
+ reinterpret_cast<SStateNode **>(this->m_AutoAllocator.allocate(
+ sizeof(SStateNode *), "TargetList", __FILE__, __LINE__));
+ targetPtr[0]->m_Target = toDataRef(newNode, 1);
+ *const_cast<SStateNode **>(targetPtr[0]->m_Target.begin()) = theNode;
+ targetPtr[0]->m_Target.mSize = 1;
+ comboValue = stateId;
+ } else {
+ *targetPtr = NULL;
+ comboValue = "";
+ }
+ *exprPtr = "";
+ }
+ STransition *postTrans = *targetPtr;
+ const char8_t *postExpr = *exprPtr;
+ TEditorStr postComboValue = comboValue;
+ STransaction *theTransaction = GetOpenTransactionImpl();
+ if (theTransaction) {
+ SInitialComboChange *theChange = new SInitialComboChange(
+ inNode, ToEditor(inNode), initialExpr, postExpr, initialTrans, postTrans,
+ initialComboValue, postComboValue);
+ theChange->Do();
+ theTransaction->m_Changes.push_back(theChange);
+ TStateNodeList *children = inNode.GetChildren();
+ if (children) {
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ theTransaction->m_Changes.push_back(new SChange(*ToEditor(*iter)));
+ }
+ }
+ }
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, SDataModel *&inTarget)
+ {
+ if (inData.hasValue()) {
+ TObjPtr thePtr = inData->getData<TObjPtr>();
+ if (thePtr)
+ inTarget = FromEditor<SDataModel>(thePtr);
+ else
+ inTarget = NULL;
+ } else
+ inTarget = NULL;
+ }
+
+ SValue SEditorImpl::Get(SDataModel *inTarget) { return ToEditor(inTarget); }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, TDataList &inTarget)
+ {
+ while (inTarget.empty() == false)
+ inTarget.remove(inTarget.front());
+ if (inData.hasValue()) {
+ TObjList newData = inData->getData<TObjList>();
+ for (TObjList::iterator iter = newData.begin(), end = newData.end(); iter != end;
+ ++iter) {
+ SData *entry = FromEditor<SData>(*iter);
+ if (entry)
+ inTarget.push_back(*entry);
+ }
+ }
+ }
+
+ SValue SEditorImpl::Get(TDataList &inTarget)
+ {
+ TObjList retval;
+ for (TDataList::iterator iter = inTarget.begin(), end = inTarget.end(); iter != end;
+ ++iter) {
+ TObjPtr item = ToEditor(*iter);
+ if (item)
+ retval.push_back(item);
+ }
+ return retval;
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, NVConstDataRef<QT3DSVec2> &inTarget)
+ {
+ if (inTarget.size()) {
+ m_AutoAllocator.deallocate((void *)inTarget.mData);
+ inTarget.mData = NULL;
+ inTarget.mSize = 0;
+ }
+ if (inData.hasValue()) {
+ TVec2List newData(inData->getData<TVec2List>());
+ if (newData.size()) {
+ size_t newDataSize = newData.size() * sizeof(QT3DSVec2);
+ QT3DSVec2 *newTargetData = (QT3DSVec2 *)m_AutoAllocator.allocate(
+ newDataSize, "Transition::m_Path", __FILE__, __LINE__);
+ memCopy(newTargetData, newData.data(), newDataSize);
+ inTarget = toDataRef(newTargetData, newData.size());
+ }
+ }
+ }
+
+ SValue SEditorImpl::Get(const NVConstDataRef<QT3DSVec2> &inTarget)
+ {
+ TVec2List retval;
+ if (inTarget.mSize)
+ retval.insert(retval.end(), inTarget.begin(), inTarget.end());
+ return retval;
+ }
+
+ void SEditorImpl::Set(const Option<SValue> &inData, SSend *&inTarget)
+ {
+ inTarget = NULL;
+ if (inData.hasValue() && inData->getType() == ValueTypes::ObjPtr) {
+ TObjPtr object = inData->getData<TObjPtr>();
+ SSend *newPtr = FromEditor<SSend>(object);
+ inTarget = newPtr;
+ }
+ }
+
+ SValue SEditorImpl::Get(SSend *inTarget)
+ {
+ if (!inTarget)
+ return SValue(TObjPtr());
+ return ToEditor(inTarget);
+ }
+
+ TObjPtr SEditorImpl::CreateExecutableContent(SStateNode &inParent,
+ const char8_t *inTypeName)
+ {
+ SExecutableContent *newExecutable = NULL;
+ TObjPtr newObj;
+ if (AreEqual(inTypeName, SSendEditor::GetTypeStr())) {
+ eastl::pair<SSend *, TObjPtr> theNewEditor = CreateEditorAndObject<SSend>();
+ GenerateUniqueId(*theNewEditor.first, "send");
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else if (AreEqual(inTypeName, SScriptEditor::GetTypeStr())) {
+ eastl::pair<SScript *, TObjPtr> theNewEditor = CreateEditorAndObject<SScript>();
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else if (AreEqual(inTypeName, SCancelEditor::GetTypeStr())) {
+ eastl::pair<SCancel *, TObjPtr> theNewEditor = CreateEditorAndObject<SCancel>();
+ newExecutable = theNewEditor.first;
+ newObj = theNewEditor.second;
+ } else {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ if (newExecutable)
+ newExecutable->m_StateNodeParent = &inParent;
+ return newObj;
+ }
+
+ SValue SEditorImpl::GetSendId(const char8_t *expression)
+ {
+ if (isTrivial(expression))
+ return SValue((QT3DSU32)0);
+ return static_cast<QT3DSU32>(IExecutionContext::ParseTimeStrToMilliseconds(expression));
+ }
+
+ void SEditorImpl::SetSendId(const Option<SValue> &inData, const char8_t *&outExpression)
+ {
+ if (!isTrivial(outExpression))
+ m_AutoAllocator.deallocate(const_cast<char8_t *>(outExpression));
+
+ outExpression = "";
+
+ if (inData.hasValue() && inData->getType() == ValueTypes::U32) {
+ QT3DSU32 expr = inData->getData<QT3DSU32>();
+ char buffer[64];
+ sprintf(buffer, "%u", expr);
+ int len = strlen(buffer);
+ if (len < 61) {
+ buffer[len] = 'm';
+ buffer[len + 1] = 's';
+ buffer[len + 2] = 0;
+ }
+ outExpression = ToGraphStr(buffer);
+ }
+ }
+
+ IStateContext &SEditorImpl::GetStateContext() { return *m_StateContext; }
+
+ void SEditorImpl::SetTransactionManager(STransactionManagerImpl &manager)
+ {
+ m_TransactionManager = &manager;
+ }
+
+ bool IsLegalHistoryTarget(StateNodeTypes::Enum inType)
+ {
+ return inType == StateNodeTypes::State || inType == StateNodeTypes::Final
+ || inType == StateNodeTypes::Parallel;
+ }
+
+ void RecurseGetChildrenForHistoryTarget(SState &inNode,
+ eastl::vector<CRegisteredString> &retval)
+ {
+ for (TStateNodeList::iterator iter = inNode.m_Children.begin(),
+ end = inNode.m_Children.end();
+ iter != end; ++iter) {
+ if (IsLegalHistoryTarget(iter->m_Type)) {
+ retval.push_back(iter->m_Id);
+ if (iter->m_Type == StateNodeTypes::State)
+ RecurseGetChildrenForHistoryTarget(static_cast<SState &>(*iter), retval);
+ }
+ }
+ }
+
+ eastl::vector<CRegisteredString> SEditorImpl::GetLegalHistoryDefaultValues(SHistory &inData)
+ {
+ eastl::vector<CRegisteredString> retval;
+ if (inData.m_Parent && inData.m_Parent->m_Type == StateNodeTypes::State) {
+ SState &theState = static_cast<SState &>(*inData.m_Parent);
+ for (TStateNodeList::iterator iter = theState.m_Children.begin(),
+ end = theState.m_Children.end();
+ iter != end; ++iter) {
+ if (IsLegalHistoryTarget(iter->m_Type)) {
+ retval.push_back(iter->m_Id);
+ if (inData.m_Flags.IsDeep()) {
+ if (iter->m_Type == StateNodeTypes::State)
+ RecurseGetChildrenForHistoryTarget(static_cast<SState &>(*iter),
+ retval);
+ }
+ }
+ }
+ }
+ return retval;
+ }
+
+ void RecurseGetLegalParents(eastl::vector<CRegisteredString> &outResult,
+ SStateNode &inTarget, SStateNode &inCurrent)
+ {
+ // Basic checks
+ if (inCurrent.m_Type == StateNodeTypes::History
+ || inCurrent.m_Type == StateNodeTypes::Final || &inCurrent == &inTarget)
+ return;
+
+ // Parallel's cannot have finals, but their children could
+ bool isIllegalChild = (inTarget.m_Type == StateNodeTypes::Final
+ && inCurrent.m_Type == StateNodeTypes::Parallel)
+ || inCurrent.m_Type == StateNodeTypes::SCXML;
+
+ if (isIllegalChild == false) {
+ outResult.push_back(inCurrent.m_Id);
+ }
+ TStateNodeList *children = inCurrent.GetChildren();
+ if (children) {
+ for (TStateNodeList::iterator iter = children->begin(), end = children->end();
+ iter != end; ++iter) {
+ RecurseGetLegalParents(outResult, inTarget, *iter);
+ }
+ }
+ }
+
+ static bool lexi(CRegisteredString lhs, CRegisteredString rhs)
+ {
+ return strcmp(lhs.c_str(), rhs.c_str()) < 0;
+ }
+
+ eastl::vector<CRegisteredString> SEditorImpl::GetLegalParentIds(SStateNode &inNode)
+ {
+ // Do a depth first search and just do not include this node or any history or final
+ // nodes.
+ eastl::vector<CRegisteredString> retval;
+ RecurseGetLegalParents(retval, inNode, *m_StateContext->GetRoot());
+ eastl::sort(retval.begin(), retval.end(), lexi);
+ return retval;
+ }
+
+ struct SParentChange : public IChange
+ {
+ TObjPtr m_ChildObj;
+ TStateNodeList *m_OldList;
+ TStateNodeList &m_NewList;
+ SStateNode *m_OldParent;
+ SStateNode &m_NewParent;
+ SStateNode &m_Child;
+ QT3DSI32 m_RefCount;
+ SParentChange(TObjPtr childObj, TStateNodeList *oldL, TStateNodeList &newL,
+ SStateNode *oldp, SStateNode &newp, SStateNode &c)
+ : m_ChildObj(childObj)
+ , m_OldList(oldL)
+ , m_NewList(newL)
+ , m_OldParent(oldp)
+ , m_NewParent(newp)
+ , m_Child(c)
+ , m_RefCount(0)
+ {
+ }
+
+ void addRef() { ++m_RefCount; }
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+
+ virtual void Do()
+ {
+ if (m_OldList)
+ m_OldList->remove(m_Child);
+ m_NewList.push_back(m_Child);
+ m_Child.m_Parent = &m_NewParent;
+ }
+ virtual void Undo()
+ {
+ m_NewList.remove(m_Child);
+ if (m_OldList)
+ m_OldList->push_back(m_Child);
+ m_Child.m_Parent = m_OldParent;
+ }
+
+ virtual TObjPtr GetEditor() { return m_ChildObj; }
+ };
+
+ void SEditorImpl::SetParent(SStateNode &inNode, const Option<SValue> &inValue)
+ {
+ STransaction *theTransaction = m_TransactionManager->GetOpenTransactionImpl();
+ SStateNode *oldParent = inNode.m_Parent;
+ TEditorStr newParentId;
+ if (inValue.hasValue())
+ newParentId = inValue->getData<TEditorStr>();
+
+ SStateNode *newParent =
+ m_StateContext->FindStateNode(m_StringTable->RegisterStr(newParentId.c_str()));
+ if (newParent == NULL)
+ newParent = m_StateContext->GetRoot();
+ TStateNodeList *newList = newParent->GetChildren();
+ if (newList == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ SParentChange *theChange =
+ new SParentChange(ToEditor(inNode), oldParent ? oldParent->GetChildren() : NULL,
+ *newList, oldParent, *newParent, inNode);
+ theChange->Do();
+ if (theTransaction) {
+ theTransaction->m_Changes.push_back(theChange);
+ if (oldParent)
+ theTransaction->m_Changes.push_back(new SChange(*ToEditor(*oldParent)));
+ theTransaction->m_Changes.push_back(new SChange(*ToEditor(*newParent)));
+ }
+ CheckAndSetValidHistoryDefault(ToEditor(inNode));
+ // Fix the initial state of the old parent if it exists
+ if (oldParent) {
+ if (isTrivial(oldParent->GetInitialExpression())
+ && oldParent->GetInitialTransition()) {
+ STransition *theTransition = oldParent->GetInitialTransition();
+ if (theTransition != NULL && theTransition->m_Target.size()) {
+ bool foundItem = false;
+ for (QT3DSU32 idx = 0, end = theTransition->m_Target.size();
+ idx < end && foundItem == false; ++idx) {
+ if (theTransition->m_Target[idx] == &inNode)
+ foundItem = true;
+ }
+ if (foundItem) {
+ SetInitialAttribute(SValue(TEditorStr()), *oldParent);
+ }
+ }
+ }
+ // fix all history nodes
+ TStateNodeList *parentChildren = oldParent->GetChildren();
+ if (parentChildren) {
+ for (TStateNodeList::iterator iter = parentChildren->begin(),
+ end = parentChildren->end();
+ iter != end; ++iter) {
+ if (iter->m_Type == StateNodeTypes::History) {
+ CheckAndSetValidHistoryDefault(ToEditor(*iter));
+ }
+ }
+ }
+ }
+ // Set the new object inside the new parent.
+ ToEditor(inNode)->SetPropertyValue("position", QT3DSVec2(0, 0));
+ }
+
+ void SEditorImpl::CheckAndSetValidHistoryDefault(TObjPtr inHistoryNode)
+ {
+ if (inHistoryNode && AreEqual(inHistoryNode->TypeName(), "history")) {
+ eastl::vector<CRegisteredString> validChildren =
+ inHistoryNode->GetLegalValues("default");
+ TObjList existing = inHistoryNode->GetPropertyValue("default")->getData<TObjList>();
+ if (existing.size()) {
+ // Ensure all of the existing nodes are in the valid children list.
+ bool allValid = true;
+ for (size_t existingIdx = 0, existingEnd = existing.size();
+ existingIdx < existingEnd && allValid; ++existingIdx) {
+ CRegisteredString idStr =
+ m_StringTable->RegisterStr(existing[existingIdx]->GetId().c_str());
+ if (eastl::find(validChildren.begin(), validChildren.end(), idStr)
+ == validChildren.end())
+ allValid = false;
+ }
+ if (allValid)
+ return;
+ }
+ SetValidHistoryDefault(inHistoryNode);
+ }
+ }
+
+ void SEditorImpl::SetValidHistoryDefault(TObjPtr inHistoryNode)
+ {
+ if (inHistoryNode && AreEqual(inHistoryNode->TypeName(), "history")) {
+ eastl::vector<CRegisteredString> legalValues =
+ inHistoryNode->GetLegalValues("default");
+ if (legalValues.size()) {
+ TObjPtr firstLegal = GetObjectById(legalValues[0]);
+ TObjList propValue;
+ propValue.push_back(firstLegal);
+ inHistoryNode->SetPropertyValue("default", propValue);
+ }
+ }
+ }
+
+ MallocAllocator SBaseEditorFoundation::g_BaseAlloc;
+
+ bool SPropertyDecNameFinder::operator()(const SPropertyDeclaration &dec) const
+ {
+ return AreEqual(m_NameStr, dec.m_Name.c_str());
+ }
+
+ void IEditorObject::Append(const char8_t *inPropName, TObjPtr inObj)
+ {
+ Option<SValue> theProp = GetPropertyValue(inPropName);
+ if (theProp.hasValue() && theProp->getType() == ValueTypes::ObjPtrList
+ && inObj != NULL) {
+ TObjList theData = theProp->getData<TObjList>();
+ theData.push_back(inObj);
+ SetPropertyValue(inPropName, theData);
+ }
+ }
+
+ void IEditorObject::Remove(const char8_t *inPropName, TObjPtr inObj)
+ {
+ Option<SValue> theProp = GetPropertyValue(inPropName);
+ if (theProp.hasValue() && theProp->getType() == ValueTypes::ObjPtrList
+ && inObj != NULL) {
+ TObjList theData = theProp->getData<TObjList>();
+ TObjList::iterator theFind = eastl::find(theData.begin(), theData.end(), inObj);
+ if (theFind != theData.end())
+ theData.erase(theFind);
+ SetPropertyValue(inPropName, theData);
+ }
+ }
+
+ IEditor &IEditor::CreateEditor()
+ {
+ TFoundationPtr theFoundation = SBaseEditorFoundation::Create();
+ return *QT3DS_NEW(theFoundation->getAllocator(), SEditorImpl)(
+ theFoundation, IStringTable::CreateStringTable(theFoundation->getAllocator()));
+ }
+
+ IEditor *IEditor::CreateEditor(const char8_t *inFname)
+ {
+ CFileSeekableIOStream theFile(inFname, FileReadFlags());
+ if (theFile.IsOpen() == false)
+ return NULL;
+
+ return CreateEditor(inFname, theFile);
+ }
+
+ IEditor *IEditor::CreateEditor(const char8_t *inFname, IInStream &inStream)
+ {
+ IEditor &theEditor(CreateEditor());
+ if (static_cast<SEditorImpl &>(theEditor).Load(inStream, inFname))
+ return &theEditor;
+
+ theEditor.release();
+ return NULL;
+ }
+ }
+}
+}
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.h
new file mode 100644
index 00000000..31c83afe
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditor.h
@@ -0,0 +1,315 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_H
+#define QT3DS_STATE_EDITOR_H
+#pragma once
+#include "Qt3DSState.h"
+#include "Qt3DSStateSignalConnection.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/TaggedPointer.h"
+#include "EASTL/vector.h"
+#include "EASTL/string.h"
+#include "foundation/Qt3DSVec2.h"
+#include "foundation/Qt3DSVec3.h"
+#include "Qt3DSStateInterpreter.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ class IEditorObject;
+ typedef NVScopedRefCounted<IEditorObject> TObjPtr;
+ typedef eastl::vector<TObjPtr> TObjList;
+
+ class IEditorChangeListener
+ {
+ protected:
+ virtual ~IEditorChangeListener() {}
+ public:
+ // The same object may be represented multiple times in this list.
+ virtual void OnDataChange(const TObjList &inChangedObjects,
+ const TObjList &inRemovedObjects) = 0;
+ };
+
+ struct EditorPropertyTypes
+ {
+ enum Enum {
+ NoProperty = 0,
+ Event, // string
+ Id, // string
+ Object, // IEditorObject
+ ObjectList, // IEditorObject list
+ // A string, but one from a set of strings.
+ StringSet, // string
+ String, // string, single line
+ BigString, // string than may be multiple lines long
+ Position, // vec2
+ Dimension, // vec2
+ PositionList, // vector<QT3DSVec2>
+ Script, // string
+ Color,
+ Boolean,
+ U32,
+ };
+ };
+
+ struct SPropertyDeclaration
+ {
+ CRegisteredString m_Name;
+ EditorPropertyTypes::Enum m_Type;
+ // for enumerations, the list of enumerations
+ SPropertyDeclaration() {}
+ SPropertyDeclaration(CRegisteredString inName, EditorPropertyTypes::Enum inType)
+ : m_Name(inName)
+ , m_Type(inType)
+ {
+ }
+ };
+
+ typedef eastl::vector<SPropertyDeclaration> TPropertyDeclarationList;
+
+ struct SPropertyDecNameFinder
+ {
+ const char8_t *m_NameStr;
+ SPropertyDecNameFinder(const char8_t *nmStr)
+ : m_NameStr(nmStr)
+ {
+ }
+ // Implemented in UICStateEditor.cpp to include "foundation/Utils.h"
+ bool operator()(const SPropertyDeclaration &dec) const;
+ };
+
+ typedef eastl::vector<QT3DSVec2> TVec2List;
+ typedef eastl::string TEditorStr;
+ typedef eastl::vector<TEditorStr> TEditorStrList;
+
+ // Our generic discriminated union type for all property values.
+ struct SValue;
+ struct SValueOpt;
+
+ class IEditor;
+
+ class IEditorObject : public NVRefCounted
+ {
+ protected:
+ virtual ~IEditorObject() {}
+ const char8_t *m_TypeName;
+
+ public:
+ IEditorObject(const char8_t *tn)
+ : m_TypeName(tn)
+ {
+ }
+ const char8_t *TypeName() const { return m_TypeName; }
+ // implemented in UICStateEditor.cpp using getpropertyvalue.
+ virtual TEditorStr GetId() = 0;
+ virtual TEditorStr GetDescription() = 0;
+ virtual void SetId(const TEditorStr &inId) = 0;
+ virtual void SetDescription(const TEditorStr &inName) = 0;
+ // Things only have one canonical parent.
+ virtual TObjPtr Parent() = 0;
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties) = 0;
+ virtual Option<SPropertyDeclaration> FindProperty(const char8_t *propName) = 0;
+ // The legal values for a property tend to be something that have to be calculated
+ // dynamically.
+ virtual eastl::vector<CRegisteredString> GetLegalValues(const char8_t *propName) = 0;
+
+ virtual Option<SValue> GetPropertyValue(const char8_t *inPropName) = 0;
+ virtual void SetPropertyValue(const char8_t *inPropName, const SValueOpt &inValue) = 0;
+ virtual bool endEdit() { return false; } // should SetPropertyValue end property edit
+
+ // Utility function implemented in UICStateEditor.cpp
+ virtual void Append(const char8_t *inPropName, TObjPtr inObj);
+ virtual void Remove(const char8_t *inPropName, TObjPtr inObj);
+
+ // Remove this from any parents. Remove any transitions pointing to this.
+ // Only works for state-node derived types.
+ // Executable content, onEntry onExit and such will not work with this.
+ // Also removes this item id from the id map. This is what is used to delete objects.
+ // Do not reattach yourself; callers should release all references to this object after
+ // calling
+ // this.
+ virtual void RemoveObjectFromGraph() = 0;
+
+ // Internal calls, don't call externally
+ virtual void RemoveIdFromContext() = 0;
+ virtual void AddIdToContext() = 0;
+
+ // Most things do not have any executable content.
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ return NVConstDataRef<InterpreterEventTypes::Enum>();
+ }
+
+ // Be aware that there are more types of executable content than indicated in the store;
+ // a lot more.
+ // So just ignore the types the story ignores.
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum /*inType*/)
+ {
+ return TObjList();
+ }
+
+ // inName can be an executable content:
+ //'script', 'send', 'cancel'
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum /*inType*/,
+ const char8_t * /*inName*/)
+ {
+ return TObjPtr();
+ }
+
+ virtual IEditor &GetEditor() = 0;
+ };
+
+ class ITransaction : public NVRefCounted
+ {
+ protected:
+ virtual ~ITransaction() {}
+ TEditorStr m_Name;
+
+ public:
+ ITransaction(const TEditorStr inName = TEditorStr())
+ : m_Name(inName)
+ {
+ }
+ TEditorStr GetName() const { return m_Name; }
+ // Send the signals collected during the creation of this transaction
+ virtual void SendDoSignals() = 0;
+ virtual TObjList GetEditedObjects() = 0;
+ virtual void Do() = 0;
+ virtual void Undo() = 0;
+ virtual bool Empty() = 0;
+ };
+
+ typedef NVScopedRefCounted<ITransaction> TTransactionPtr;
+
+ class ITransactionManager : public NVRefCounted
+ {
+ protected:
+ virtual ~ITransactionManager() {}
+ public:
+ virtual TSignalConnectionPtr AddChangeListener(IEditorChangeListener &inListener) = 0;
+ // Undo/redo is supported via a transparent transaction system.
+ // calls are reentrant but last call will close the transaction object.
+ // Any changes to any editor objects will go through this transaction when they happen.
+ virtual TTransactionPtr BeginTransaction(const TEditorStr &inName = TEditorStr()) = 0;
+ virtual TTransactionPtr GetOpenTransaction() = 0;
+ virtual void RollbackTransaction() = 0;
+ virtual void EndTransaction() = 0;
+ };
+
+ // Editor interface to a UICState dataset
+ class IEditor : public ITransactionManager
+ {
+ protected:
+ virtual ~IEditor() {}
+ public:
+ // Traditional editor interface.
+ virtual TObjPtr GetRoot() = 0;
+ // You can force the id to be a particular ID in some cases, this is useful for testing.
+ // ID is ignored for objects that don't have an id.
+ virtual TObjPtr Create(const char8_t *inTypeName, const char8_t *inId = 0) = 0;
+ virtual TObjPtr GetOrCreate(SSCXML &inData) = 0;
+ virtual TObjPtr GetOrCreate(SState &inData) = 0;
+ virtual TObjPtr GetOrCreate(STransition &inData) = 0;
+ virtual TObjPtr GetOrCreate(SParallel &inData) = 0;
+ virtual TObjPtr GetOrCreate(SHistory &inData) = 0;
+ virtual TObjPtr GetOrCreate(SFinal &inData) = 0;
+ virtual TObjPtr GetOrCreate(SDataModel &inData) = 0;
+ virtual TObjPtr GetOrCreate(SData &inData) = 0;
+ virtual TObjPtr ToEditor(SStateNode &inItem) = 0;
+
+ // Get a particular object by id. Useful in testing scenarios.
+ virtual TObjPtr GetObjectById(const char8_t *inId) = 0;
+
+ // Get an editor, if it already has been created, for this piece of graph data.
+ // Note that if it has not been created, this function couldn't possibly create it
+ // due to lack of type information.
+ virtual TObjPtr GetEditor(void *inGraphData) = 0;
+
+ // Copy save a subgraph to a string. Converts all top level positions to be relative to
+ // mouse pos.
+ virtual TEditorStr Copy(TObjList inObjects, const QT3DSVec2 &inMousePos) = 0;
+ virtual bool CanPaste(TObjPtr inTarget) = 0;
+ // Paste in, adding relative pos to all top level positions.
+ virtual void Paste(const TEditorStr &inCopiedObjects, TObjPtr inTarget,
+ const QT3DSVec2 &inRelativePos) = 0;
+
+ virtual TObjPtr GetLeastCommonAncestor(NVConstDataRef<TObjPtr> inObjects) = 0;
+
+ // Returns the list of send ids that have a delay on them.
+ virtual TObjList GetCancelableSendIds() = 0;
+
+ // Return the set of events in use in the state machine.
+ virtual TEditorStrList GetStateMachineEvents() = 0;
+
+ // Change content from one type to another.
+ virtual TObjPtr ChangeExecutableContentType(TObjPtr inContent,
+ const char8_t *newType) = 0;
+
+ virtual TEditorStr ToXML(TObjPtr inContent) = 0;
+ // Returns the error from parsing the xml or if everything went correctly replaces
+ // inContent
+ // with the result of parsing the xml and returns the new content.
+ virtual eastl::pair<TEditorStr, TObjPtr> FromXML(TObjPtr inContent,
+ const TEditorStr &ioEditedXML) = 0;
+
+ virtual bool Save(const char8_t *inFname) = 0;
+ virtual void Save(IOutStream &inStream) = 0;
+
+ // This must be called on history creation once it has been added to the proper parent.
+ virtual void SetValidHistoryDefault(TObjPtr inHistoryNode) = 0;
+
+ static IEditor &CreateEditor();
+
+ static IEditor *CreateEditor(const char8_t *inFname);
+ static IEditor *CreateEditor(const char8_t *inFname, IInStream &inStream);
+ };
+
+ typedef NVScopedRefCounted<IEditor> TEditorPtr;
+
+ struct SEditorImplTransactionScope
+ {
+ ITransactionManager &m_Editor;
+ TTransactionPtr m_Transaction;
+ SEditorImplTransactionScope(ITransactionManager &inEditor)
+ : m_Editor(inEditor)
+ , m_Transaction(inEditor.BeginTransaction())
+ {
+ }
+ ~SEditorImplTransactionScope() { m_Editor.EndTransaction(); }
+
+ TTransactionPtr operator->() { return m_Transaction; }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorEditorsImpl.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorEditorsImpl.h
new file mode 100644
index 00000000..fc9411d2
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorEditorsImpl.h
@@ -0,0 +1,1772 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_EDITORS_IMPL_H
+#define QT3DS_STATE_EDITOR_EDITORS_IMPL_H
+#include "Qt3DSState.h"
+#include "Qt3DSStateEditor.h"
+#include "Qt3DSStateEditorValue.h"
+#include "Qt3DSStateEditorTransactionImpl.h"
+#include "Qt3DSStateTypes.h"
+#include "Qt3DSStateEditorFoundation.h"
+#include "Qt3DSStateEditorProperties.h"
+#include "foundation/Utils.h"
+#include "foundation/XML.h"
+#include "Qt3DSStateXMLIO.h"
+#include "foundation/IOStreams.h"
+#include "Qt3DSStateExecutionTypes.h"
+#include "Qt3DSStateContext.h"
+#include "Qt3DSStateEditorImpl.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct EditorTypes
+ {
+ enum Enum {
+ UnknownEditor = 0,
+ SCXML,
+ State,
+ Parallel,
+ Transition,
+ History,
+ Final,
+ ExecutableContent,
+ OnEntry,
+ OnExit,
+ Send,
+ Raise,
+ If,
+ Else,
+ ElseIf,
+ Log,
+ Assign,
+ Script,
+ DataModel,
+ Data,
+ Cancel,
+ };
+ };
+
+ struct SEditorImplObject : public IEditorObject
+ {
+ TFoundationPtr m_Foundation;
+ SEditorImpl &m_Editor;
+ volatile QT3DSI32 mRefCount;
+ eastl::string m_Id;
+ eastl::string m_Description;
+ Option<QT3DSVec3> m_Color;
+ TPropertyAccessorList m_PropertyAccessors;
+
+ static void CreateAccessors(SEditorImpl &inData, TPropertyAccessorList &outAccessors,
+ bool includeId, bool includeColor)
+ {
+ if (includeId)
+ outAccessors.push_back(CreateEditorAccessor(inData, &SEditorImplObject::m_Id,
+ "id", EditorPropertyTypes::String));
+
+ outAccessors.push_back(
+ CreateEditorAccessor(inData, &SEditorImplObject::m_Description, "description",
+ EditorPropertyTypes::String));
+ if (includeColor)
+ outAccessors.push_back(CreateEditorAccessor(
+ inData, &SEditorImplObject::m_Color, "color", EditorPropertyTypes::Color));
+ }
+
+ SEditorImplObject(const char8_t *tn, SEditorImpl &inData,
+ const TPropertyAccessorList &inAccessors)
+ : IEditorObject(tn)
+ , m_Foundation(inData.m_EditorFoundation)
+ , m_Editor(inData)
+ , mRefCount(0)
+ , m_PropertyAccessors(inAccessors)
+ {
+ }
+
+ IPropertyAccessor *FindPropertyAccessor(const char8_t *inName)
+ {
+ CRegisteredString nameStr = m_Editor.m_StringTable->RegisterStr(inName);
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx)
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr)
+ return m_PropertyAccessors[idx].mPtr;
+ return NULL;
+ }
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties)
+ {
+ outProperties.clear();
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ }
+
+ virtual Option<SPropertyDeclaration> FindProperty(const char8_t *propName)
+ {
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx) {
+ if (AreEqual(m_PropertyAccessors[idx]->m_Declaration.m_Name.c_str(), propName))
+ return m_PropertyAccessors[idx]->m_Declaration;
+ }
+ return Empty();
+ }
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(const char8_t *propName)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(propName);
+ if (accessor)
+ return accessor->GetLegalValues(*this);
+ return eastl::vector<CRegisteredString>();
+ }
+
+ virtual Option<SValue> GetPropertyValue(const char8_t *inPropName)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(inPropName);
+ if (accessor) {
+ return accessor->Get(*this);
+ }
+ return Empty();
+ }
+ virtual void SetPropertyValue(const char8_t *inPropName, const SValueOpt &inValue)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(inPropName);
+ if (accessor) {
+ if (accessor->HandlesTransaction() == false) {
+ STransaction *theTransaction = m_Editor.GetOpenTransactionImpl();
+ if (theTransaction) {
+ Option<SValue> existing = accessor->Get(*this);
+ theTransaction->m_Changes.push_back(
+ new SChange(existing, inValue, *accessor, *this));
+ }
+ }
+ accessor->Set(*this, inValue);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ virtual TEditorStr GetId()
+ {
+ Option<SValue> propValue = GetPropertyValue("id");
+ if (propValue.hasValue())
+ return propValue->getData<TEditorStr>();
+ return TEditorStr();
+ }
+ virtual TEditorStr GetDescription() { return m_Description; }
+ // We have to go through the SPV interface to take transactions into account.
+
+ virtual void SetId(const TEditorStr &inName) { SetPropertyValue("id", SValue(inName)); }
+ virtual void SetDescription(const TEditorStr &inName)
+ {
+ SetPropertyValue("description", SValue(inName));
+ }
+
+ virtual TObjPtr Parent() { return TObjPtr(); }
+ virtual void RemoveObjectFromGraph() { QT3DS_ASSERT(false); }
+
+ virtual IEditor &GetEditor() { return m_Editor; }
+ };
+
+#define QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE \
+ void addRef() { atomicIncrement(&mRefCount); } \
+ void release() \
+ { \
+ TFoundationPtr fnd(m_Foundation); \
+ /*Ensure the editor sticks around till *after* we are gone. */ \
+ /*This is because our objects will keep several bits of the editor around */ \
+ QT3DS_IMPLEMENT_REF_COUNT_RELEASE(m_Foundation->getAllocator()); \
+ }
+
+ struct SSCXMLEditor : public SEditorImplObject
+ {
+ typedef SSCXML TStateType;
+ enum { EditorType = EditorTypes::SCXML };
+
+ SSCXML &m_Data;
+ eastl::string m_InitialComboValue;
+ bool m_endEdit;
+
+ static const char8_t *GetTypeStr() { return "scxml"; }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ typedef SDataProp<SSCXMLEditor, SSCXML, STransition *> TInitialProp;
+ typedef SFlagBooleanProperty<SSCXMLEditor, SSCXMLFlags> TBindingProp;
+ TPropertyAccessorList &retval(inList);
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ typedef SDataProp<SSCXMLEditor, SSCXML, CRegisteredString> TNameProp;
+ typedef SInitialComboProp<SSCXMLEditor, SSCXML> TInitialComboProp;
+ typedef SDataProp<SSCXMLEditor, SSCXML, const char8_t *> TCharProp;
+ IPropertyAccessor *newAccessor =
+ QT3DS_NEW(alloc, TNameProp)(inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("name"),
+ EditorPropertyTypes::String),
+ &SSCXML::m_Name);
+ retval.push_back(newAccessor);
+ retval.push_back(CreateEditorAccessor(inEditorData,
+ &SEditorImplObject::m_Description,
+ "description", EditorPropertyTypes::String));
+ newAccessor = QT3DS_NEW(alloc, TBindingProp)(
+ inEditorData.m_EditorFoundation, *inEditorData.m_StringTable, "binding",
+ "early", "late", &SSCXMLFlags::IsLateBinding, &SSCXMLFlags::SetLateBinding);
+ retval.push_back(newAccessor);
+
+ TInitialComboProp *theInitialCombo = QT3DS_NEW(alloc, TInitialComboProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("initial"),
+ EditorPropertyTypes::StringSet));
+ retval.push_back(theInitialCombo);
+
+ TCharProp *theInitialExprProp = QT3DS_NEW(alloc, TCharProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("initialexpr"),
+ EditorPropertyTypes::BigString),
+ &SSCXML::m_InitialExpr);
+ retval.push_back(theInitialExprProp);
+
+ retval.push_back(CreateDataAccessor<SSCXMLEditor>(
+ inEditorData, &SSCXML::m_DataModel, "datamodel", EditorPropertyTypes::Object));
+ newAccessor = QT3DS_NEW(alloc, SChildrenProperty<SSCXMLEditor>)(
+ inEditorData.m_EditorFoundation, *inEditorData.m_StringTable);
+ retval.push_back(newAccessor);
+ // Replace the name property with one that writes to our data
+ return retval;
+ }
+
+ SSCXMLEditor(SSCXML &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SEditorImplObject(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ , m_endEdit(false)
+ {
+ }
+
+ void ApplyInitial(const TEditorStr &comboValue)
+ {
+ m_endEdit = false;
+
+ if (m_InitialComboValue == "(script expression)"
+ || comboValue == "(script expression)")
+ m_endEdit = true;
+
+ m_InitialComboValue = comboValue;
+ }
+
+ virtual bool endEdit()
+ {
+ bool bTemp = m_endEdit;
+ m_endEdit = false;
+ return bTemp;
+ }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual void RemoveObjectFromGraph() { QT3DS_ASSERT(false); }
+
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties)
+ {
+ outProperties.clear();
+ eastl::vector<CRegisteredString> temp;
+ m_Editor.GetLegalInitialValues(m_Data, temp);
+ bool hasInitialState = temp.size() > 1;
+ bool hasInitialExpr = false;
+ CRegisteredString nameStr = m_Editor.RegisterStr("initial");
+ CRegisteredString initialExprStr = m_Editor.RegisterStr("initialexpr");
+
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx) {
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr) {
+ if (hasInitialState)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ } else if (m_PropertyAccessors[idx]->m_Declaration.m_Name == initialExprStr) {
+ if (hasInitialExpr)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ } else {
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ }
+
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr) {
+ TEditorStr initialValue =
+ m_PropertyAccessors[idx]->Get(*this)->getData<TEditorStr>();
+ if (initialValue == "(script expression)")
+ hasInitialExpr = true;
+ }
+ }
+ }
+ };
+
+ struct SPositionalEditor : public SEditorImplObject
+ {
+ SPositionalEditor(const char8_t *inTypeName, SEditorImpl &inData,
+ const TPropertyAccessorList &inAccessors)
+ : SEditorImplObject(inTypeName, inData, inAccessors)
+ {
+ }
+ };
+
+ template <typename TListType, typename TListItem>
+ struct TListInsertDeleteChange : public IChange
+ {
+ TObjPtr m_EditorObject;
+ QT3DSI32 mRefCount;
+ TListType *m_ParentList;
+ TListItem *m_TargetContent;
+ QT3DSI32 m_ContentIdx;
+ bool m_AddOnDo;
+ // The item must be in the list for this to work.
+ TListInsertDeleteChange(TObjPtr inEditor, TListType *plist, TListItem *tc, bool addOnDo)
+ : m_EditorObject(inEditor)
+ , mRefCount(0)
+ , m_ParentList(plist)
+ , m_TargetContent(tc)
+ , m_ContentIdx(-1)
+ , m_AddOnDo(addOnDo)
+ {
+ for (typename TListType::iterator iter = m_ParentList->begin(),
+ end = m_ParentList->end();
+ iter != end; ++iter) {
+ if (&(*iter) == m_TargetContent)
+ break;
+ // setup content idx to point to the item just our target item.
+ ++m_ContentIdx;
+ }
+ }
+ virtual void addRef() { atomicIncrement(&mRefCount); }
+ virtual void release()
+ {
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ delete this;
+ }
+ }
+ void add()
+ {
+ if (m_ContentIdx > -1) {
+ QT3DSI32 idx = m_ContentIdx - 1;
+ typename TListType::iterator iter = m_ParentList->begin();
+
+ for (typename TListType::iterator end = m_ParentList->end();
+ iter != end && idx > -1; ++iter, --idx) {
+ };
+
+ if (iter != m_ParentList->end())
+ m_ParentList->insert_after(*iter, *m_TargetContent);
+ else
+ m_ParentList->push_back(*m_TargetContent);
+ } else
+ m_ParentList->push_front(*m_TargetContent);
+ }
+ void remove() { m_ParentList->remove(*m_TargetContent); }
+
+ virtual void Do()
+ {
+ if (m_AddOnDo)
+ add();
+ else
+ remove();
+ }
+ virtual void Undo()
+ {
+ if (m_AddOnDo)
+ remove();
+ else
+ add();
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObject; }
+ };
+
+ struct SExecutableContentChange
+ : public TListInsertDeleteChange<TExecutableContentList, SExecutableContent>
+ {
+ typedef TListInsertDeleteChange<TExecutableContentList, SExecutableContent> TBase;
+ SExecutableContentChange(TObjPtr inEditor, TExecutableContentList *plist,
+ SExecutableContent *tc, bool addOnDo)
+ : TBase(inEditor, plist, tc, addOnDo)
+ {
+ }
+ };
+
+ struct SStateEditor : public SPositionalEditor
+ {
+ typedef SState TStateType;
+ enum { EditorType = EditorTypes::State };
+ SState &m_Data;
+ eastl::string m_InitialComboValue;
+ bool m_endEdit;
+ static const char8_t *GetTypeStr() { return "state"; }
+
+ template <typename TEditorType>
+ static void CreateStateNodeChildren(SEditorImpl &inEditorData,
+ TPropertyAccessorList &retval)
+ {
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ IPropertyAccessor *newAccessor = QT3DS_NEW(alloc, SChildrenProperty<TEditorType>)(
+ inEditorData.m_EditorFoundation, *inEditorData.m_StringTable);
+ retval.push_back(newAccessor);
+ }
+
+ template <typename TEditorType>
+ static void CreatePositionColorAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &retval)
+ {
+ typedef SOptionAccessorProp<TEditorType, SStateNode, QT3DSVec2> TVec2Access;
+ typedef SOptionAccessorProp<TEditorType, SStateNode, QT3DSVec3> TVec3Access;
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ retval.push_back(QT3DS_NEW(alloc, TVec2Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("position"),
+ EditorPropertyTypes::Position),
+ &SStateNode::GetPosition, &SStateNode::SetPosition));
+ retval.push_back(QT3DS_NEW(alloc, TVec2Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("dimension"),
+ EditorPropertyTypes::Position),
+ &SStateNode::GetDimension, &SStateNode::SetDimension));
+
+ retval.push_back(QT3DS_NEW(alloc, TVec3Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("color"),
+ EditorPropertyTypes::Color),
+ &SStateNode::GetColor, &SStateNode::SetColor));
+ }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ typedef SDataProp<SStateEditor, SState, STransition *> TInitialProp;
+ typedef SDataIdProp<SStateEditor, SState> TIdPropType;
+ typedef SInitialTargetProp<SStateEditor, SState> TInitialTargetProp;
+ typedef SInitialComboProp<SStateEditor, SState> TInitialComboProp;
+ typedef SDataProp<SStateEditor, SState, const char8_t *> TCharProp;
+
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(QT3DS_NEW(alloc, TIdPropType)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SState::m_Id));
+
+ typedef SParentProp<SStateEditor> TParentPropType;
+ retval.push_back(QT3DS_NEW(alloc, TParentPropType)(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable));
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+
+ TInitialComboProp *theInitialCombo = QT3DS_NEW(alloc, TInitialComboProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("initial"),
+ EditorPropertyTypes::StringSet));
+ retval.push_back(theInitialCombo);
+
+ TCharProp *theInitialExprProp = QT3DS_NEW(alloc, TCharProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("initialexpr"),
+ EditorPropertyTypes::BigString),
+ &SState::m_InitialExpr);
+ retval.push_back(theInitialExprProp);
+ retval.push_back(CreateDataAccessor<SStateEditor>(
+ inEditorData, &SState::m_DataModel, "datamodel", EditorPropertyTypes::Object));
+ CreateStateNodeChildren<SStateEditor>(inEditorData, retval);
+ CreatePositionColorAccessors<SStateEditor>(inEditorData, retval);
+ retval.push_back(QT3DS_NEW(alloc, TInitialTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("is initial target"),
+ EditorPropertyTypes::Boolean)));
+ // Replace the name property with one that writes to our data
+ return retval;
+ }
+ SStateEditor(SState &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SPositionalEditor(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ , m_endEdit(false)
+ {
+ }
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties)
+ {
+ outProperties.clear();
+ eastl::vector<CRegisteredString> temp;
+ m_Editor.GetLegalInitialValues(m_Data, temp);
+ bool hasInitialState = temp.size() > 1;
+ bool hasInitialExpr = false;
+ CRegisteredString nameStr = m_Editor.RegisterStr("initial");
+ CRegisteredString initialExprStr = m_Editor.RegisterStr("initialexpr");
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx) {
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr) {
+ if (hasInitialState)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ } else if (m_PropertyAccessors[idx]->m_Declaration.m_Name == initialExprStr) {
+ if (hasInitialExpr)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ } else
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+
+ if (hasInitialState
+ && m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr) {
+ TEditorStr initialValue =
+ m_PropertyAccessors[idx]->Get(*this)->getData<TEditorStr>();
+ if (initialValue == "(script expression)")
+ hasInitialExpr = true;
+ }
+ }
+ }
+
+ void ApplyInitial(const TEditorStr &comboValue)
+ {
+ m_endEdit = false;
+
+ if (m_InitialComboValue == "(script expression)"
+ || comboValue == "(script expression)")
+ m_endEdit = true;
+
+ m_InitialComboValue = comboValue;
+ }
+
+ virtual bool endEdit()
+ {
+ bool bTemp = m_endEdit;
+ m_endEdit = false;
+ return bTemp;
+ }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ struct SObjListContains
+ {
+ const TObjList &m_Targets;
+ SObjListContains(const TObjList &t)
+ : m_Targets(t)
+ {
+ }
+ bool operator()(const TObjPtr &inObj) const
+ {
+ return eastl::find(m_Targets.begin(), m_Targets.end(), inObj.mPtr)
+ != m_Targets.end();
+ }
+ };
+
+ static void CheckAndRemoveStateFromTransitionProperty(TObjList &removedItems,
+ IEditorObject &inEditorObj,
+ const char8_t *propName)
+ {
+ Option<SValue> propVal = inEditorObj.GetPropertyValue(propName);
+ if (propVal.hasValue()) {
+ if (propVal->getType() == ValueTypes::ObjPtr) {
+ TObjPtr transProp = propVal->getData<TObjPtr>();
+ if (transProp) {
+ TObjList propValValues =
+ transProp->GetPropertyValue("target")->getData<TObjList>();
+ TObjList::iterator lastIter =
+ eastl::remove_if(propValValues.begin(), propValValues.end(),
+ SObjListContains(removedItems));
+ if (lastIter != propValValues.end()) {
+ propValValues.erase(lastIter, propValValues.end());
+ if (propValValues.empty())
+ inEditorObj.SetPropertyValue(propName, TObjPtr());
+ else
+ transProp->SetPropertyValue("target", propValValues);
+ }
+ }
+ } else if (propVal->getType() == ValueTypes::String) {
+ TEditorStr theIdStr = propVal->getData<TEditorStr>();
+ for (size_t idx = 0, end = removedItems.size(); idx < end; ++idx) {
+ if (removedItems[idx]->GetId() == theIdStr) {
+ inEditorObj.SetPropertyValue(propName, TEditorStr());
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ static void RemoveTransitionsPointingTo(TObjList &removedItems,
+ IEditorObject &inEditorObj)
+ {
+ if (AreEqual(inEditorObj.TypeName(), "transition")) {
+ TObjList targets = inEditorObj.GetPropertyValue("target")->getData<TObjList>();
+ TObjList::iterator lastIter = eastl::remove_if(targets.begin(), targets.end(),
+ SObjListContains(removedItems));
+ if (lastIter != targets.end()) {
+ targets.erase(lastIter, targets.end());
+ inEditorObj.SetPropertyValue("target", targets);
+ if (targets.size() == 0)
+ inEditorObj.RemoveObjectFromGraph();
+ }
+ } else {
+ // state and parallel
+ CheckAndRemoveStateFromTransitionProperty(removedItems, inEditorObj, "initial");
+ // history
+ CheckAndRemoveStateFromTransitionProperty(removedItems, inEditorObj,
+ "transition");
+ }
+ Option<SValue> childListOpt = inEditorObj.GetPropertyValue("children");
+ if (childListOpt.hasValue()) {
+ TObjList childList = childListOpt->getData<TObjList>();
+ for (size_t idx = 0, end = childList.size(); idx < end; ++idx)
+ RemoveTransitionsPointingTo(removedItems, *childList[idx]);
+ }
+ }
+ static void RemoveObjectFromGraph(IEditorObject &inEditorObj,
+ TObjList &outRemovedObjects)
+ {
+ TObjPtr parentPtr = inEditorObj.Parent();
+ outRemovedObjects.push_back(&inEditorObj);
+ if (parentPtr)
+ parentPtr->Remove("children", &inEditorObj);
+
+ Option<SValue> nodeChildrenOpt = inEditorObj.GetPropertyValue("children");
+ if (nodeChildrenOpt.hasValue()) {
+ TObjList nodeChildren(nodeChildrenOpt->getData<TObjList>());
+ for (size_t idx = 0, end = nodeChildren.size(); idx < end; ++idx)
+ RemoveObjectFromGraph(*nodeChildren[idx], outRemovedObjects);
+ }
+ }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ // Simple and slow as hell.
+ static void RemoveObjectFromGraph(SEditorImplObject &inEditorObj)
+ {
+ TObjList removedItems;
+ TObjPtr oldParent = inEditorObj.Parent();
+ RemoveObjectFromGraph(inEditorObj, removedItems);
+ RemoveTransitionsPointingTo(removedItems, *inEditorObj.m_Editor.GetRoot());
+ for (size_t idx = 0, end = removedItems.size(); idx < end; ++idx) {
+ inEditorObj.m_Editor.m_TransactionManager->OnObjectDeleted(removedItems[idx]);
+ inEditorObj.RemoveIdFromContext();
+ }
+ if (oldParent) {
+ TObjList children =
+ oldParent->GetPropertyValue("children")->getData<TObjList>();
+ for (size_t childIdx = 0, childEnd = children.size(); childIdx < childEnd;
+ ++childIdx) {
+ inEditorObj.m_Editor.CheckAndSetValidHistoryDefault(children[childIdx]);
+ }
+ }
+ }
+
+ virtual void RemoveObjectFromGraph() { RemoveObjectFromGraph(*this); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ static InterpreterEventTypes::Enum retval[] = { InterpreterEventTypes::StateEnter,
+ InterpreterEventTypes::StateExit };
+ return toConstDataRef(retval, 2);
+ }
+
+ template <typename TListType>
+ static TObjList ListDataToEditor(TListType &inList, SEditorImplObject &ioObj)
+ {
+ TObjList retval;
+ typename TListType::iterator iter = inList.begin();
+ // Take the first one.
+ if (iter != inList.end()) {
+ for (TExecutableContentList::iterator
+ contentIter = iter->m_ExecutableContent.begin(),
+ contentEnd = iter->m_ExecutableContent.end();
+ contentIter != contentEnd; ++contentIter) {
+ TObjPtr editorData = ioObj.m_Editor.ExecutableContentToEditor(*contentIter);
+ retval.push_back(editorData);
+ }
+ }
+ return retval;
+ }
+
+ static TObjList ListDataToEditor(InterpreterEventTypes::Enum inType,
+ SEntryExitBase &inItem, SEditorImplObject &ioObj)
+ {
+ switch (inType) {
+ case InterpreterEventTypes::StateEnter:
+ return ListDataToEditor(inItem.m_OnEntry, ioObj);
+ case InterpreterEventTypes::StateExit:
+ return ListDataToEditor(inItem.m_OnExit, ioObj);
+ default:
+ break;
+ }
+ QT3DS_ASSERT(false);
+ return TObjList();
+ }
+
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum inType)
+ {
+ return SStateEditor::ListDataToEditor(inType, m_Data, *this);
+ }
+
+ template <typename TListItemType, typename TListType>
+ static TObjPtr DoCreateAppendExecutableContent(SEditorImplObject &inObj,
+ TListType &inList, const char8_t *inName,
+ SStateNode &inNode)
+ {
+ NVScopedRefCounted<IChange> theNewListItemChange;
+ if (inList.empty()) {
+ TListItemType *newType =
+ QT3DS_NEW(inObj.m_Editor.m_AutoAllocator, TListItemType)();
+ inList.push_back(*newType);
+ theNewListItemChange = new TListInsertDeleteChange<TListType, TListItemType>(
+ inObj, &inList, newType, true);
+ }
+ TListItemType &theFront = *inList.begin();
+ TObjPtr retval = inObj.m_Editor.CreateExecutableContent(inNode, inName);
+ SExecutableContent *theContent = inObj.m_Editor.ExecutableContentFromEditor(retval);
+ theFront.m_ExecutableContent.push_back(*theContent);
+ if (inObj.m_Editor.GetOpenTransactionImpl()) {
+ if (theNewListItemChange)
+ inObj.m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(
+ theNewListItemChange);
+
+ NVScopedRefCounted<SExecutableContentChange> newChange =
+ new SExecutableContentChange(inObj, &theFront.m_ExecutableContent,
+ theContent, true);
+ inObj.m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(newChange.mPtr);
+ }
+ return retval;
+ }
+
+ static TObjPtr DoCreateAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName,
+ SEditorImplObject &inObj,
+ SEntryExitBase &inItem)
+ {
+ switch (inType) {
+ case InterpreterEventTypes::StateEnter:
+ return DoCreateAppendExecutableContent<SOnEntry>(inObj, inItem.m_OnEntry,
+ inName, inItem);
+ case InterpreterEventTypes::StateExit:
+ return DoCreateAppendExecutableContent<SOnExit>(inObj, inItem.m_OnExit, inName,
+ inItem);
+ default:
+ break;
+ }
+
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName)
+ {
+ return SStateEditor::DoCreateAppendExecutableContent(inType, inName, *this, m_Data);
+ }
+ };
+
+ struct SParallelEditor : public SPositionalEditor
+ {
+ typedef SParallel TStateType;
+ enum { EditorType = EditorTypes::Parallel };
+ SParallel &m_Data;
+ static const char8_t *GetTypeStr() { return "parallel"; }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+
+ typedef SInitialTargetProp<SParallelEditor, SParallel> TInitialTargetProp;
+ TPropertyAccessorList &retval(inList);
+ typedef SDataIdProp<SParallelEditor, SParallel> TIdPropType;
+ typedef SParentProp<SParallelEditor> TParallelParentProp;
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ retval.push_back(QT3DS_NEW(alloc, TIdPropType)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SParallel::m_Id));
+
+ retval.push_back(QT3DS_NEW(alloc, TParallelParentProp)(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable));
+
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+ retval.push_back(CreateDataAccessor<SParallelEditor>(
+ inEditorData, &SState::m_DataModel, "datamodel", EditorPropertyTypes::Object));
+ SStateEditor::CreateStateNodeChildren<SParallelEditor>(inEditorData, retval);
+ SStateEditor::CreatePositionColorAccessors<SParallelEditor>(inEditorData, retval);
+ // Replace the name property with one that writes to our data
+ retval.push_back(QT3DS_NEW(alloc, TInitialTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("is initial target"),
+ EditorPropertyTypes::Boolean)));
+ return retval;
+ }
+
+ SParallelEditor(SParallel &inData, SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ : SPositionalEditor(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ virtual void RemoveObjectFromGraph() { SStateEditor::RemoveObjectFromGraph(*this); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ static InterpreterEventTypes::Enum retval[] = { InterpreterEventTypes::StateEnter,
+ InterpreterEventTypes::StateExit };
+ return toConstDataRef(retval, 2);
+ }
+
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum inType)
+ {
+ return SStateEditor::ListDataToEditor(inType, m_Data, *this);
+ }
+
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName)
+ {
+ return SStateEditor::DoCreateAppendExecutableContent(inType, inName, *this, m_Data);
+ }
+ };
+
+ struct SFinalEditor : public SPositionalEditor
+ {
+ typedef SFinal TStateType;
+ enum { EditorType = EditorTypes::Final };
+
+ SFinal &m_Data;
+ static const char8_t *GetTypeStr() { return "final"; }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ typedef SDataIdProp<SFinalEditor, SFinal> TIdPropType;
+ typedef SInitialTargetProp<SFinalEditor, SFinal> TInitialTargetProp;
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ typedef SParentProp<SFinalEditor> TFinalParentProp;
+ retval.push_back(QT3DS_NEW(alloc, TIdPropType)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SFinal::m_Id));
+ retval.push_back(QT3DS_NEW(alloc, TFinalParentProp)(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable));
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+ SStateEditor::CreatePositionColorAccessors<SFinalEditor>(inEditorData, retval);
+ retval.push_back(QT3DS_NEW(alloc, TInitialTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("is initial target"),
+ EditorPropertyTypes::Boolean)));
+ // Replace the name property with one that writes to our data
+ return retval;
+ }
+
+ SFinalEditor(SFinal &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SPositionalEditor(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ virtual void RemoveObjectFromGraph() { SStateEditor::RemoveObjectFromGraph(*this); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ static InterpreterEventTypes::Enum retval[] = { InterpreterEventTypes::StateEnter,
+ InterpreterEventTypes::StateExit };
+ return toConstDataRef(retval, 2);
+ }
+
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum inType)
+ {
+ return SStateEditor::ListDataToEditor(inType, m_Data, *this);
+ }
+
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName)
+ {
+ return SStateEditor::DoCreateAppendExecutableContent(inType, inName, *this, m_Data);
+ }
+ };
+
+ struct SHistoryEditor : public SPositionalEditor
+ {
+ typedef SHistory TStateType;
+ enum { EditorType = EditorTypes::History };
+ SHistory &m_Data;
+ static const char8_t *GetTypeStr() { return "history"; }
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ typedef SDataProp<SHistoryEditor, SHistory, STransition *> TTransitionProp;
+
+ typedef SFlagBooleanProperty<SHistoryEditor, SHistoryFlags> THistoryFlagsProp;
+ typedef SDataIdProp<SHistoryEditor, SHistory> TIdPropType;
+ typedef SHistoryTransitionProp<SHistoryEditor> THistoryTransitionProp;
+ typedef SParentProp<SHistoryEditor> THistoryParentProp;
+ typedef SInitialTargetProp<SHistoryEditor, SHistory> TInitialTargetProp;
+
+ TPropertyAccessorList &retval(inList);
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+ retval.push_back(QT3DS_NEW(alloc, TIdPropType)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SHistory::m_Id));
+ retval.push_back(QT3DS_NEW(alloc, THistoryParentProp)(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable));
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+ SStateEditor::CreatePositionColorAccessors<SHistoryEditor>(inEditorData, retval);
+ retval.push_back(QT3DS_NEW(
+ alloc, THistoryFlagsProp(inEditorData.m_EditorFoundation,
+ *inEditorData.m_StringTable, "type", "shallow", "deep",
+ &SHistoryFlags::IsDeep, &SHistoryFlags::SetDeep)));
+
+ retval.push_back(QT3DS_NEW(alloc, TTransitionProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("transition"),
+ EditorPropertyTypes::ObjectList),
+ &SHistory::m_Transition));
+
+ retval.push_back(QT3DS_NEW(alloc, THistoryTransitionProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("default"),
+ EditorPropertyTypes::ObjectList)));
+
+ retval.push_back(QT3DS_NEW(alloc, TInitialTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("is initial target"),
+ EditorPropertyTypes::Boolean)));
+
+ return retval;
+ }
+
+ SHistoryEditor(SHistory &inData, SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ : SPositionalEditor(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual void RemoveObjectFromGraph() { SStateEditor::RemoveObjectFromGraph(*this); }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+ };
+
+ struct STransitionEditor : public SEditorImplObject
+ {
+ typedef STransition TStateType;
+ STransition &m_Data;
+ enum { EditorType = EditorTypes::Transition };
+ static const char8_t *GetTypeStr() { return "transition"; }
+ TVec2List m_PathList;
+
+ static TPropertyAccessorList CreateAccessors(SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ typedef SDataProp<STransitionEditor, STransition, const char8_t *> TTransCharProp;
+ typedef SDataProp<STransitionEditor, STransition, CRegisteredString>
+ TTransRegStrProp;
+ typedef SDataProp<STransitionEditor, STransition, NVConstDataRef<SStateNode *>>
+ TTransTargetProp;
+ typedef SDataProp<STransitionEditor, STransition, NVConstDataRef<QT3DSVec2>>
+ TTransPathProp;
+ typedef SFlagBooleanProperty<STransitionEditor, STransitionFlags>
+ TTransitionFlagsProp;
+ typedef SDataIdProp<STransitionEditor, STransition> TIdPropType;
+ typedef SOptionAccessorProp<STransitionEditor, STransition, QT3DSVec2> TVec2Access;
+ typedef SOptionAccessorProp<STransitionEditor, STransition, QT3DSVec3> TVec3Access;
+ typedef SDataIdProp<STransitionEditor, STransition> TIdPropType;
+
+ TPropertyAccessorList &retval(inList);
+ NVAllocatorCallback &alloc(inEditorData.m_EditorFoundation->getAllocator());
+
+ retval.push_back(
+ QT3DS_NEW(alloc, TIdPropType)(inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("id"),
+ EditorPropertyTypes::String),
+ &STransition::m_Id));
+
+ retval.push_back(QT3DS_NEW(alloc, TTransRegStrProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("event"),
+ EditorPropertyTypes::String),
+ &STransition::m_Event));
+
+ SEditorImplObject::CreateAccessors(inEditorData, retval, false, false);
+ IPropertyAccessor *accessor = QT3DS_NEW(alloc, TTransCharProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("cond"),
+ EditorPropertyTypes::BigString),
+ &STransition::m_Condition);
+
+ retval.push_back(accessor);
+ accessor = QT3DS_NEW(alloc, TTransTargetProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("target"),
+ EditorPropertyTypes::ObjectList),
+ &STransition::m_Target);
+ retval.push_back(accessor);
+ retval.push_back(QT3DS_NEW(alloc, TTransitionFlagsProp)(
+ inEditorData.m_EditorFoundation, *inEditorData.m_StringTable, "type",
+ "external", "internal", &STransitionFlags::IsInternal,
+ &STransitionFlags::SetInternal));
+
+ accessor = QT3DS_NEW(alloc, TTransPathProp)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("path"),
+ EditorPropertyTypes::PositionList),
+ &STransition::m_Path);
+ retval.push_back(accessor);
+ retval.push_back(QT3DS_NEW(alloc, TVec2Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("position"),
+ EditorPropertyTypes::PositionList),
+ &STransition::GetPosition, &STransition::SetPosition));
+
+ retval.push_back(QT3DS_NEW(alloc, TVec2Access)(
+ inEditorData.m_EditorFoundation,
+ SPropertyDeclaration(inEditorData.RegisterStr("end position"),
+ EditorPropertyTypes::PositionList),
+ &STransition::GetEndPosition, &STransition::SetEndPosition));
+
+ return retval;
+ }
+
+ STransitionEditor(STransition &inData, SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ : SEditorImplObject(GetTypeStr(), inEditorData,
+ CreateAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual void RemoveObjectFromGraph() { SStateEditor::RemoveObjectFromGraph(*this); }
+
+ virtual TEditorStr GetId() { return m_Data.m_Id.c_str(); }
+ virtual void RemoveIdFromContext() { m_Editor.GetStateContext().EraseId(m_Data.m_Id); }
+
+ virtual void AddIdToContext()
+ {
+ if (m_Data.m_Id.IsValid())
+ m_Editor.GetStateContext().InsertId(m_Data.m_Id, &m_Data);
+ }
+
+ virtual TObjPtr Parent() { return m_Editor.ToEditor(m_Data.m_Parent); }
+
+ virtual NVConstDataRef<InterpreterEventTypes::Enum> GetExecutableContentTypes()
+ {
+ static InterpreterEventTypes::Enum data[] = { InterpreterEventTypes::Transition };
+ return toConstDataRef(data, 1);
+ }
+
+ virtual TObjList GetExecutableContent(InterpreterEventTypes::Enum inType)
+ {
+ TObjList retval;
+ if (inType == InterpreterEventTypes::Transition) {
+ for (TExecutableContentList::iterator iter = m_Data.m_ExecutableContent.begin(),
+ end = m_Data.m_ExecutableContent.end();
+ iter != end; ++iter) {
+ retval.push_back(m_Editor.ExecutableContentToEditor(*iter));
+ }
+ }
+ return retval;
+ }
+ virtual TObjPtr CreateAndAppendExecutableContent(InterpreterEventTypes::Enum inType,
+ const char8_t *inName)
+ {
+ TObjPtr retval;
+ if (inType == InterpreterEventTypes::Transition) {
+ retval = m_Editor.CreateExecutableContent(m_Data, inName);
+ SExecutableContent *theContent = m_Editor.ExecutableContentFromEditor(retval);
+ m_Data.m_ExecutableContent.push_back(*theContent);
+ if (m_Editor.GetOpenTransactionImpl()) {
+ NVScopedRefCounted<SExecutableContentChange> newChange =
+ new SExecutableContentChange(*this, &m_Data.m_ExecutableContent,
+ theContent, true);
+ m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(newChange.mPtr);
+ }
+ }
+ return retval;
+ }
+ };
+
+ struct SExecutableContentParentInfo
+ {
+ SExecutableContent &content;
+ SEditorImpl &m_Editor;
+ TExecutableContentList *parentList;
+ TObjPtr editorObj;
+ TOnEntryList *entryList;
+ SOnEntry *entryItem;
+ TOnExitList *exitList;
+ SOnExit *exitItem;
+ SExecutableContentParentInfo(SExecutableContent &c, SEditorImpl &e)
+ : content(c)
+ , m_Editor(e)
+ , parentList(NULL)
+ , entryList(NULL)
+ , entryItem(NULL)
+ , exitList(NULL)
+ , exitItem(NULL)
+ {
+ if (GetContent().m_StateNodeParent) {
+ editorObj = m_Editor.ToEditor(*GetContent().m_StateNodeParent);
+ if (GetContent().m_StateNodeParent->m_Type == StateNodeTypes::Transition)
+ parentList = &static_cast<STransition *>(GetContent().m_StateNodeParent)
+ ->m_ExecutableContent;
+ else {
+ SExecutableContent *targetContent = &GetContent();
+ entryList = GetContent().m_StateNodeParent->GetOnEntryList();
+ exitList = GetContent().m_StateNodeParent->GetOnExitList();
+ if (entryList) {
+ for (TOnEntryList::iterator iter = entryList->begin(),
+ end = entryList->end();
+ iter != end && parentList == NULL; ++iter) {
+ for (TExecutableContentList::iterator
+ contentIter = iter->m_ExecutableContent.begin(),
+ contentEnd = iter->m_ExecutableContent.end();
+ contentIter != contentEnd && parentList == NULL;
+ ++contentIter) {
+ if (&(*contentIter) == targetContent) {
+ parentList = &iter->m_ExecutableContent;
+ exitList = NULL;
+ entryItem = &(*iter);
+ }
+ }
+ }
+ }
+ if (parentList == NULL && exitList != NULL) {
+ for (TOnExitList::iterator iter = exitList->begin(),
+ end = exitList->end();
+ iter != end && parentList == NULL; ++iter) {
+ for (TExecutableContentList::iterator
+ contentIter = iter->m_ExecutableContent.begin(),
+ contentEnd = iter->m_ExecutableContent.end();
+ contentIter != contentEnd && parentList == NULL;
+ ++contentIter) {
+ if (&(*contentIter) == targetContent) {
+ parentList = &iter->m_ExecutableContent;
+ entryList = NULL;
+ exitItem = &(*iter);
+ }
+ }
+ }
+ }
+ }
+ } else {
+ editorObj = m_Editor.ToEditor(GetContent().m_Parent);
+ parentList = &GetContent().m_Parent->m_Children;
+ }
+ }
+ SExecutableContent &GetContent() { return content; }
+ };
+
+ struct SExecutableContentEditor : public SEditorImplObject
+ {
+ SExecutableContentEditor(const char8_t *inTypeStr, SEditorImpl &inEditorData,
+ const TPropertyAccessorList &inList)
+ : SEditorImplObject(inTypeStr, inEditorData, inList)
+ {
+ }
+
+ virtual SExecutableContent &GetContent() = 0;
+
+ virtual void RemoveObjectFromGraph()
+ {
+ SExecutableContentParentInfo parentInfo(GetContent(), m_Editor);
+ TExecutableContentList *parentList = parentInfo.parentList;
+ TObjPtr editorObj = parentInfo.editorObj;
+ TOnEntryList *entryList = parentInfo.entryList;
+ SOnEntry *entryItem = parentInfo.entryItem;
+ TOnExitList *exitList = parentInfo.exitList;
+ SOnExit *exitItem = parentInfo.exitItem;
+
+ if (parentList) {
+ NVScopedRefCounted<SExecutableContentChange> change =
+ new SExecutableContentChange(editorObj, parentList, &GetContent(), false);
+ change->Do();
+ if (m_Editor.GetOpenTransactionImpl())
+ m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(change.mPtr);
+ // Perhaps remove the item itself
+ if (parentList->empty()) {
+ NVScopedRefCounted<IChange> newChange;
+ if (entryItem)
+ newChange = new TListInsertDeleteChange<TOnEntryList, SOnEntry>(
+ editorObj, entryList, entryItem, false);
+ else if (exitItem)
+ newChange = new TListInsertDeleteChange<TOnExitList, SOnExit>(
+ editorObj, exitList, exitItem, false);
+
+ if (newChange) {
+ newChange->Do();
+ if (m_Editor.GetOpenTransactionImpl())
+ m_Editor.GetOpenTransactionImpl()->m_Changes.push_back(newChange);
+ }
+ }
+ }
+ }
+ };
+
+ struct SSendEditor : public SExecutableContentEditor
+ {
+ typedef SSend TStateType;
+ enum { EditorType = EditorTypes::Send };
+ SSend &m_Data;
+ static const char8_t *GetTypeStr() { return "send"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ typedef SDataIdProp<SSendEditor, SSend> TIdPropType;
+
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SSendEditor>(inData, &SSend::m_Event, "event",
+ EditorPropertyTypes::String));
+ NVFoundationBase &fnd = inData.m_EditorFoundation->getFoundation();
+
+ retval.push_back(QT3DS_NEW(fnd.getAllocator(), TIdPropType)(
+ inData.m_EditorFoundation,
+ SPropertyDeclaration(inData.RegisterStr("id"), EditorPropertyTypes::Id),
+ &SSend::m_Id));
+
+ retval.push_back(QT3DS_NEW(fnd.getAllocator(), SDelayProp<SSendEditor>)(
+ inData.m_EditorFoundation,
+ SPropertyDeclaration(inData.RegisterStr("delay"), EditorPropertyTypes::U32)));
+ return retval;
+ }
+ SSendEditor(SSend &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SRaiseEditor : public SExecutableContentEditor
+ {
+ typedef SRaise TStateType;
+ enum { EditorType = EditorTypes::Raise };
+ SRaise &m_Data;
+ static const char8_t *GetTypeStr() { return "raise"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SRaiseEditor>(inData, &SRaise::m_Event, "event",
+ EditorPropertyTypes::String));
+ return retval;
+ }
+ SRaiseEditor(SRaise &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SLogEditor : public SExecutableContentEditor
+ {
+ typedef SLog TStateType;
+ enum { EditorType = EditorTypes::Log };
+ SLog &m_Data;
+ static const char8_t *GetTypeStr() { return "log"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SLogEditor>(inData, &SLog::m_Label, "label",
+ EditorPropertyTypes::String));
+ retval.push_back(CreateDataAccessor<SLogEditor>(inData, &SLog::m_Expression, "expr",
+ EditorPropertyTypes::String));
+ return retval;
+ }
+ SLogEditor(SLog &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SAssignEditor : public SExecutableContentEditor
+ {
+ typedef SAssign TStateType;
+ enum { EditorType = EditorTypes::Assign };
+ SAssign &m_Data;
+ static const char8_t *GetTypeStr() { return "assign"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SAssignEditor>(
+ inData, &SAssign::m_Location, "location", EditorPropertyTypes::String));
+ retval.push_back(CreateDataAccessor<SAssignEditor>(
+ inData, &SAssign::m_Expression, "expr", EditorPropertyTypes::String));
+ return retval;
+ }
+ SAssignEditor(SAssign &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SIfEditor : public SExecutableContentEditor
+ {
+ typedef SIf TStateType;
+ enum { EditorType = EditorTypes::If };
+ SIf &m_Data;
+ static const char8_t *GetTypeStr() { return "if"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SIfEditor>(inData, &SIf::m_Cond, "cond",
+ EditorPropertyTypes::String));
+ return retval;
+ }
+
+ SIfEditor(SIf &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SElseIfEditor : public SExecutableContentEditor
+ {
+ typedef SElseIf TStateType;
+ enum { EditorType = EditorTypes::ElseIf };
+ SElseIf &m_Data;
+ static const char8_t *GetTypeStr() { return "elseif"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SElseIfEditor>(inData, &SElseIf::m_Cond, "cond",
+ EditorPropertyTypes::String));
+ return retval;
+ }
+
+ SElseIfEditor(SElseIf &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SElseEditor : public SExecutableContentEditor
+ {
+ typedef SElse TStateType;
+ enum { EditorType = EditorTypes::Else };
+ SElse &m_Data;
+ static const char8_t *GetTypeStr() { return "else"; }
+
+ SElseEditor(SElse &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData, inList)
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SScriptEditor : public SExecutableContentEditor
+ {
+ typedef SScript TStateType;
+ enum { EditorType = EditorTypes::Script };
+ SScript &m_Data;
+ static const char8_t *GetTypeStr() { return "script"; }
+
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SScriptEditor>(
+ inData, &SScript::m_Data, "content", EditorPropertyTypes::BigString));
+ return retval;
+ }
+
+ SScriptEditor(SScript &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SCancelEditor : public SExecutableContentEditor
+ {
+ typedef SData TStateType;
+ enum { EditorType = EditorTypes::Cancel };
+ SCancel &m_Data;
+ static const char8_t *GetTypeStr() { return "cancel"; }
+
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList retval;
+ retval.push_back(CreateDataAccessor<SCancelEditor>(
+ inData, &SCancel::m_Send, "sendid", EditorPropertyTypes::Object));
+ retval.push_back(CreateDataAccessor<SCancelEditor>(
+ inData, &SCancel::m_IdExpression, "sendidexpr", EditorPropertyTypes::String));
+ return retval;
+ }
+
+ SCancelEditor(SCancel &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SExecutableContentEditor(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual SExecutableContent &GetContent() { return m_Data; }
+ virtual void *GetWrappedObject() { return &m_Data; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SDataModelEditor : public SEditorImplObject
+ {
+ typedef SDataModel TStateType;
+ enum { EditorType = EditorTypes::DataModel };
+ SDataModel &m_Data;
+ static const char8_t *GetTypeStr() { return "datamodel"; }
+
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList &retval(inList);
+ retval.push_back(CreateDataAccessor<SDataModelEditor>(
+ inData, &SDataModel::m_Data, "data", EditorPropertyTypes::ObjectList));
+ return retval;
+ }
+
+ SDataModelEditor(SDataModel &inData, SEditorImpl &inEditorData,
+ TPropertyAccessorList &inList)
+ : SEditorImplObject(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SDataEditor : public SEditorImplObject
+ {
+ typedef SData TStateType;
+ enum { EditorType = EditorTypes::Data };
+ SData &m_Data;
+ static const char8_t *GetTypeStr() { return "data"; }
+ static TPropertyAccessorList GetPropertyAccessors(SEditorImpl &inData,
+ TPropertyAccessorList &inList)
+ {
+ if (inList.size())
+ return inList;
+ TPropertyAccessorList retval;
+ retval.push_back(CreateDataAccessor<SDataEditor>(inData, &SData::m_Id, "id",
+ EditorPropertyTypes::String));
+ retval.push_back(CreateDataAccessor<SDataEditor>(
+ inData, &SData::m_Expression, "expr", EditorPropertyTypes::String));
+ return retval;
+ }
+
+ SDataEditor(SData &inData, SEditorImpl &inEditorData, TPropertyAccessorList &inList)
+ : SEditorImplObject(GetTypeStr(), inEditorData,
+ GetPropertyAccessors(inEditorData, inList))
+ , m_Data(inData)
+ {
+ }
+
+ QT3DS_STATE_EDITOR_OBJECT_IMPLEMENT_ADDREF_RELEASE;
+
+ virtual void *GetWrappedObject() { return &m_Data; }
+
+ virtual CRegisteredString GetId() const { return m_Data.m_Id; }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ };
+
+ struct SNullEditor
+ {
+ int m_Data;
+ static const char8_t *GetTypeStr() { return ""; }
+ };
+
+ template <typename TStateType>
+ struct SStateEditorMap
+ {
+ typedef SNullEditor TEditorType;
+ template <typename TIgnored>
+ static TObjPtr CreateEditor(TIgnored &, SEditorImpl &, TAccessorMap &)
+ {
+ return TObjPtr();
+ }
+ };
+
+ template <typename TEditorType>
+ struct SEditorImplStateMap
+ {
+ typedef int TStateType;
+ };
+
+ static inline TPropertyAccessorList &GetAccessorList(TAccessorMap &inMap, int inAccessor)
+ {
+ return inMap.insert(eastl::make_pair(inAccessor, TPropertyAccessorList()))
+ .first->second;
+ }
+
+ template <>
+ struct SStateEditorMap<STransition>
+ {
+ typedef STransition stateType;
+ typedef STransitionEditor TEditorType;
+ typedef STransitionEditor editorType;
+ static TObjPtr CreateEditor(stateType &inData, SEditorImpl &inEditorData,
+ TAccessorMap &inAccessors)
+ {
+ return QT3DS_NEW(inEditorData.m_EditorFoundation->getAllocator(), TEditorType)(
+ inData, inEditorData,
+ GetAccessorList(inAccessors, (int)editorType::EditorType));
+ }
+ static TObjPtr CreateEditor(SEditorImpl &inEditorData, TAccessorMap &inAccessors)
+ {
+ STransition *newTransition = QT3DS_NEW(inEditorData.m_AutoAllocator, stateType)();
+ newTransition->m_Flags.SetInternal(true);
+ return CreateEditor(*newTransition, inEditorData, inAccessors);
+ }
+ };
+ template <>
+ struct SEditorImplStateMap<STransitionEditor>
+ {
+ typedef STransition TStateType;
+ };
+
+#define DEFINE_STATE_EDITOR_TYPE_MAP(stateType, editorType) \
+ template <> \
+ struct SStateEditorMap<stateType> \
+ { \
+ typedef editorType TEditorType; \
+ static TObjPtr CreateEditor(stateType &inData, SEditorImpl &inEditorData, \
+ TAccessorMap &inAccessors) \
+ { \
+ return QT3DS_NEW(inEditorData.m_EditorFoundation->getAllocator(), TEditorType)( \
+ inData, inEditorData, GetAccessorList(inAccessors, (int)editorType::EditorType)); \
+ } \
+ static TObjPtr CreateEditor(SEditorImpl &inEditorData, TAccessorMap &inAccessors) \
+ { \
+ return CreateEditor(*QT3DS_NEW(inEditorData.m_AutoAllocator, stateType)(), inEditorData, \
+ inAccessors); \
+ } \
+ }; \
+ template <> \
+ struct SEditorImplStateMap<editorType> \
+ { \
+ typedef stateType TStateType; \
+ };
+
+ DEFINE_STATE_EDITOR_TYPE_MAP(SSCXML, SSCXMLEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SState, SStateEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SParallel, SParallelEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SHistory, SHistoryEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SFinal, SFinalEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SSend, SSendEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SRaise, SRaiseEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SLog, SLogEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SAssign, SAssignEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SIf, SIfEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SElseIf, SElseIfEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SElse, SElseEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SScript, SScriptEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SDataModel, SDataModelEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SData, SDataEditor);
+ DEFINE_STATE_EDITOR_TYPE_MAP(SCancel, SCancelEditor);
+
+#undef DEFINE_STATE_EDITOR_TYPE_MAP
+ }
+}
+}
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorFoundation.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorFoundation.h
new file mode 100644
index 00000000..edb55ee3
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorFoundation.h
@@ -0,0 +1,81 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_FOUNDATION_H
+#define QT3DS_STATE_EDITOR_FOUNDATION_H
+#pragma once
+
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSAtomic.h"
+#include "foundation/Qt3DSFoundation.h"
+#include "foundation/Qt3DSBroadcastingAllocator.h"
+#include "foundation/AutoDeallocatorAllocator.h"
+#include "foundation/TrackingAllocator.h"
+#include "foundation/Qt3DSRefCounted.h"
+#include "foundation/IOStreams.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct SBaseEditorFoundation
+ {
+ static MallocAllocator g_BaseAlloc;
+ QT3DSI32 mRefCount;
+ CAllocator m_BaseAllocator;
+ NVScopedRefCounted<NVFoundation> m_Foundation;
+ SBaseEditorFoundation()
+ : mRefCount(0)
+ , m_BaseAllocator()
+ , m_Foundation(NVCreateFoundation(QT3DS_FOUNDATION_VERSION, m_BaseAllocator))
+ {
+ }
+
+ ~SBaseEditorFoundation() {}
+
+ NVFoundationBase &getFoundation() { return *m_Foundation; }
+ NVAllocatorCallback &getAllocator() { return m_Foundation->getAllocator(); }
+
+ QT3DS_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(g_BaseAlloc);
+
+ static SBaseEditorFoundation &Create()
+ {
+ SBaseEditorFoundation *fndPtr =
+ (SBaseEditorFoundation *)malloc(sizeof(SBaseEditorFoundation));
+ new (fndPtr) SBaseEditorFoundation();
+ return *fndPtr;
+ }
+ };
+
+ typedef NVScopedRefCounted<SBaseEditorFoundation> TFoundationPtr;
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorImpl.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorImpl.h
new file mode 100644
index 00000000..c1a691ee
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorImpl.h
@@ -0,0 +1,406 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_IMPL_H
+#define QT3DS_STATE_EDITOR_IMPL_H
+#include "Qt3DSStateEditor.h"
+#include "Qt3DSStateEditorValue.h"
+#include "Qt3DSStateTypes.h"
+#include "Qt3DSStateEditorFoundation.h"
+#include "foundation/Utils.h"
+#include "foundation/XML.h"
+#include "Qt3DSStateXMLIO.h"
+#include "foundation/IOStreams.h"
+#include "Qt3DSStateExecutionTypes.h"
+#include "Qt3DSStateContext.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct SEditorImpl;
+
+ typedef nvhash_map<void *, TObjPtr> TStateEditorMap;
+
+ class IPropertyAccessor : public NVRefCounted
+ {
+ protected:
+ virtual ~IPropertyAccessor() {}
+ public:
+ SPropertyDeclaration m_Declaration;
+ IPropertyAccessor(const SPropertyDeclaration &inDec)
+ : m_Declaration(inDec)
+ {
+ }
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject & /*inObj*/)
+ {
+ return eastl::vector<CRegisteredString>();
+ }
+
+ virtual Option<SValue> Get(IEditorObject &inObj) = 0;
+ virtual void Set(IEditorObject &inObj, const Option<SValue> &inValue) = 0;
+ // Return true if this access handles the transaction code itself.
+ // Currently only used for the is initial target property.
+ virtual bool HandlesTransaction() { return false; }
+ };
+
+ typedef NVScopedRefCounted<IPropertyAccessor> TPropertyAccessorPtr;
+ typedef eastl::vector<TPropertyAccessorPtr> TPropertyAccessorList;
+ typedef nvhash_map<int, TPropertyAccessorList> TAccessorMap;
+
+#define QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(foundationPtr, refcountVar) \
+ void addRef() { atomicIncrement(&(refcountVar)); } \
+ void release() \
+ { \
+ TFoundationPtr temp(foundationPtr); \
+ QT3DSI32 value = atomicDecrement(&(refcountVar)); \
+ if (value <= 0) \
+ NVDelete(foundationPtr->getAllocator(), this); \
+ }
+
+ struct SEditorImplStrIOStream : public IOutStream
+ {
+ TEditorStr m_Str;
+ bool Write(NVConstDataRef<QT3DSU8> data)
+ {
+ if (data.size())
+ m_Str.append((const char8_t *)data.begin(), (const char8_t *)data.end());
+ return true;
+ }
+ };
+
+ struct SEditorImplStrInStream : public IInStream
+ {
+ const char8_t *m_Pos;
+ const char8_t *m_End;
+ SEditorImplStrInStream(const TEditorStr &inStr)
+ : m_Pos(inStr.data())
+ , m_End(inStr.data() + inStr.size())
+ {
+ }
+
+ virtual QT3DSU32 Read(NVDataRef<QT3DSU8> data)
+ {
+ QT3DSU32 amountLeft = (QT3DSU32)(m_End - m_Pos);
+ QT3DSU32 amountToRead = NVMin(amountLeft, data.size());
+ memCopy(data.mData, m_Pos, amountToRead);
+ m_Pos += amountToRead;
+ return amountToRead;
+ }
+ };
+
+ struct STransaction;
+
+ typedef eastl::vector<IEditorChangeListener *> TChangeListenerList;
+ struct STransactionManagerImpl;
+
+ class IEditorCopyPasteListener
+ {
+ protected:
+ virtual ~IEditorCopyPasteListener() {}
+
+ public:
+ virtual void OnCopy(TEditorPtr inEditor, eastl::vector<SStateNode *> &ioCopiedRoots,
+ IDOMWriter &ioWriter,
+ eastl::vector<SNamespacePair> &ioNamespaces) = 0;
+ virtual void OnPaste(TEditorPtr inEditor, IDOMReader &ioReader,
+ CXMLIO::TIdRemapMap &inStateIdRemapMap) = 0;
+ virtual void OnIDChange(TEditorPtr inEditor, SStateNode &inNode,
+ const char8_t *inOldId) = 0;
+ };
+
+ // Forward declaration of the editor interface that the properties and the editor objects
+ // can all use. Implementation in UICStateEditor.cpp
+ struct SEditorImpl : public IEditor
+ {
+ // Note that *we* keep references to our editors
+ // but our editor objects *cannot* keep a reference to us.
+ // This means that our editor objects need to be able to function (not crash) if we
+ // ourselves go out of scope.
+ TFoundationPtr m_EditorFoundation;
+ NVScopedRefCounted<IStringTable> m_StringTable;
+ SSAutoDeallocatorAllocator m_AutoAllocator;
+ NVScopedRefCounted<IStateContext> m_StateContext;
+ TStateEditorMap m_Editors;
+ volatile QT3DSI32 mRefCount;
+ NVScopedRefCounted<STransactionManagerImpl> m_TransactionManager;
+ TAccessorMap m_Accessors;
+ IEditorCopyPasteListener *m_CopyPasteListener;
+
+ SEditorImpl(TFoundationPtr inFoundation,
+ NVScopedRefCounted<IStringTable> inStringTable);
+ void addRef();
+ void release();
+
+ TObjPtr InsertEditor(void *inData, IEditorObject *inEditor);
+
+ template <typename TStateType>
+ TObjPtr ToEditor(TStateType &inItem);
+
+ template <typename TStateType>
+ TObjPtr ToEditor(TStateType *inItem);
+ TObjPtr ToEditor(SStateNode &inItem);
+ TObjPtr ExecutableContentToEditor(SExecutableContent &inItem);
+
+ template <typename TStateType>
+ TStateType *FromEditor(TObjPtr inPtr);
+
+ SStateNode *StateNodeFromEditor(TObjPtr inPtr);
+
+ SExecutableContent *ExecutableContentFromEditor(TObjPtr inPtr);
+ void GenerateUniqueId(SStateNode &inNode, const char8_t *inStem);
+ void GenerateUniqueId(SSend &inNode, const char8_t *inStem);
+
+ virtual TObjPtr GetRoot();
+
+ template <typename TStateType>
+ eastl::pair<TStateType *, TObjPtr> CreateEditorAndObject();
+
+ TObjPtr DoCreate(const char8_t *inTypeName, const char8_t *inId);
+
+ virtual TObjPtr Create(const char8_t *inTypeName, const char8_t *inId);
+
+ virtual TObjPtr GetOrCreate(SSCXML &inData);
+ virtual TObjPtr GetOrCreate(SState &inData);
+ virtual TObjPtr GetOrCreate(STransition &inData);
+ virtual TObjPtr GetOrCreate(SParallel &inData);
+ virtual TObjPtr GetOrCreate(SFinal &inData);
+ virtual TObjPtr GetOrCreate(SHistory &inData);
+ virtual TObjPtr GetOrCreate(SDataModel &inData);
+ virtual TObjPtr GetOrCreate(SData &inData);
+
+ virtual TObjPtr GetObjectById(const char8_t *inId);
+
+ virtual TObjPtr GetEditor(void *inGraphData);
+
+ virtual TSignalConnectionPtr AddChangeListener(IEditorChangeListener &inListener);
+
+ void RemoveChangeListener(IEditorChangeListener &inListener);
+
+ virtual TTransactionPtr BeginTransaction(const TEditorStr &inName);
+
+ virtual TTransactionPtr GetOpenTransaction();
+
+ STransaction *GetOpenTransactionImpl();
+
+ virtual void RollbackTransaction();
+
+ virtual void EndTransaction();
+
+ virtual TEditorStr Copy(TObjList inObjects, const QT3DSVec2 &inMousePos);
+
+ virtual bool CanPaste(TObjPtr inTarget);
+
+ void AddNewPasteObjectToTransaction(SStateNode &inNode);
+
+ virtual void Paste(const TEditorStr &inCopiedObjects, TObjPtr inTarget,
+ const QT3DSVec2 &inMousePos);
+
+ static bool IsDerivedFrom(SStateNode &child, SStateNode &parent);
+
+ // This method should always return a value because the scxml root item
+ // is the parent of everyone else.
+ static SStateNode &GetLeastCommonAncestor(SStateNode &lhs, SStateNode &rhs);
+
+ TObjPtr GetLeastCommonAncestor(NVConstDataRef<TObjPtr> inObjects);
+
+ TObjList GetCancelableSendIds();
+
+ TObjPtr ChangeExecutableContentType(TObjPtr inContent, const char8_t *newType);
+
+ TEditorStr ToXML(TObjPtr inContent);
+
+ eastl::pair<TEditorStr, TObjPtr> FromXML(TObjPtr inContent,
+ const TEditorStr &ioEditedXML);
+
+ // Return the set of events in use in the state machine.
+ TEditorStrList GetStateMachineEvents();
+
+ void ReleaseEditor(void *inData);
+
+ virtual bool Save(const char8_t *inFileName);
+ virtual void Save(IOutStream &inStream);
+
+ bool Load(IInStream &inStream, const char8_t *inFilename);
+
+ /////////////////////////////////////////////////////////////////////
+ // Property access helpers
+ /////////////////////////////////////////////////////////////////////
+ void SetIdProperty(SStateNode &inNode, const SValue &inData,
+ CRegisteredString &inTarget);
+ void SetIdProperty(SSend &inNode, const SValue &inData, CRegisteredString &inTarget);
+
+ void Set(const SValue &inData, CRegisteredString &inTarget)
+ {
+ TEditorStr theStr(inData.getData<TEditorStr>());
+ inTarget = m_StringTable->RegisterStr(theStr.c_str());
+ }
+
+ void Set(const SValue &inData, const char8_t *&inTarget)
+ {
+ TEditorStr theStr(inData.getData<TEditorStr>());
+ const char8_t *theData(theStr.c_str());
+
+ if (inTarget && *inTarget != 0)
+ m_AutoAllocator.deallocate(const_cast<char8_t *>(inTarget));
+
+ size_t len = StrLen(theData);
+ if (len == 0)
+ inTarget = NULL;
+
+ ++len; // account for null terminate
+
+ char8_t *newTarget = (char8_t *)m_AutoAllocator.allocate(len + 1, "graph string",
+ __FILE__, __LINE__);
+ memCopy(newTarget, theData, len);
+ inTarget = newTarget;
+ }
+ SValue Get(CRegisteredString &inTarget) { return inTarget.c_str(); }
+ SValue Get(const char8_t *inItem) { return nonNull(inItem); }
+
+ void Set(const Option<SValue> &inData, NVConstDataRef<SStateNode *> &inTarget);
+ SValue Get(NVConstDataRef<SStateNode *> &inTarget);
+
+ void Set(const Option<SValue> &inData, STransition *&inTarget);
+ Option<SValue> Get(STransition *&inTarget);
+
+ void SetInitial(const TObjList &inList, STransition *&outInitialTransition);
+ SValue GetInitial(STransition *inInitialTransition);
+
+ void SetInitialTarget(const Option<SValue> &inData, SStateNode &inNode);
+ SValue IsInitialTarget(SStateNode &inNode);
+
+ void Set(const Option<SValue> &inData, SDataModel *&inTarget);
+ SValue Get(SDataModel *inTarget);
+
+ void Set(const Option<SValue> &inData, TDataList &inTarget);
+ SValue Get(TDataList &inTarget);
+
+ void Set(const Option<SValue> &inData, NVConstDataRef<QT3DSVec2> &inTarget);
+ SValue Get(const NVConstDataRef<QT3DSVec2> &inTarget);
+
+ void Set(const Option<SValue> &inData, SSend *&inTarget);
+ SValue Get(SSend *inTarget);
+
+ void Set(const Option<SValue> &inData, TEditorStr &inTarget)
+ {
+ inTarget.clear();
+ if (inData.hasValue())
+ inTarget = inData->getData<TEditorStr>();
+ }
+
+ SValue Get(const TEditorStr &inTarget) { return inTarget; }
+
+ void Set(const Option<SValue> &inData, Option<QT3DSVec2> &inTarget)
+ {
+ if (inData.hasValue()) {
+ inTarget = inData->getData<QT3DSVec2>();
+ } else {
+ inTarget = Empty();
+ }
+ }
+ Option<SValue> Get(const Option<QT3DSVec2> &inTarget)
+ {
+ Option<SValue> retval;
+ if (inTarget.hasValue())
+ retval = SValue(inTarget.getValue());
+ return retval;
+ }
+
+ void Set(const Option<SValue> &inData, TVec2List &inTarget)
+ {
+ if (inData.hasValue()) {
+ inTarget = inData->getData<TVec2List>();
+ } else {
+ inTarget.clear();
+ }
+ }
+ SValue Get(const TVec2List &inTarget) { return inTarget; }
+
+ void Set(const Option<SValue> &inData, Option<QT3DSVec3> &inTarget)
+ {
+ if (inData.hasValue()) {
+ inTarget = inData->getData<QT3DSVec3>();
+ } else {
+ inTarget = Empty();
+ }
+ }
+ const Option<SValue> Get(const Option<QT3DSVec3> &inTarget)
+ {
+ Option<SValue> retval;
+ if (inTarget.hasValue())
+ retval = SValue(inTarget.getValue());
+ return retval;
+ }
+
+ const char8_t *ToGraphStr(const char8_t *inStr)
+ {
+ if (isTrivial(inStr))
+ return "";
+ QT3DSU32 len = StrLen(inStr) + 1;
+ char8_t *retval =
+ (char8_t *)m_AutoAllocator.allocate(len, "GraphStr", __FILE__, __LINE__);
+ memCopy(retval, inStr, len);
+ return retval;
+ }
+
+ SValue GetSendId(const char8_t *expression);
+ void SetSendId(const Option<SValue> &inData, const char8_t *&outExpression);
+
+ TObjPtr CreateExecutableContent(SStateNode &inParent, const char8_t *inTypeName);
+ eastl::pair<SExecutableContent *, TObjPtr>
+ CreateExecutableContent(const char8_t *inTypeName);
+
+ CRegisteredString RegisterStr(const char8_t *str)
+ {
+ return m_StringTable->RegisterStr(str);
+ }
+ IStateContext &GetStateContext();
+
+ TEditorStr GetDefaultInitialValue(SStateNode &inNode);
+ void GetLegalInitialValues(SStateNode &inNode,
+ eastl::vector<CRegisteredString> &outValues);
+ void SetInitialAttribute(const Option<SValue> &inData, SStateNode &inNode);
+
+ void SetTransactionManager(STransactionManagerImpl &trans);
+
+ void ReplaceExecutableContent(SExecutableContent &oldContent,
+ SExecutableContent &newContent);
+
+ eastl::vector<CRegisteredString> GetLegalHistoryDefaultValues(SHistory &inData);
+
+ eastl::vector<CRegisteredString> GetLegalParentIds(SStateNode &inNode);
+ void SetParent(SStateNode &inNode, const Option<SValue> &inValue);
+ void CheckAndSetValidHistoryDefault(TObjPtr inHistoryNode);
+ void SetValidHistoryDefault(TObjPtr inHistoryNode);
+ };
+ }
+}
+}
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorProperties.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorProperties.h
new file mode 100644
index 00000000..5f06fbdb
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorProperties.h
@@ -0,0 +1,599 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_PROPERTIES_H
+#define QT3DS_STATE_EDITOR_PROPERTIES_H
+#pragma once
+
+#include "Qt3DSStateEditorFoundation.h"
+#include "Qt3DSStateEditorImpl.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ template <typename TEditorType>
+ struct SPropertyAccessorBase : public IPropertyAccessor
+ {
+ TFoundationPtr m_Allocator;
+ volatile QT3DSI32 mRefCount;
+
+ SPropertyAccessorBase(TFoundationPtr alloc, const SPropertyDeclaration &inDec)
+ : IPropertyAccessor(inDec)
+ , m_Allocator(alloc)
+ , mRefCount(0)
+ {
+ }
+
+ virtual Option<SValue> Get(IEditorObject &inObj)
+ {
+ return DoGet(static_cast<TEditorType &>(inObj));
+ }
+ virtual void Set(IEditorObject &inObj, const Option<SValue> &inValue)
+ {
+ DoSet(static_cast<TEditorType &>(inObj), inValue);
+ }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj) = 0;
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue) = 0;
+ };
+
+ template <typename TEditorType>
+ struct SInitialProperty : SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ static SPropertyDeclaration CreatePropertyDeclaration(IStringTable &inStrTable)
+ {
+ SPropertyDeclaration theDeclaration;
+ theDeclaration.m_Name = inStrTable.RegisterStr("initial");
+ theDeclaration.m_Type = EditorPropertyTypes::Object;
+ return theDeclaration;
+ }
+ SInitialProperty(TFoundationPtr inAlloc, IStringTable &inStrTable)
+ : TBaseType(inAlloc, CreatePropertyDeclaration(inStrTable))
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return SValue(inObj.Get(inObj.m_Data.m_Initial));
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Initial =
+ inObj.template FromEditor<STransition>(inValue->getData<TObjPtr>());
+ }
+ };
+
+ template <typename TEditorType, typename TFlagsType>
+ struct SFlagBooleanProperty : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ typedef bool (TFlagsType::*TGetPropPtr)() const;
+ typedef void (TFlagsType::*TSetPropPtr)(bool);
+
+ TGetPropPtr m_GetProp;
+ TSetPropPtr m_SetProp;
+ eastl::vector<CRegisteredString> m_LegalValues;
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject & /*inObj*/)
+ {
+ return m_LegalValues;
+ }
+
+ static SPropertyDeclaration CreatePropertyDeclaration(IStringTable &inStrTable,
+ const char8_t *propName)
+ {
+ SPropertyDeclaration theDeclaration;
+ theDeclaration.m_Name = inStrTable.RegisterStr(propName);
+ theDeclaration.m_Type = EditorPropertyTypes::StringSet;
+ return theDeclaration;
+ }
+ SFlagBooleanProperty(TFoundationPtr inFnd, IStringTable &inStrTable,
+ const char8_t *inPropName, const char8_t *falseName,
+ const char8_t *trueName, TGetPropPtr inGetProp,
+ TSetPropPtr inSetProp)
+ : TBaseType(inFnd, CreatePropertyDeclaration(inStrTable, inPropName))
+ , m_GetProp(inGetProp)
+ , m_SetProp(inSetProp)
+ {
+ m_LegalValues.push_back(inStrTable.RegisterStr(falseName));
+ m_LegalValues.push_back(inStrTable.RegisterStr(trueName));
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ bool boolVal = (inObj.m_Data.m_Flags.*m_GetProp)();
+ TEditorStr retval = boolVal ? TEditorStr(m_LegalValues[1].c_str())
+ : TEditorStr(m_LegalValues[0].c_str());
+ return SValue(retval);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValueOpt)
+ {
+ if (inValueOpt.hasValue()) {
+ TEditorStr data = inValueOpt->getData<TEditorStr>();
+
+ if (AreEqual(data.c_str(), m_LegalValues[1].c_str()))
+ (inObj.m_Data.m_Flags.*m_SetProp)(true);
+
+ else if (AreEqual(data.c_str(), m_LegalValues[0].c_str()))
+ (inObj.m_Data.m_Flags.*m_SetProp)(false);
+
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ };
+
+ template <typename TEditorType>
+ struct SChildrenProperty : SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ static SPropertyDeclaration CreatePropertyDeclaration(IStringTable &inStrTable)
+ {
+ SPropertyDeclaration theDeclaration;
+ theDeclaration.m_Name = inStrTable.RegisterStr("children");
+ theDeclaration.m_Type = EditorPropertyTypes::ObjectList;
+ return theDeclaration;
+ }
+
+ SChildrenProperty(TFoundationPtr inAlloc, IStringTable &inStrTable)
+ : TBaseType(inAlloc, CreatePropertyDeclaration(inStrTable))
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TObjList retval;
+ for (TStateNodeList::iterator iter = inObj.m_Data.m_Children.begin(),
+ end = inObj.m_Data.m_Children.end();
+ iter != end; ++iter)
+ retval.push_back(inObj.m_Editor.ToEditor(*iter));
+ return SValue(retval);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ if (inValue.hasValue()) {
+ const TObjList &data = inValue->getData<TObjList>();
+ TStateNodeList &theChildren(inObj.m_Data.m_Children);
+ // De-set all children.
+ while (theChildren.empty() == false)
+ inObj.m_Data.RemoveChild(theChildren.front());
+
+ for (TObjList::const_iterator iter = data.begin(), end = data.end();
+ iter != end; ++iter) {
+ SStateNode *theNode = inObj.m_Editor.StateNodeFromEditor(*iter);
+ if (theNode)
+ inObj.m_Data.AppendChild(*theNode);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ }
+ };
+
+ // Property that is represented by a data item's member.
+ template <typename TEditorType, typename TStateType, typename TDataType>
+ struct SDataProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ typedef TDataType TStateType::*TPropertyPtr;
+ TPropertyPtr m_Ptr;
+ eastl::vector<CRegisteredString> m_LegalValues;
+
+ SDataProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec, TPropertyPtr inPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_Ptr(inPtr)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject & /*inObj*/)
+ {
+ return m_LegalValues;
+ }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.m_Editor.Get(inObj.m_Data.*(this->m_Ptr));
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Editor.Set(inValue, inObj.m_Data.*(this->m_Ptr));
+ }
+ };
+
+ template <typename TEditorType, typename TStateType>
+ struct SInitialTargetProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SInitialTargetProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec)
+ : TBaseType(inAlloc, inDec)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.m_Editor.IsInitialTarget(inObj.m_Data);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Editor.SetInitialTarget(inValue, inObj.m_Data);
+ }
+
+ virtual bool HandlesTransaction() { return true; }
+ };
+
+ template <typename TEditorType, typename TStateType>
+ struct SInitialComboProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SInitialComboProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec)
+ : TBaseType(inAlloc, inDec)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject &inEditor)
+ {
+ eastl::vector<CRegisteredString> retval;
+ TEditorType &theEditor = static_cast<TEditorType &>(inEditor);
+ retval.push_back(theEditor.m_Editor.RegisterStr("(script expression)"));
+ theEditor.m_Editor.GetLegalInitialValues(theEditor.m_Data, retval);
+ return retval;
+ }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TEditorType &theEditor = inObj;
+ if (theEditor.m_InitialComboValue.size() == 0) {
+ if (!isTrivial(inObj.m_Data.GetInitialExpression()))
+ theEditor.m_InitialComboValue = "(script expression)";
+ else if (inObj.m_Data.GetInitialTransition()
+ && inObj.m_Data.GetInitialTransition()->m_Target.size())
+ theEditor.m_InitialComboValue.assign(
+ inObj.m_Data.GetInitialTransition()->m_Target[0]->m_Id.c_str());
+ else
+ theEditor.m_InitialComboValue =
+ theEditor.m_Editor.GetDefaultInitialValue(theEditor.m_Data);
+ }
+ return theEditor.m_InitialComboValue;
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ if (inValue->getData<TEditorStr>() != "(script expression)") {
+ inObj.m_Editor.SetInitialAttribute(inValue, inObj.m_Data);
+ } else {
+ inObj.m_Editor.SetInitialAttribute(Option<SValue>(), inObj.m_Data);
+ }
+ }
+ virtual bool HandlesTransaction() { return true; }
+ };
+
+ template <typename TEditorType, typename TStateType, typename TDataType>
+ struct SOptionAccessorProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ typedef Option<TDataType> TOptType;
+ typedef TOptType (TStateType::*TGetPtr)() const;
+ typedef void (TStateType::*TSetPtr)(const TOptType &inOpt);
+ TGetPtr m_Getter;
+ TSetPtr m_Setter;
+
+ SOptionAccessorProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TGetPtr inPtr, TSetPtr inSetPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_Getter(inPtr)
+ , m_Setter(inSetPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TOptType theOpt = (inObj.m_Data.*m_Getter)();
+ if (theOpt.hasValue())
+ return SValue(*theOpt);
+ return Empty();
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ TOptType theNewValue;
+ if (inValue.hasValue())
+ theNewValue = TOptType(inValue->getData<TDataType>());
+ (inObj.m_Data.*m_Setter)(theNewValue);
+ }
+ };
+
+ template <typename TEditorType, typename TStateType>
+ struct SDataIdProp : SDataProp<TEditorType, TStateType, CRegisteredString>
+ {
+ typedef SDataProp<TEditorType, TStateType, CRegisteredString> TBaseType;
+ typedef CRegisteredString TStateType::*TPropertyPtr;
+
+ SDataIdProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyPtr inPtr)
+ : TBaseType(inAlloc, inDec, inPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ // This may mangle the id in order to find a unique id.
+ inObj.m_Editor.SetIdProperty(inObj.m_Data, inValue, inObj.m_Data.*(this->m_Ptr));
+ }
+ };
+
+ template <typename TEditorObject, typename TStateType, typename TDataType>
+ IPropertyAccessor *CreateDataAccessor(SEditorImpl &inData, TDataType TStateType::*inDataPtr,
+ const char8_t *inName,
+ EditorPropertyTypes::Enum inPropType)
+ {
+ typedef SDataProp<TEditorObject, TStateType, TDataType> TPropType;
+ return QT3DS_NEW(inData.m_EditorFoundation->getAllocator(), TPropType)(
+ inData.m_EditorFoundation,
+ SPropertyDeclaration(inData.RegisterStr(inName), inPropType), inDataPtr);
+ }
+
+ template <typename TEditorType, typename TDataType>
+ struct SEditorImplProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ typedef TDataType TEditorType::*TPropertyPtr;
+ TPropertyPtr m_Ptr;
+
+ SEditorImplProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyPtr inPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_Ptr(inPtr)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.m_Editor.Get(inObj.*(this->m_Ptr));
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Editor.Set(inValue, inObj.*(this->m_Ptr));
+ }
+ };
+
+ template <typename TEditorObject, typename TDataType>
+ IPropertyAccessor *
+ CreateEditorAccessor(SEditorImpl &inData, TDataType TEditorObject::*inDataPtr,
+ const char8_t *inName, EditorPropertyTypes::Enum inPropType)
+ {
+ typedef SEditorImplProp<TEditorObject, TDataType> TPropType;
+ return QT3DS_NEW(inData.m_EditorFoundation->getAllocator(), TPropType)(
+ inData.m_EditorFoundation,
+ SPropertyDeclaration(inData.RegisterStr(inName), inPropType), inDataPtr);
+ }
+
+ // General property for initial and history transition properties
+ template <typename TEditorType, typename TStateType>
+ struct SSCXMLInitialPtr : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef STransition *TStateType::*TPropertyType;
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ TPropertyType m_PropPtr;
+
+ SSCXMLInitialPtr(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyType inPropPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_PropPtr(inPropPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.GetInitial(inObj.m_Data.*m_PropPtr);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.SetInitial(inValue->getData<TObjList>(), inObj.m_Data.*m_PropPtr);
+ }
+ };
+
+ template <typename TEditorType, typename TStateType>
+ struct SSCXMLInitialContent : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef STransition *TStateType::*TPropertyType;
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+ TPropertyType m_PropPtr;
+
+ SSCXMLInitialContent(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyType inPropPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_PropPtr(inPropPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ return inObj.GetInitialContent(inObj.m_Data.*m_PropPtr);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ if (inValue.hasValue()) {
+ inObj.SetInitialContent(inValue->getData<TObjList>(), inObj.m_Data.*m_PropPtr);
+ } else {
+ inObj.SetInitialContent(TObjList(), inObj.m_Data.*m_PropPtr);
+ }
+ }
+ };
+
+ template <typename TEditorType>
+ struct SDelayProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SDelayProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec)
+ : TBaseType(inAlloc, inDec)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ if (!isTrivial(inObj.m_Data.m_DelayExpr))
+ return Empty();
+
+ return inObj.m_Editor.GetSendId(inObj.m_Data.m_Delay);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Data.m_DelayExpr = "";
+ inObj.m_Editor.SetSendId(inValue, inObj.m_Data.m_Delay);
+ }
+ };
+
+ template <typename TEditorType>
+ struct SHistoryTransitionProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SHistoryTransitionProp(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec)
+ : TBaseType(inAlloc, inDec)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject &inEditor)
+ {
+ TEditorType &theEditor = static_cast<TEditorType &>(inEditor);
+ return theEditor.m_Editor.GetLegalHistoryDefaultValues(theEditor.m_Data);
+ }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ if (inObj.m_Data.m_Transition)
+ return inObj.m_Editor.Get(inObj.m_Data.m_Transition->m_Target);
+
+ return SValue(TObjList());
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ TObjList newObjects;
+
+ if (inValue.hasValue())
+ newObjects = inValue->getData<TObjList>();
+
+ if (newObjects.size()) {
+ if (!inObj.m_Data.m_Transition) {
+ inObj.m_Data.m_Transition =
+ (STransition *)inObj.m_Editor.m_AutoAllocator.allocate(
+ sizeof(STransition), "transition", __FILE__, __LINE__);
+ new (inObj.m_Data.m_Transition) STransition();
+ inObj.m_Data.m_Transition->m_Parent = &inObj.m_Data;
+ }
+ inObj.m_Editor.Set(inValue, inObj.m_Data.m_Transition->m_Target);
+ } else
+ inObj.m_Data.m_Transition = NULL;
+ }
+ };
+
+ template <typename TEditorType>
+ struct SParentProp : public SPropertyAccessorBase<TEditorType>
+ {
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ SParentProp(TFoundationPtr inAlloc, IStringTable &inStrTable)
+ : TBaseType(inAlloc, SPropertyDeclaration(inStrTable.RegisterStr("parent"),
+ EditorPropertyTypes::StringSet))
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ static const char *SCXMLParentName() { return "(none)"; }
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TObjPtr current = inObj.Parent();
+ if (current && AreEqual(current->TypeName(), "scxml") == false)
+ return SValue(current->GetId());
+ return Option<SValue>(SValue(eastl::string(SCXMLParentName())));
+ }
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(IEditorObject &inEditor)
+ {
+ TEditorType &theEditor = static_cast<TEditorType &>(inEditor);
+ eastl::vector<CRegisteredString> retval =
+ theEditor.m_Editor.GetLegalParentIds(theEditor.m_Data);
+ CRegisteredString parentName(theEditor.m_Editor.RegisterStr(SCXMLParentName()));
+ retval.insert(retval.begin(), parentName);
+ return retval;
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ inObj.m_Editor.SetParent(inObj.m_Data, inValue);
+ }
+
+ virtual bool HandlesTransaction() { return true; }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.cpp
new file mode 100644
index 00000000..1bc0faf7
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.cpp
@@ -0,0 +1,239 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSStateEditorTransactionImpl.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ // Some things have to be implemented below so that we can take advantage of the full
+ // SEditorImpl definition.
+
+ STransaction::STransaction(TFoundationPtr alloc, STransactionManagerImpl &inEditor,
+ const TEditorStr &inName)
+ : ITransaction(inName)
+ , m_Alloc(alloc)
+ , mRefCount(0)
+ , m_Editor(inEditor)
+ {
+ }
+
+ STransaction::~STransaction() {}
+
+ void STransaction::addRef() { atomicIncrement(&mRefCount); }
+
+ void STransaction::release()
+ {
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ TFoundationPtr fnd(m_Alloc);
+ NVDelete(fnd->getAllocator(), this);
+ }
+ }
+
+ void STransaction::ChangesToEditors()
+ {
+ m_EditorsList.clear();
+ m_EditorsList.resize(m_Changes.size());
+ eastl::transform(m_Changes.begin(), m_Changes.end(), m_EditorsList.begin(),
+ SChangeToEditor());
+ }
+
+ void STransaction::SendDoSignals()
+ {
+ ChangesToEditors();
+ CreateRemovedEditorsList(true);
+ for (TChangeListenerList::iterator listenerIter = m_Editor->m_ChangeListeners.begin(),
+ listenerEnd = m_Editor->m_ChangeListeners.end();
+ listenerIter != listenerEnd; ++listenerIter)
+ (*listenerIter)->OnDataChange(m_EditorsList, m_RemovedEditorsList);
+ }
+
+ void STransaction::Do()
+ {
+ for (TChangeList::iterator iter = m_Changes.begin(), end = m_Changes.end(); iter != end;
+ ++iter)
+ (*iter)->Do();
+
+ for (TRemovePairList::iterator iter = m_RemovedPairs.begin(),
+ end = m_RemovedPairs.end();
+ iter != end; ++iter) {
+ if (iter->first)
+ iter->second->RemoveIdFromContext();
+ else
+ iter->second->AddIdToContext();
+ }
+
+ SendDoSignals();
+ }
+
+ void STransaction::SilentUndo()
+ {
+ for (TChangeList::reverse_iterator iter = m_Changes.rbegin(), end = m_Changes.rend();
+ iter != end; ++iter)
+ (*iter)->Undo();
+
+ for (TRemovePairList::iterator iter = m_RemovedPairs.begin(),
+ end = m_RemovedPairs.end();
+ iter != end; ++iter) {
+ if (iter->first)
+ iter->second->AddIdToContext();
+ else
+ iter->second->RemoveIdFromContext();
+ }
+ }
+
+ void STransaction::Undo()
+ {
+ SilentUndo();
+ m_EditorsList.clear();
+ m_EditorsList.resize(m_Changes.size());
+ eastl::transform(m_Changes.rbegin(), m_Changes.rend(), m_EditorsList.begin(),
+ SChangeToEditor());
+ CreateRemovedEditorsList(false);
+ for (TChangeListenerList::iterator listenerIter = m_Editor->m_ChangeListeners.begin(),
+ listenerEnd = m_Editor->m_ChangeListeners.end();
+ listenerIter != listenerEnd; ++listenerIter)
+ (*listenerIter)->OnDataChange(m_EditorsList, m_RemovedEditorsList);
+ }
+
+ struct STransactionImplConnection : public IStateSignalConnection
+ {
+ TFoundationPtr &m_Alloc;
+ QT3DSI32 mRefCount;
+ STransactionManagerImpl &m_Editor;
+ IEditorChangeListener &m_ChangeListener;
+ STransactionImplConnection(TFoundationPtr &alloc, STransactionManagerImpl &e,
+ IEditorChangeListener &listener)
+ : m_Alloc(alloc)
+ , mRefCount(0)
+ , m_Editor(e)
+ , m_ChangeListener(listener)
+ {
+ }
+ // Implemented below editor to use editor's interfaces
+ virtual ~STransactionImplConnection()
+ {
+ m_Editor.RemoveChangeListener(m_ChangeListener);
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(m_Alloc, mRefCount);
+ };
+
+ void STransactionManagerImpl::addRef() { atomicIncrement(&mRefCount); }
+ void STransactionManagerImpl::release()
+ {
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ TFoundationPtr fnd(m_EditorFoundation);
+ NVDelete(fnd->getAllocator(), this);
+ }
+ }
+
+ TSignalConnectionPtr
+ STransactionManagerImpl::AddChangeListener(IEditorChangeListener &inListener)
+ {
+ m_ChangeListeners.push_back(&inListener);
+ // If we have any listeners, then we can't be destroyed as their connections will still
+ // need
+ // to talk to us to release its stuff.
+ if (m_ChangeListeners.size() == 1)
+ addRef();
+ return QT3DS_NEW(m_EditorFoundation->getAllocator(),
+ STransactionImplConnection)(m_EditorFoundation, *this, inListener);
+ }
+
+ void STransactionManagerImpl::RemoveChangeListener(IEditorChangeListener &inListener)
+ {
+ TChangeListenerList::iterator iter =
+ eastl::find(m_ChangeListeners.begin(), m_ChangeListeners.end(), &inListener);
+ if (iter != m_ChangeListeners.end())
+ m_ChangeListeners.erase(iter);
+ if (m_ChangeListeners.size() == 0)
+ release();
+ }
+ TTransactionPtr STransactionManagerImpl::BeginTransaction(const TEditorStr &inName)
+ {
+ if (!m_Transaction) {
+ QT3DS_ASSERT(m_OpenCount == 0);
+ m_Transaction = QT3DS_NEW(m_EditorFoundation->getAllocator(),
+ STransaction)(m_EditorFoundation, *this, inName);
+ }
+ ++m_OpenCount;
+ return m_Transaction.mPtr;
+ }
+
+ TTransactionPtr STransactionManagerImpl::GetOpenTransaction() { return m_Transaction.mPtr; }
+ STransaction *STransactionManagerImpl::GetOpenTransactionImpl()
+ {
+ return m_Transaction.mPtr;
+ }
+
+ void STransactionManagerImpl::RollbackTransaction()
+ {
+ --m_OpenCount;
+ if (m_OpenCount <= 0) {
+ if (m_Transaction) {
+ m_Transaction->Undo();
+ m_Transaction = NULL;
+ }
+ }
+ QT3DS_ASSERT(m_OpenCount == 0);
+ m_OpenCount = 0;
+ }
+
+ void STransactionManagerImpl::EndTransaction()
+ {
+ TTransactionPtr retval = m_Transaction.mPtr;
+ --m_OpenCount;
+ if (m_OpenCount <= 0)
+ m_Transaction = NULL;
+ QT3DS_ASSERT(m_OpenCount >= 0);
+ m_OpenCount = 0;
+ }
+
+ void STransactionManagerImpl::OnObjectDeleted(TObjPtr inObj)
+ {
+ if (m_Transaction)
+ m_Transaction->m_RemovedPairs.push_back(eastl::make_pair(true, inObj));
+
+ if (m_ObjListener)
+ m_ObjListener->OnObjectDeleted(inObj);
+ }
+ void STransactionManagerImpl::OnObjectCreated(TObjPtr inObj)
+ {
+ if (m_Transaction)
+ m_Transaction->m_RemovedPairs.push_back(eastl::make_pair(false, inObj));
+
+ if (m_ObjListener)
+ m_ObjListener->OnObjectCreated(inObj);
+ }
+ }
+}
+} \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.h
new file mode 100644
index 00000000..9996c351
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransactionImpl.h
@@ -0,0 +1,195 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_TRANSACTION_IMPL_H
+#define QT3DS_STATE_EDITOR_TRANSACTION_IMPL_H
+#include "Qt3DSStateEditor.h"
+#include "Qt3DSStateEditorFoundation.h"
+#include "Qt3DSStateEditorProperties.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ class IChange : public NVRefCounted
+ {
+ protected:
+ virtual ~IChange() {}
+ public:
+ virtual void Do() = 0;
+ virtual void Undo() = 0;
+ virtual TObjPtr GetEditor() = 0;
+ };
+
+ typedef eastl::vector<NVScopedRefCounted<IChange>> TChangeList;
+
+ struct SChange : public IChange
+ {
+ Option<SValue> m_OldVal;
+ Option<SValue> m_NewVal;
+ TPropertyAccessorPtr m_Accessor;
+ TObjPtr m_Editor;
+ QT3DSI32 mRefCount;
+
+ SChange(const Option<SValue> &inOldVal, const Option<SValue> &inNewVal,
+ IPropertyAccessor &inAccessor, IEditorObject &inEditor)
+ : m_OldVal(inOldVal)
+ , m_NewVal(inNewVal)
+ , m_Accessor(inAccessor)
+ , m_Editor(inEditor)
+ , mRefCount(0)
+ {
+ }
+ // Sometimes we need to create a change just to signal a new object.
+ // In this case there isn't an accessor and there aren't old values and
+ // new values.
+ SChange(IEditorObject &inEditor)
+ : m_Editor(inEditor)
+ , mRefCount(0)
+ {
+ }
+ SChange()
+ : mRefCount(0)
+ {
+ }
+ void Do()
+ {
+ if (m_Accessor)
+ m_Accessor->Set(*m_Editor, m_NewVal);
+ }
+
+ void Undo()
+ {
+ if (m_Accessor)
+ m_Accessor->Set(*m_Editor, m_OldVal);
+ }
+ void addRef() { atomicIncrement(&mRefCount); }
+ void release()
+ {
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0)
+ delete this;
+ }
+
+ TObjPtr GetEditor() { return m_Editor; }
+ };
+
+ // true if removed on do, false if removed on false;
+ typedef eastl::pair<bool, TObjPtr> TRemovePair;
+ typedef eastl::vector<TRemovePair> TRemovePairList;
+
+ struct STransactionManagerImpl;
+
+ struct STransaction : public ITransaction
+ {
+ TFoundationPtr m_Alloc;
+ TChangeList m_Changes;
+ volatile QT3DSI32 mRefCount;
+ // We have to keep a refcount to the editor ourselves.
+ NVScopedRefCounted<STransactionManagerImpl> m_Editor;
+ TObjList m_EditorsList;
+ TObjList m_RemovedEditorsList;
+ TRemovePairList m_RemovedPairs;
+
+ STransaction(TFoundationPtr alloc, STransactionManagerImpl &inEditor,
+ const TEditorStr &inName);
+ virtual ~STransaction();
+ virtual void addRef();
+ virtual void release();
+
+ void CreateRemovedEditorsList(bool inDo)
+ {
+ m_RemovedEditorsList.clear();
+ for (TRemovePairList::iterator iter = m_RemovedPairs.begin(),
+ end = m_RemovedPairs.end();
+ iter != end; ++iter)
+ if (iter->first == inDo)
+ m_RemovedEditorsList.push_back(iter->second);
+ }
+
+ struct SChangeToEditor
+ {
+ TObjPtr operator()(IChange *inChange) const { return inChange->GetEditor(); }
+ };
+ virtual void SendDoSignals();
+ virtual void Do();
+ virtual void SilentUndo();
+ virtual void Undo();
+ virtual bool Empty() { return m_Changes.empty() && m_RemovedPairs.empty(); }
+ virtual TObjList GetEditedObjects()
+ {
+ ChangesToEditors();
+ return m_EditorsList;
+ }
+ void ChangesToEditors();
+ };
+
+ class ITransManagerImplListener
+ {
+ protected:
+ virtual ~ITransManagerImplListener() {}
+ public:
+ virtual void OnObjectDeleted(TObjPtr inObj) = 0;
+ virtual void OnObjectCreated(TObjPtr inObj) = 0;
+ };
+
+ struct STransactionManagerImpl : public ITransactionManager
+ {
+ TFoundationPtr m_EditorFoundation;
+ volatile QT3DSI32 mRefCount;
+ TChangeListenerList m_ChangeListeners;
+ NVScopedRefCounted<STransaction> m_Transaction;
+ QT3DSI32 m_OpenCount;
+ ITransManagerImplListener *m_ObjListener;
+
+ STransactionManagerImpl(TFoundationPtr fnd)
+ : m_EditorFoundation(fnd)
+ , mRefCount(0)
+ , m_OpenCount(0)
+ , m_ObjListener(NULL)
+ {
+ }
+ virtual void addRef();
+ virtual void release();
+ virtual TSignalConnectionPtr AddChangeListener(IEditorChangeListener &inListener);
+ virtual TTransactionPtr BeginTransaction(const TEditorStr &inName = TEditorStr());
+ virtual TTransactionPtr GetOpenTransaction();
+ STransaction *GetOpenTransactionImpl();
+ virtual void RollbackTransaction();
+ virtual void EndTransaction();
+
+ void OnObjectDeleted(TObjPtr inObj);
+ void OnObjectCreated(TObjPtr inObj);
+
+ void RemoveChangeListener(IEditorChangeListener &inListener);
+ };
+ }
+}
+}
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.cpp
new file mode 100644
index 00000000..7fb4ab9f
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.cpp
@@ -0,0 +1,813 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSStateEditorTransitionPath.h"
+
+namespace {
+
+using namespace qt3ds::state::editor;
+using namespace qt3ds::state;
+
+bool inBounds(QT3DSF32 item, QT3DSF32 lower, QT3DSF32 upper)
+{
+ if (item >= lower && item <= upper)
+ return true;
+ return false;
+}
+
+DirectionTypes::Enum EdgeTypeToDirectionType(EdgeTypes::Enum inEdgeType)
+{
+ switch (inEdgeType) {
+ case EdgeTypes::Bottom:
+ case EdgeTypes::Top:
+ return DirectionTypes::Vertical;
+ default:
+ return DirectionTypes::Horizontal;
+ }
+}
+
+static inline SEndPoint DoGetActualEndPoint(const SEndPoint &inPoint, const SRect &inMyRect,
+ const QT3DSVec2 &inOtherCenter)
+{
+ SEndPoint theEndPoint = inPoint;
+ if (inPoint.m_EdgeType == EdgeTypes::UnsetEdgeType)
+ theEndPoint = CEditorTransitionPath::CalculateDefaultEndPoint(inMyRect, inOtherCenter);
+ return theEndPoint;
+}
+
+static inline eastl::pair<QT3DSVec2, EdgeTypes::Enum>
+GetEndpointPoint(const SEndPoint &inPoint, const SRect &inMyRect, const QT3DSVec2 &inOtherCenter)
+{
+ SEndPoint theEndPoint = DoGetActualEndPoint(inPoint, inMyRect, inOtherCenter);
+ SLine theRectLine;
+ switch (theEndPoint.m_EdgeType) {
+ case EdgeTypes::Top:
+ theRectLine = inMyRect.topEdge();
+ break;
+ case EdgeTypes::Bottom:
+ theRectLine = inMyRect.bottomEdge();
+ break;
+ case EdgeTypes::Left:
+ theRectLine = inMyRect.leftEdge();
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ // fallthrough intentional
+ case EdgeTypes::Right:
+ theRectLine = inMyRect.rightEdge();
+ break;
+ }
+
+ return eastl::make_pair(theRectLine.toPoint(theEndPoint.m_Interp), theEndPoint.m_EdgeType);
+}
+
+inline bool RectDiffers(const Option<SRect> &inLhs, const SRect &inRhs)
+{
+ if (inLhs.isEmpty())
+ return true;
+ SRect lhs = *inLhs;
+ return CEditorTransitionPath::AreAlmostEqual(lhs.m_TopLeft, inRhs.m_TopLeft) == false
+ || CEditorTransitionPath::AreAlmostEqual(lhs.m_WidthHeight, inRhs.m_WidthHeight) == false;
+}
+}
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ bool SRect::contains(const QT3DSVec2 &inPoint) const
+ {
+ if (inPoint.x >= left() && inPoint.x <= right() && inPoint.y >= top()
+ && inPoint.y <= bottom())
+ return true;
+ return false;
+ }
+
+ void CEditorTransitionPath::SetPathType(TransitionPathTypes::Enum inType)
+ {
+ m_TransitionPathType = inType;
+ if (m_TransitionPathType == TransitionPathTypes::BeginToBegin
+ || m_TransitionPathType == TransitionPathTypes::Targetless)
+ m_ControlPoints.clear();
+ MarkDirty();
+ }
+
+ bool CEditorTransitionPath::UpdateBeginEndRects(const SRect &inStartRect,
+ const SRect &inEndRect)
+ {
+ bool dataChanged = false;
+
+ if (m_BeginRect.hasValue() && m_EndRect.hasValue()) {
+ QT3DSVec2 beginDiff = inStartRect.center() - m_BeginRect->center();
+ QT3DSVec2 endDiff = inEndRect.center() - m_EndRect->center();
+
+ if (AreAlmostEqual(beginDiff, QT3DSVec2(0, 0)) == false
+ && AreAlmostEqual(beginDiff, endDiff, 1)) {
+ dataChanged = m_ControlPoints.empty() == false;
+ // Move all the control points.
+ for (size_t idx = 0, end = m_ControlPoints.size(); idx < end; ++idx) {
+ SControlPoint &thePoint(m_ControlPoints[idx]);
+ QT3DSF32 diffComponent =
+ SControlPoint::GetComponent(beginDiff, thePoint.m_Direction);
+ thePoint.m_Position += diffComponent;
+ }
+ }
+ }
+ bool rectDiffers =
+ RectDiffers(m_BeginRect, inStartRect) || RectDiffers(m_EndRect, inEndRect);
+ m_BeginRect = inStartRect;
+ m_EndRect = inEndRect;
+ if (rectDiffers)
+ MarkDirty();
+ m_TransitionPathType = TransitionPathTypes::BeginToEnd;
+ return dataChanged;
+ }
+
+ bool CEditorTransitionPath::UpdateBeginEndRects(const SRect &inStartRect,
+ TransitionPathTypes::Enum inPathType)
+ {
+ bool dataChanged = m_ControlPoints.empty() == false;
+ QT3DS_ASSERT(inPathType == TransitionPathTypes::BeginToBegin
+ || inPathType == TransitionPathTypes::Targetless);
+ bool rectDiffers = m_EndRect.hasValue() || RectDiffers(m_BeginRect, inStartRect);
+ m_BeginRect = inStartRect;
+ m_EndRect = Empty();
+ if (rectDiffers || dataChanged)
+ MarkDirty();
+ m_TransitionPathType = inPathType;
+ return dataChanged;
+ }
+
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> CEditorTransitionPath::GetBeginPointAndEdge() const
+ {
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint(QT3DSVec2(0, 0),
+ EdgeTypes::UnsetEdgeType);
+ if (m_BeginRect.hasValue()) {
+ QT3DSVec2 center(m_BeginRect->center());
+ center.x += 1;
+ if (m_EndRect.hasValue())
+ center = m_EndRect->center();
+ theStartPoint = GetEndpointPoint(m_Begin, *m_BeginRect, center);
+ }
+ return theStartPoint;
+ }
+
+ eastl::pair<QT3DSVec2, QT3DSVec2> CEditorTransitionPath::GetBeginEndPoints() const
+ {
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint(GetBeginPointAndEdge());
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theEndPoint(QT3DSVec2(0, 0),
+ EdgeTypes::UnsetEdgeType);
+ if (m_EndRect.hasValue())
+ theEndPoint = GetEndpointPoint(m_End, *m_EndRect, m_BeginRect->center());
+
+ return eastl::make_pair(theStartPoint.first, theEndPoint.first);
+ }
+
+ SEndPoint CEditorTransitionPath::CalculateEndPoint(const SRect &inRect,
+ const QT3DSVec2 &inPoint,
+ QT3DSF32 inEdgeBoundary)
+ {
+ if (inRect.width() == 0 || inRect.height() == 0) {
+ QT3DS_ASSERT(false);
+ return SEndPoint();
+ }
+
+ SLine centerToPoint = SLine(inRect.center(), inPoint);
+ SLine leftOrRight;
+ SLine topOrBottom;
+ Option<QT3DSF32> isect;
+ EdgeTypes::Enum theEdge = EdgeTypes::UnsetEdgeType;
+ // If line runs right, test against right edge
+ QT3DSF32 distance = 0;
+ SLine theRectLine;
+ if (centerToPoint.dx() > 0) {
+ theRectLine = inRect.rightEdge();
+ isect = theRectLine.intersect(centerToPoint);
+ // If we are out of range for the right edge
+ if (isect.hasValue() && inBounds(*isect, 0.0f, 1.0f)) {
+ distance = theRectLine.dy();
+ theEdge = EdgeTypes::Right;
+ }
+ } else {
+ theRectLine = inRect.leftEdge();
+ isect = theRectLine.intersect(centerToPoint);
+ if (isect.hasValue() && inBounds(*isect, 0.0f, 1.0f)) {
+ distance = theRectLine.dy();
+ theEdge = EdgeTypes::Left;
+ }
+ }
+ // If we haven't resolved the edge type
+ if (theEdge == EdgeTypes::UnsetEdgeType) {
+ if (centerToPoint.dy() < 0) {
+ theRectLine = inRect.topEdge();
+ isect = theRectLine.intersect(centerToPoint);
+ theEdge = EdgeTypes::Top;
+ distance = theRectLine.dx();
+ } else {
+ theRectLine = inRect.bottomEdge();
+ isect = theRectLine.intersect(centerToPoint);
+ theEdge = EdgeTypes::Bottom;
+ distance = theRectLine.dx();
+ }
+ }
+ // Now drop a perpendicular from the point to the rect line.
+ SLine normalLine(inPoint, inPoint + QT3DSVec2(theRectLine.dy(), -theRectLine.dx()));
+ isect = theRectLine.intersect(normalLine);
+ if (isect.isEmpty()) {
+ theEdge = EdgeTypes::Right;
+ isect = .5f;
+ }
+ SEndPoint retval(theEdge, *isect);
+ QT3DSF32 normalizedBoundary =
+ NVMax(0.0f, inEdgeBoundary / static_cast<float>(fabs(distance)));
+
+ QT3DSF32 edgeLowerBound = NVMax(0.0f, normalizedBoundary);
+ QT3DSF32 edgeUpperBound = NVMin(1.0f, 1.0f - normalizedBoundary);
+ retval.m_Interp = NVMax(edgeLowerBound, retval.m_Interp);
+ retval.m_Interp = NVMin(edgeUpperBound, retval.m_Interp);
+ return retval;
+ }
+
+ // Default end points always are in the middle of the rect edge
+ SEndPoint CEditorTransitionPath::CalculateDefaultEndPoint(const SRect &inRect,
+ const QT3DSVec2 &inPoint)
+ {
+ SEndPoint ep = CalculateEndPoint(inRect, inPoint, 0.0f);
+ return SEndPoint(ep.m_EdgeType, .5f);
+ }
+
+ void CEditorTransitionPath::SetEndPoint(const QT3DSVec2 &inWorldPoint)
+ {
+ QT3DS_ASSERT(m_EndRect.hasValue());
+ m_End = CalculateEndPoint(*m_EndRect, inWorldPoint, m_StateEdgeBuffer);
+ MarkDirty();
+ }
+
+ void CEditorTransitionPath::SetBeginPoint(const QT3DSVec2 &inWorldPoint)
+ {
+ QT3DS_ASSERT(m_BeginRect.hasValue());
+ m_Begin = CalculateEndPoint(*m_BeginRect, inWorldPoint, m_StateEdgeBuffer);
+ MarkDirty();
+ }
+
+ SEndPoint CEditorTransitionPath::GetActualBeginPoint() const
+ {
+ return DoGetActualEndPoint(m_Begin, *m_BeginRect, m_EndRect->center());
+ }
+
+ SEndPoint CEditorTransitionPath::GetActualEndPoint() const
+ {
+ return DoGetActualEndPoint(m_End, *m_EndRect, m_BeginRect->center());
+ }
+
+ void CEditorTransitionPath::SetControlPoint(QT3DSI32 idx, const QT3DSVec2 &inPosition,
+ bool inMoveAdjacentEndPoint)
+ {
+ if (idx < 0 || idx >= (QT3DSI32)m_ControlPoints.size()) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ m_ControlPoints[idx].Set(inPosition);
+ if (inMoveAdjacentEndPoint) {
+ // Move the end points adjacent to the handle.
+ QT3DSI32 possiblePoint = MapControlPointToPossibleControlPoint(idx);
+ size_t numPossible = m_PossibleControlPoints.size();
+ if (possiblePoint == 0)
+ SetBeginPoint(inPosition);
+ if (possiblePoint == ((QT3DSI32)numPossible - 1))
+ SetEndPoint(inPosition);
+ }
+
+ MarkDirty();
+ }
+
+ void CEditorTransitionPath::SetControlPoints(const TPointList &inList)
+ {
+ QT3DS_ASSERT(inList.size() == m_Path.size());
+ for (size_t idx = 0, end = m_ControlPoints.size(); idx < end; ++idx) {
+ SControlPoint &theControlPoint(m_ControlPoints[idx]);
+ if (theControlPoint.m_PathIndex >= 0
+ && theControlPoint.m_PathIndex < (QT3DSI32)inList.size())
+ theControlPoint.Set(inList[theControlPoint.m_PathIndex]);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+
+ TPointList CEditorTransitionPath::GetPath() const
+ {
+ MaybeRegeneratePath();
+ return m_Path;
+ }
+
+ TControlPointList CEditorTransitionPath::GetPossibleControlPoints() const
+ {
+ MaybeRegeneratePath();
+ return m_PossibleControlPoints;
+ }
+
+ SControlPoint CEditorTransitionPath::GetPossibleControlPoint(QT3DSI32 inIdx) const
+ {
+ MaybeRegeneratePath();
+ if (inIdx >= 0 && inIdx < (QT3DSI32)m_PossibleControlPoints.size())
+ return m_PossibleControlPoints[inIdx];
+ QT3DS_ASSERT(false);
+ return SControlPoint();
+ }
+
+ // This may create a new control point thus invalidating the path and possible control
+ // points.
+ QT3DSI32
+ CEditorTransitionPath::MapPossibleControlPointToControlPoint(QT3DSI32 inPossiblePointIndex)
+ {
+ if (inPossiblePointIndex < 0
+ || inPossiblePointIndex >= (QT3DSI32)m_PossibleControlPoints.size()) {
+ QT3DS_ASSERT(false);
+ return -1;
+ }
+ const SControlPoint &thePossiblePoint(m_PossibleControlPoints[inPossiblePointIndex]);
+ TControlPointList::iterator iter = eastl::lower_bound(
+ m_ControlPoints.begin(), m_ControlPoints.end(), thePossiblePoint);
+ QT3DSI32 retval = (QT3DSI32)m_ControlPoints.size();
+ if (iter != m_ControlPoints.end()) {
+ retval = (QT3DSI32)(iter - m_ControlPoints.begin());
+ if (iter->m_PathIndex == thePossiblePoint.m_PathIndex)
+ return retval;
+ }
+
+ m_ControlPoints.insert(iter, thePossiblePoint);
+ return retval;
+ }
+
+ QT3DSI32 CEditorTransitionPath::MapControlPointToPossibleControlPoint(QT3DSI32 inPointIndex)
+ {
+ if (inPointIndex < 0 || inPointIndex >= (QT3DSI32)m_ControlPoints.size()) {
+ QT3DS_ASSERT(false);
+ return -1;
+ }
+ const SControlPoint &theControlPoint(m_ControlPoints[inPointIndex]);
+ TControlPointList::iterator iter = eastl::lower_bound(
+ m_PossibleControlPoints.begin(), m_PossibleControlPoints.end(), theControlPoint);
+ if (iter != m_PossibleControlPoints.end()) {
+ QT3DSI32 retval = (QT3DSI32)(iter - m_PossibleControlPoints.begin());
+ if (iter->m_PathIndex == theControlPoint.m_PathIndex)
+ return retval;
+ }
+ QT3DS_ASSERT(false);
+ return -1;
+ }
+
+ bool CEditorTransitionPath::DoesControlPointExist(QT3DSI32 inPossiblePointIndex) const
+ {
+ if (inPossiblePointIndex < 0
+ || inPossiblePointIndex >= (QT3DSI32)m_PossibleControlPoints.size()) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+ const SControlPoint &thePossiblePoint(m_PossibleControlPoints[inPossiblePointIndex]);
+ TControlPointList::const_iterator iter = eastl::lower_bound(
+ m_ControlPoints.begin(), m_ControlPoints.end(), thePossiblePoint);
+ if (iter != m_ControlPoints.end() && iter->m_PathIndex == thePossiblePoint.m_PathIndex)
+ return true;
+ return false;
+ }
+
+ // Run through the control point list and if you find another control point on the line,
+ // return it's index. Else
+ // bail.
+ // If you are finding the remove algorithm is too specific or hard to use increase the 2.0f
+ // numbers below.
+ inline Option<size_t> NextParallelControlPointOnLine(const SControlPoint &inItem,
+ const SControlPoint &inEndPoint,
+ const TControlPointList &inList,
+ size_t inStartIdx)
+ {
+ for (size_t idx = inStartIdx, end = inList.size(); idx < end; ++idx) {
+ const SControlPoint &theTestPoint(inList[idx]);
+ if (theTestPoint.m_Direction == inItem.m_Direction) {
+ if (CEditorTransitionPath::AreAlmostEqual(inItem.m_Position,
+ theTestPoint.m_Position, 2.0f))
+ return idx + 1;
+ else
+ return Empty();
+ }
+ }
+
+ // Check if beginning and end lie in the same path, or within just a couple pixels.
+ if (inItem.m_Direction == inEndPoint.m_Direction
+ && CEditorTransitionPath::AreAlmostEqual(inItem.m_Position, inEndPoint.m_Position,
+ 2.0f))
+ return inList.size();
+
+ return Empty();
+ }
+
+ // We try to find control point that point the same direction and lie on the same line with
+ // only control points with
+ // orthogonal directions in between. If we find points that fullfill this criteria, we know
+ // we can remove all intermediate
+ // points because the transition path will end up making a straight line.
+ bool CEditorTransitionPath::RemoveRedundantControlPoints()
+ {
+ if (m_ControlPoints.empty())
+ return false;
+
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint = GetBeginPointAndEdge();
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theEndPoint;
+ if (m_EndRect.hasValue())
+ theEndPoint = GetEndpointPoint(m_End, *m_EndRect, m_BeginRect->center());
+ else
+ theEndPoint = theStartPoint;
+ // Find runs of control points in the same line. Remove the points in the middle of the
+ // line.
+ SControlPoint theLastControlPoint(EdgeTypeToDirectionType(theStartPoint.second));
+ theLastControlPoint.Set(theStartPoint.first);
+ SControlPoint theEndControlPoint(EdgeTypeToDirectionType(theEndPoint.second));
+ theEndControlPoint.Set(theEndPoint.first);
+ size_t numControlPoints(m_ControlPoints.size());
+ for (size_t idx = 0, end = numControlPoints; idx < end; ++idx) {
+ Option<size_t> removeEnd = NextParallelControlPointOnLine(
+ theLastControlPoint, theEndControlPoint, m_ControlPoints, idx);
+ if (removeEnd.isEmpty() == false) {
+ size_t lastItem = *removeEnd;
+ m_ControlPoints.erase(m_ControlPoints.begin() + idx,
+ m_ControlPoints.begin() + lastItem);
+ --idx;
+ end = m_ControlPoints.size();
+ } else
+ theLastControlPoint = m_ControlPoints[idx];
+ }
+ if (m_ControlPoints.size() != numControlPoints) {
+ MarkDirty();
+ return true;
+ }
+ return false;
+ }
+
+ void CEditorTransitionPath::RestoreAutoTransition()
+ {
+ m_ControlPoints.clear();
+ m_Begin = SEndPoint();
+ m_End = SEndPoint();
+ MarkDirty();
+ }
+
+ bool CEditorTransitionPath::IsManualMode() const
+ {
+ return m_Begin.m_EdgeType != EdgeTypes::UnsetEdgeType
+ || m_End.m_EdgeType != EdgeTypes::UnsetEdgeType || m_ControlPoints.empty() == false;
+ }
+
+ SPointQueryResult CEditorTransitionPath::Pick(QT3DSVec2 inPoint, QT3DSVec2 inControlBoxDims,
+ QT3DSVec2 inEndBoxDims)
+ {
+ MaybeRegeneratePath();
+
+ if (inEndBoxDims.x && inEndBoxDims.y) {
+ SRect endBox(QT3DSVec2(-1.0f * inEndBoxDims.x / 2, -1.0f * inEndBoxDims.y / 2),
+ inEndBoxDims);
+ SRect testRect(endBox);
+ testRect.translate(m_Path.front());
+ if (testRect.contains(inPoint))
+ return SPointQueryResult(PointQueryResultType::Begin);
+ testRect = SRect(endBox);
+ testRect.translate(m_Path.back());
+ if (testRect.contains(inPoint))
+ return SPointQueryResult(PointQueryResultType::End);
+ }
+ if (inControlBoxDims.x && inControlBoxDims.y) {
+ SRect theControlBox(
+ QT3DSVec2(-1.0f * inControlBoxDims.x / 2.0f, -1.0f * inControlBoxDims.y / 2.0f),
+ inControlBoxDims);
+ for (size_t idx = 0, end = m_PossibleControlPoints.size(); idx < end; ++idx) {
+ const SControlPoint &thePoint(m_PossibleControlPoints[idx]);
+ QT3DSVec2 startPoint = m_Path[thePoint.m_PathIndex];
+ QT3DSVec2 endPoint = m_Path[thePoint.m_PathIndex + 1];
+ // We stretch the rect to contain the entire line, not just where we display the
+ // point.
+ QT3DSVec2 lineDims = endPoint - startPoint;
+ QT3DSF32 lineRectHeight = SControlPoint::GetComponent(theControlBox.m_WidthHeight,
+ thePoint.m_Direction);
+ QT3DSF32 lineRectLength =
+ fabs(SControlPoint::GetOrthogonalComponent(lineDims, thePoint.m_Direction));
+ QT3DSVec2 rectDims = SControlPoint::FromComponentToVector(
+ lineRectHeight, lineRectLength, thePoint.m_Direction);
+ QT3DSVec2 rectTopLeft =
+ QT3DSVec2(NVMin(startPoint.x, endPoint.x), NVMin(startPoint.y, endPoint.y));
+ QT3DSF32 rectComponent =
+ SControlPoint::GetComponent(rectTopLeft, thePoint.m_Direction);
+ rectComponent -= lineRectHeight / 2.0f; // Center the box about the line.
+ rectTopLeft = SControlPoint::SetComponent(rectTopLeft, rectComponent,
+ thePoint.m_Direction);
+ SRect testRect(rectTopLeft, rectDims);
+ if (testRect.contains(inPoint))
+ return SPointQueryResult(PointQueryResultType::Control, (QT3DSI32)idx);
+ }
+ }
+ return PointQueryResultType::NoPoint;
+ }
+
+ SPointQueryResult
+ CEditorTransitionPath::PickClosestControlPoint(QT3DSVec2 inPoint,
+ DirectionTypes::Enum inDirectionType)
+ {
+ MaybeRegeneratePath();
+ QT3DSI32 closestIdx = -1;
+ QT3DSF32 minDistance = QT3DS_MAX_F32;
+
+ for (size_t idx = 0, end = m_PossibleControlPoints.size(); idx < end; ++idx) {
+ const SControlPoint &thePoint(m_PossibleControlPoints[idx]);
+ QT3DSVec2 startPoint = m_Path[thePoint.m_PathIndex];
+ QT3DSVec2 endPoint = m_Path[thePoint.m_PathIndex + 1];
+ SLine theLine(startPoint, endPoint);
+ QT3DSF32 distance = theLine.distance(inPoint);
+
+ if (distance < minDistance && thePoint.m_Direction == inDirectionType) {
+ closestIdx = idx;
+ minDistance = distance;
+ }
+ }
+ if (closestIdx == -1)
+ return SPointQueryResult();
+ return SPointQueryResult(PointQueryResultType::Control, closestIdx);
+ }
+
+ // The output functions are both setup under these assumptions:
+ // 1. The current point does *not* have representation in the possible control points list.
+ // 2. The last point *does* have representation in the possible control points list.
+ // 3. The algorithm should output all path elements up and to but not including the
+ // current point.
+ // So, given straight line do not output any possible points.
+ // Given zig-zag, output two points.
+ SControlPoint
+ CEditorTransitionPath::OutputParallelPoints(const SControlPoint &inLastPoint,
+ const SControlPoint &inCurrentPoint,
+ QT3DSF32 runWidth) const
+ {
+ const SControlPoint &theRunPoint(inCurrentPoint);
+ const SControlPoint theLastControlPoint(inLastPoint);
+ DirectionTypes::Enum runDirection(inLastPoint.m_Direction);
+ if (AreAlmostEqual(theRunPoint.m_Position, theLastControlPoint.m_Position, 1.0)) {
+ // Straigh line. Perhaps remove this point?
+ theRunPoint.m_PathIndex = theLastControlPoint.m_PathIndex;
+ return theRunPoint;
+ } else {
+ // First, output a possible control point inline with inLastPoint
+ SControlPoint possiblePoint(inLastPoint);
+ possiblePoint.m_PathIndex = (QT3DSI32)(m_Path.size() - 1);
+ m_PossibleControlPoints.push_back(possiblePoint);
+ // Output zig-zag, we zig zag from last control point to theRunPoint. We need to
+ // push two points and two control points.
+ QT3DSVec2 startPos(m_Path.back());
+ QT3DSF32 startComponent = SControlPoint::GetComponent(startPos, runDirection);
+ QT3DSF32 orthoStartComponent =
+ SControlPoint::GetOrthogonalComponent(startPos, runDirection);
+ QT3DSF32 endComponent = theRunPoint.m_Position;
+ QT3DSF32 orthoEndComponent = orthoStartComponent + runWidth;
+ QT3DSVec2 endPos = SControlPoint::FromComponentToVector(
+ endComponent, orthoEndComponent, runDirection);
+ QT3DSF32 zigZagOrthoPos = orthoStartComponent + runWidth / 2;
+ QT3DSI32 crossbarIndex = (QT3DSI32)m_Path.size();
+ QT3DSVec2 crossbarStart = SControlPoint::FromComponentToVector(
+ startComponent, zigZagOrthoPos, runDirection);
+ m_Path.push_back(crossbarStart);
+ m_PossibleControlPoints.push_back(
+ SControlPoint(SControlPoint::OppositeDirection(theRunPoint.m_Direction),
+ orthoStartComponent, crossbarIndex));
+ QT3DSVec2 crossbarEnd = SControlPoint::FromComponentToVector(
+ endComponent, zigZagOrthoPos, theRunPoint.m_Direction);
+ m_Path.push_back(crossbarEnd);
+ theRunPoint.m_PathIndex = crossbarIndex + 1;
+ // Do not, however, output the run point. This will happen in the next step.
+ return inCurrentPoint;
+ }
+ }
+
+ // Given right angle, output 1 point.
+ SControlPoint
+ CEditorTransitionPath::OutputOrthogonalPoints(const SControlPoint &inLastPoint,
+ const SControlPoint &inCurrentPoint) const
+ {
+ SLine lastLine = inLastPoint.ToLine();
+ SLine currentLine = inCurrentPoint.ToLine();
+ Option<QT3DSF32> isect = lastLine.intersect(currentLine);
+ QT3DS_ASSERT(isect.hasValue());
+ inLastPoint.m_PathIndex = (QT3DSI32)(m_Path.size() - 1);
+ m_PossibleControlPoints.push_back(inLastPoint);
+ if (isect.hasValue()) {
+ QT3DSVec2 theIsectPoint = lastLine.toPoint(*isect);
+ m_Path.push_back(theIsectPoint);
+ }
+ inCurrentPoint.m_PathIndex = (QT3DSI32)(m_Path.size() - 1);
+ return inCurrentPoint;
+ }
+
+ void CEditorTransitionPath::MaybeRegeneratePath() const
+ {
+ if (IsDirty() == false)
+ return;
+
+ if (m_TransitionPathType == TransitionPathTypes::BeginToEnd) {
+ // Ensure intermediate information is cleared.
+ const_cast<CEditorTransitionPath *>(this)->MarkDirty();
+ // We don't have the begin and end states.
+ if (m_BeginRect.isEmpty() || m_EndRect.isEmpty()) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ // Find the start and end points.
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint =
+ GetEndpointPoint(m_Begin, *m_BeginRect, m_EndRect->center());
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theEndPoint =
+ GetEndpointPoint(m_End, *m_EndRect, m_BeginRect->center());
+ m_Path.push_back(theStartPoint.first);
+ SControlPoint theLastControlPoint(EdgeTypeToDirectionType(theStartPoint.second));
+ theLastControlPoint.Set(theStartPoint.first);
+ theLastControlPoint.m_PathIndex = 0;
+ SControlPoint theEndControlPoint(EdgeTypeToDirectionType(theEndPoint.second));
+ theEndControlPoint.Set(theEndPoint.first);
+ for (size_t idx = 0, end = m_ControlPoints.size(); idx < end; ++idx) {
+ const SControlPoint &thePoint(m_ControlPoints[idx]);
+ if (thePoint.m_Direction == theLastControlPoint.m_Direction) {
+ // zig zag. Requires us to find the first point that *isn't a zig zag in
+ // order to
+ // calculate where the zig zag should be. We could have a section composed
+ // of only
+ // parallel directions and we can't lay then out until we find how much
+ // distance we have
+ // to space each on out.
+ // Image you get to this point and you find you have a set of control points
+ // with vertical direction.
+ // Their positions will tell us how far left each one should sit. But we
+ // don't have enough information
+ // to lay them out without knowing how much vertical space this section
+ // should fill. So we would have to
+ // search forward until we can figure this out.
+ QT3DSVec2 runStart = m_Path.back();
+ // Search forward until either we run out of points or until we find a point
+ // who's direction
+ // does not match the current direction. We call a contiguous set of
+ // control points who all
+ // have the same direction a 'run'.
+ size_t runEnd;
+ size_t zigzagCount = 1;
+ DirectionTypes::Enum runDirection = theLastControlPoint.m_Direction;
+ // Search forward till we find a point that is different.
+ for (runEnd = idx + 1; runEnd < end
+ && m_ControlPoints[runEnd].m_Direction == thePoint.m_Direction;
+ ++runEnd) {
+ // Skip items that are in line. They shouldn't be counted towards our
+ // zigzag count.
+ if (AreAlmostEqual(m_ControlPoints[runEnd].m_Position,
+ m_ControlPoints[runEnd - 1].m_Position)
+ == false)
+ ++zigzagCount;
+ }
+ // Two possible cases. Either we find a control point that has a different
+ // direction in which case we then figure out
+ // how much space we need overall *or* we ran out of control points in which
+ // case we use the end point.
+ QT3DSVec2 runEndPoint(0, 0);
+ if (runEnd == end) {
+ // check if the end point direction is the same. This could be the
+ // final zig zag. Else it will be a righthand turn
+ if (EdgeTypeToDirectionType(theEndPoint.second) == runDirection
+ && AreAlmostEqual(theEndControlPoint.m_Position,
+ thePoint.m_Position)
+ == false)
+ ++zigzagCount;
+
+ runEndPoint = theEndPoint.first;
+ } else {
+ SLine thePointLine(thePoint.ToLine());
+ Option<QT3DSF32> isect =
+ thePointLine.intersect(m_ControlPoints[runEnd].ToLine());
+ if (isect.hasValue())
+ runEndPoint = thePointLine.toPoint(*isect);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ QT3DSF32 runOrthoStart =
+ SControlPoint::GetOrthogonalComponent(runStart, runDirection);
+ QT3DSF32 runOrthoEnd =
+ SControlPoint::GetOrthogonalComponent(runEndPoint, runDirection);
+ QT3DSF32 runRange = runOrthoEnd - runOrthoStart;
+ QT3DSF32 runWidth = runRange / (QT3DSF32)zigzagCount;
+ // Now we iterate through the run itself and output path elements.
+ for (; idx < runEnd; ++idx) {
+ theLastControlPoint = OutputParallelPoints(
+ theLastControlPoint, m_ControlPoints[idx], runWidth);
+ }
+ // Subtract one to account for the loop upate that happens next
+ --idx;
+ } else // right angle
+ {
+ theLastControlPoint =
+ OutputOrthogonalPoints(theLastControlPoint, m_ControlPoints[idx]);
+ }
+ }
+ // Finished iterating through the control points. Now we have the sticky situation
+ // of the very last point
+ // and how it joins with the end point.
+ QT3DSVec2 lastPoint(m_Path.back());
+ if (theEndControlPoint.m_Direction == theLastControlPoint.m_Direction) {
+ QT3DSF32 lastPointOrthoComponent = SControlPoint::GetOrthogonalComponent(
+ lastPoint, theLastControlPoint.m_Direction);
+ QT3DSF32 endOrthoComponent = SControlPoint::GetOrthogonalComponent(
+ theEndPoint.first, theLastControlPoint.m_Direction);
+ QT3DSF32 runWidth = endOrthoComponent - lastPointOrthoComponent;
+ OutputParallelPoints(theLastControlPoint, theEndControlPoint, runWidth);
+ } else {
+ theLastControlPoint =
+ OutputOrthogonalPoints(theLastControlPoint, theEndControlPoint);
+ }
+ // Now output the last possible point which matches the end control point's
+ // direction and such.
+ theEndControlPoint.m_PathIndex = (QT3DSI32)(m_Path.size() - 1);
+#ifdef _DEBUG
+ // The directly previous possible point in the list should not match this point. In
+ // fact, it really should be orthogonal.
+ if (m_PossibleControlPoints.size()) {
+ QT3DS_ASSERT(m_PossibleControlPoints.back().m_Direction
+ != theEndControlPoint.m_Direction
+ || AreAlmostEqual(m_PossibleControlPoints.back().m_Position,
+ theEndControlPoint.m_Position)
+ == false);
+ }
+#endif
+ m_PossibleControlPoints.push_back(theEndControlPoint);
+ // Finally push back the last item.
+ m_Path.push_back(theEndPoint.first);
+ } else if (m_TransitionPathType == TransitionPathTypes::BeginToBegin
+ || m_TransitionPathType == TransitionPathTypes::Targetless) {
+ QT3DSVec2 beginCenter(m_BeginRect->center());
+ beginCenter.x += 1;
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> theStartPoint =
+ GetEndpointPoint(m_Begin, *m_BeginRect, beginCenter);
+ QT3DSVec2 lineDir;
+ m_Path.push_back(theStartPoint.first);
+ if (m_TransitionPathType == TransitionPathTypes::BeginToBegin) {
+ switch (theStartPoint.second) {
+ case EdgeTypes::Top:
+ lineDir = QT3DSVec2(0, -1);
+ break;
+ case EdgeTypes::Bottom:
+ lineDir = QT3DSVec2(0, 1);
+ break;
+ case EdgeTypes::Left:
+ lineDir = QT3DSVec2(-1, 0);
+ break;
+ case EdgeTypes::Right:
+ lineDir = QT3DSVec2(1, 0);
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ QT3DSF32 squareDiagLen = 30;
+ QT3DSF32 halfDiag = squareDiagLen / 2.0f;
+ QT3DSVec2 theOppPoint = theStartPoint.first + lineDir * squareDiagLen;
+ QT3DSVec2 middle = (theStartPoint.first + theOppPoint) / 2.0f;
+ QT3DSVec2 orthoLineDir = QT3DSVec2(lineDir.y, lineDir.x);
+ QT3DSVec2 loopTop = middle + orthoLineDir * halfDiag;
+ QT3DSVec2 loopBottom = middle - orthoLineDir * halfDiag;
+ m_Path.push_back(loopTop);
+ m_Path.push_back(theOppPoint);
+ m_Path.push_back(loopBottom);
+ m_Path.push_back(theStartPoint.first);
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+}
+} \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.h
new file mode 100644
index 00000000..bacf60f2
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorTransitionPath.h
@@ -0,0 +1,467 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_TRANSITION_PATH_H
+#define QT3DS_STATE_EDITOR_TRANSITION_PATH_H
+#pragma once
+#include "Qt3DSState.h"
+#include "foundation/Qt3DSVec2.h"
+#include "EASTL/vector.h"
+#include "foundation/Qt3DSOption.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct EdgeTypes
+ {
+ enum Enum {
+ UnsetEdgeType = 0,
+ Left,
+ Right,
+ Top,
+ Bottom,
+ };
+ };
+
+ struct SEndPoint
+ {
+ // Which edge are we on
+ EdgeTypes::Enum m_EdgeType;
+ // How far from the start of the edge are we.
+ QT3DSF32 m_Interp;
+ SEndPoint(EdgeTypes::Enum inType = EdgeTypes::UnsetEdgeType, QT3DSF32 interp = .5f)
+ : m_EdgeType(inType)
+ , m_Interp(interp)
+ {
+ }
+ };
+
+ struct DirectionTypes
+ {
+ enum Enum {
+ UnknownDirection = 0,
+ Horizontal,
+ Vertical,
+ };
+ };
+
+ struct SLine
+ {
+ QT3DSVec2 m_Begin;
+ QT3DSVec2 m_End;
+
+ SLine(const QT3DSVec2 &beg = QT3DSVec2(0, 0), const QT3DSVec2 &end = QT3DSVec2(0, 0))
+ : m_Begin(beg)
+ , m_End(end)
+ {
+ }
+
+ QT3DSF32 dx() const { return m_End.x - m_Begin.x; }
+ QT3DSF32 dy() const { return m_End.y - m_Begin.y; }
+ QT3DSVec2 begin() const { return m_Begin; }
+ QT3DSVec2 end() const { return m_End; }
+ QT3DSVec2 toPoint(QT3DSF32 interp) { return m_Begin + QT3DSVec2(dx() * interp, dy() * interp); }
+ Option<QT3DSF32> slope() const
+ {
+ QT3DSF32 run = dx();
+ if (fabs(run) > .0001f)
+ return dy() / dx();
+ return Empty();
+ }
+ // If this function returns a value, it returns an interpolation value such that if you
+ // call toPoint on the return value it gives you the intersection point.
+ // http://tog.acm.org/resources/GraphicsGems/gemsiii/insectc.c
+ // Simplifications taken from Qt's line intersect
+ Option<QT3DSF32> intersect(const SLine &other) const
+ {
+ // ipmlementation is based on Graphics Gems III's "Faster Line Segment Intersection"
+ QT3DSVec2 a = m_End - m_Begin;
+ QT3DSVec2 b = other.m_End - other.m_Begin;
+ QT3DSVec2 c = m_Begin - other.m_Begin;
+
+ QT3DSF32 denominator = a.y * b.x - a.x * b.y;
+
+ if (denominator == 0 || fabs(denominator) < .0001f)
+ return Empty();
+
+ QT3DSF32 reciprocal = 1.0f / denominator;
+ return (b.y * c.x - b.x * c.y) * reciprocal;
+ }
+
+ // Caculates the top half of the distance to line equation with the fabs or sqrt.
+ QT3DSF32 distanceNumerator(const QT3DSVec2 &inPoint) const
+ {
+ QT3DSF32 x2 = inPoint.x;
+ QT3DSF32 y2 = inPoint.y;
+
+ QT3DSF32 x1 = m_End.x;
+ QT3DSF32 y1 = m_End.y;
+
+ QT3DSF32 x0 = m_Begin.x;
+ QT3DSF32 y0 = m_Begin.y;
+ return ((x2 - x1) * (y1 - y0)) - ((x1 - x0) * (y2 - y1));
+ }
+
+ QT3DSF32 distance(const QT3DSVec2 &inPoint) const
+ {
+ QT3DSF32 theDx = dx();
+ QT3DSF32 theDy = dy();
+ return fabs(distanceNumerator(inPoint)) / sqrtf(theDx * theDx + theDy * theDy);
+ }
+ };
+
+ // Rect in same coordinate space as QT.
+ struct SRect
+ {
+ QT3DSVec2 m_TopLeft;
+ QT3DSVec2 m_WidthHeight;
+ SRect(const QT3DSVec2 &tl = QT3DSVec2(0, 0), const QT3DSVec2 &wh = QT3DSVec2(0, 0))
+ : m_TopLeft(tl)
+ , m_WidthHeight(wh)
+ {
+ }
+ QT3DSF32 left() const { return m_TopLeft.x; }
+ QT3DSF32 top() const { return m_TopLeft.y; }
+ QT3DSF32 right() const { return m_TopLeft.x + width(); }
+ QT3DSF32 bottom() const { return m_TopLeft.y + height(); }
+ QT3DSF32 width() const { return m_WidthHeight.x; }
+ QT3DSF32 height() const { return m_WidthHeight.y; }
+ QT3DSVec2 topLeft() const { return m_TopLeft; }
+ QT3DSVec2 bottomLeft() const { return QT3DSVec2(left(), bottom()); }
+ QT3DSVec2 bottomRight() const { return QT3DSVec2(right(), bottom()); }
+ QT3DSVec2 topRight() const { return QT3DSVec2(right(), top()); }
+ QT3DSVec2 center() const
+ {
+ return QT3DSVec2(left() + width() / 2.0f, top() + height() / 2.0f);
+ }
+ SLine leftEdge() const { return SLine(topLeft(), bottomLeft()); }
+ SLine rightEdge() const { return SLine(topRight(), bottomRight()); }
+ SLine topEdge() const { return SLine(topLeft(), topRight()); }
+ SLine bottomEdge() const { return SLine(bottomLeft(), bottomRight()); }
+ void translate(QT3DSF32 x, QT3DSF32 y)
+ {
+ m_TopLeft.x += x;
+ m_TopLeft.y += y;
+ }
+ void translate(const QT3DSVec2 &vec)
+ {
+ m_TopLeft.x += vec.x;
+ m_TopLeft.y += vec.y;
+ }
+ bool contains(const QT3DSVec2 &inPoint) const;
+ };
+
+ struct SControlPoint
+ {
+ DirectionTypes::Enum m_Direction;
+ // World space position
+ QT3DSF32 m_Position;
+ // This is a calculated value. Values set will be ignored in favor of recaculating by
+ // re-deriving
+ // the transition path.
+ mutable QT3DSI32 m_PathIndex;
+ SControlPoint(DirectionTypes::Enum inDir = DirectionTypes::UnknownDirection,
+ QT3DSF32 pos = 0.0f, QT3DSI32 idx = -1)
+ : m_Direction(inDir)
+ , m_Position(pos)
+ , m_PathIndex(idx)
+ {
+ }
+ bool operator<(const SControlPoint &inOther) const
+ {
+ return m_PathIndex < inOther.m_PathIndex;
+ }
+ static QT3DSF32 GetComponent(const QT3DSVec2 &inPoint, DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return inPoint.y;
+ break;
+ case DirectionTypes::Vertical:
+ return inPoint.x;
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ return 0;
+ }
+ static DirectionTypes::Enum OppositeDirection(DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return DirectionTypes::Vertical;
+ default:
+ return DirectionTypes::Horizontal;
+ }
+ }
+ static QT3DSF32 GetOrthogonalComponent(const QT3DSVec2 &inPoint, DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return inPoint.x;
+ default:
+ return inPoint.y;
+ }
+ }
+ static QT3DSVec2 FromComponentToVector(QT3DSF32 inComponent, QT3DSF32 orthoComponent,
+ DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return QT3DSVec2(orthoComponent, inComponent);
+ default:
+ return QT3DSVec2(inComponent, orthoComponent);
+ }
+ }
+ static QT3DSVec2 SetComponent(const QT3DSVec2 &inPoint, QT3DSF32 inValue,
+ DirectionTypes::Enum inDir)
+ {
+ switch (inDir) {
+ case DirectionTypes::Horizontal:
+ return QT3DSVec2(inPoint.x, inValue);
+ default:
+ return QT3DSVec2(inValue, inPoint.y);
+ }
+ }
+ void Set(const QT3DSVec2 &inPoint) { m_Position = GetComponent(inPoint, m_Direction); }
+ SLine ToLine() const
+ {
+ switch (m_Direction) {
+ case DirectionTypes::Horizontal:
+ return SLine(QT3DSVec2(0, m_Position), QT3DSVec2(1, m_Position));
+ default:
+ return SLine(QT3DSVec2(m_Position, 0), QT3DSVec2(m_Position, 1));
+ }
+ }
+ };
+
+ typedef eastl::vector<SControlPoint> TControlPointList;
+ typedef eastl::vector<QT3DSVec2> TPointList;
+
+ struct PointQueryResultType
+ {
+ enum Enum {
+ NoPoint = 0,
+ Begin,
+ End,
+ Control,
+ };
+ };
+
+ struct SPointQueryResult
+ {
+ PointQueryResultType::Enum m_QueryType;
+ QT3DSI32 m_Index;
+ SPointQueryResult(
+ PointQueryResultType::Enum inResultType = PointQueryResultType::NoPoint,
+ QT3DSI32 inIndex = -1)
+ : m_QueryType(inResultType)
+ , m_Index(inIndex)
+ {
+ }
+ };
+
+ struct TransitionPathTypes
+ {
+ enum Enum {
+ UnknownPathType = 0,
+ BeginToEnd = 1,
+ BeginToBegin = 2,
+ Targetless = 3,
+ };
+ };
+
+ class CEditorTransitionPath
+ {
+ QT3DSF32 m_StateEdgeBuffer;
+ Option<SRect> m_BeginRect;
+ Option<SRect> m_EndRect;
+ SEndPoint m_Begin;
+ SEndPoint m_End;
+ // Control points that the user has edited.
+ TControlPointList m_ControlPoints;
+ TransitionPathTypes::Enum m_TransitionPathType;
+
+ // ephemeral data, can be derived from start,end points (along with start/end state) and
+ // the
+ // user control points.
+ mutable TPointList m_Path;
+ // The full set of possible control points can be derived from the path.
+ mutable TControlPointList m_PossibleControlPoints;
+
+ public:
+ CEditorTransitionPath(QT3DSF32 inStateEdgeBuffer = 10.0f)
+ : m_StateEdgeBuffer(inStateEdgeBuffer)
+ , m_TransitionPathType(TransitionPathTypes::BeginToEnd)
+ {
+ }
+ Option<SRect> GetBeginRect() const { return m_BeginRect; }
+ void SetBeginRect(const Option<SRect> &inRect)
+ {
+ m_BeginRect = inRect;
+ MarkDirty();
+ }
+
+ Option<SRect> GetEndRect() const { return m_EndRect; }
+ void SetEndRect(const Option<SRect> &inRect)
+ {
+ m_EndRect = inRect;
+ MarkDirty();
+ }
+
+ TransitionPathTypes::Enum GetPathType() const { return m_TransitionPathType; }
+ // May delete all the control points.
+ void SetPathType(TransitionPathTypes::Enum inType);
+ // This may move control points if instart rect and inendrect have shifted by similar
+ // amounts
+ // Returns true if persistent data changed, false otherwise.
+ bool UpdateBeginEndRects(const SRect &inStartRect, const SRect &inEndRect);
+ bool UpdateBeginEndRects(const SRect &inStartRect,
+ TransitionPathTypes::Enum inPathType);
+ eastl::pair<QT3DSVec2, QT3DSVec2> GetBeginEndPoints() const;
+
+ SEndPoint GetEndPoint() const { return m_End; }
+ void SetEndPoint(const SEndPoint &pt)
+ {
+ m_End = pt;
+ MarkDirty();
+ }
+ void SetEndPoint(const QT3DSVec2 &inWorldPoint);
+
+ SEndPoint GetBeginPoint() const { return m_Begin; }
+ void SetBeginPoint(const SEndPoint &pt)
+ {
+ m_Begin = pt;
+ MarkDirty();
+ }
+ void SetBeginPoint(const QT3DSVec2 &inWorldPoint);
+ // Return where the end point will be. Will not return an invalid in point
+ // or an end point where information is not set.
+ SEndPoint GetActualBeginPoint() const;
+ SEndPoint GetActualEndPoint() const;
+
+ TControlPointList GetControlPoints() const { return m_ControlPoints; }
+ SControlPoint GetControlPoint(QT3DSI32 inIndex) const
+ {
+ if (inIndex < (QT3DSI32)m_ControlPoints.size())
+ return m_ControlPoints[inIndex];
+ return SControlPoint();
+ }
+ void SetControlPoints(const TControlPointList &list)
+ {
+ m_ControlPoints = list;
+ MarkDirty();
+ }
+ void SetControlPoint(QT3DSI32 inControlPointIndex, const QT3DSVec2 &inPosition,
+ bool inMoveAdjacentEndPoint);
+
+ // Set the control points by updating the individual items in the path list.
+ // This works if you do not add/remove points from the list.
+ //
+ // list = GetPath
+ // move points, don't add,delete
+ // setControlPoints(list)
+ void SetControlPoints(const TPointList &inList);
+ TPointList GetPath() const;
+ TControlPointList GetPossibleControlPoints() const;
+ SControlPoint GetPossibleControlPoint(QT3DSI32 inIdx) const;
+
+ // This may create a new control point thus invalidating the path and possible control
+ // points.
+ QT3DSI32 MapPossibleControlPointToControlPoint(QT3DSI32 inPossiblePointIndex);
+ QT3DSI32 MapControlPointToPossibleControlPoint(QT3DSI32 inPointIndex);
+ // Returns true if MapPossibleControlPointToControlPoint will *not* create a new point
+ // false otherwise.
+ bool DoesControlPointExist(QT3DSI32 inPossiblePointIndex) const;
+
+ // Returns true if any points were removed.
+ bool RemoveRedundantControlPoints();
+
+ void RestoreAutoTransition();
+
+ bool IsManualMode() const;
+
+ // Query against the dataset to see what got picked. Includes the endoints.
+ // The rects should be centered about the original and just describe the picking
+ // rect to hit against. Empty rects will not get hit.
+ // Note that the result is the index into the possible point list where the hit
+ // happened.
+ // If you intend to manipulate the point, you need to call
+ // MapPossibleControlPointToControlPoint,
+ // Which may add a control point which you then can manipulate.
+ SPointQueryResult Pick(QT3DSVec2 inPoint, QT3DSVec2 inControlBoxDims,
+ QT3DSVec2 inEndBoxDims = QT3DSVec2(0, 0));
+ SPointQueryResult PickClosestControlPoint(QT3DSVec2 inPoint,
+ DirectionTypes::Enum inDirectionType);
+
+ void MarkDirty()
+ {
+ m_Path.clear();
+ m_PossibleControlPoints.clear();
+ for (size_t idx = 0, end = m_ControlPoints.size(); idx < end; ++idx)
+ m_ControlPoints[idx].m_PathIndex = -1;
+ }
+
+ bool IsDirty() const { return m_Path.empty(); }
+
+ static SEndPoint CalculateEndPoint(const SRect &inRect, const QT3DSVec2 &inPoint,
+ QT3DSF32 inEdgeBoundary);
+ static SEndPoint CalculateDefaultEndPoint(const SRect &inRect, const QT3DSVec2 &inPoint);
+ static bool AreAlmostEqual(QT3DSF32 lhs, QT3DSF32 rhs, QT3DSF32 error = .001f)
+ {
+ if (fabs(lhs - rhs) < error)
+ return true;
+ return false;
+ }
+
+ static bool AreAlmostEqual(const QT3DSVec2 &lhs, const QT3DSVec2 &rhs, QT3DSF32 error = .001f)
+ {
+ return AreAlmostEqual(lhs.x, rhs.x, error) && AreAlmostEqual(lhs.y, rhs.y, error);
+ }
+
+ // Regenerates the path if this object is dirty.
+ void MaybeRegeneratePath() const;
+
+ eastl::pair<QT3DSVec2, EdgeTypes::Enum> GetBeginPointAndEdge() const;
+
+ protected:
+ // Output the path between points that have the same direction.
+ SControlPoint OutputParallelPoints(const SControlPoint &inLastPoint,
+ const SControlPoint &inCurrentPoint,
+ QT3DSF32 inRunWidth) const;
+ SControlPoint OutputOrthogonalPoints(const SControlPoint &inLastPoint,
+ const SControlPoint &inCurrentPoint) const;
+ };
+ }
+}
+}
+
+#endif \ No newline at end of file
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorValue.h b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorValue.h
new file mode 100644
index 00000000..d47e6c99
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSStateEditorValue.h
@@ -0,0 +1,271 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef QT3DS_STATE_EDITOR_VALUE_H
+#define QT3DS_STATE_EDITOR_VALUE_H
+#pragma once
+#include "Qt3DSState.h"
+#include "Qt3DSStateEditor.h"
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+#include "foundation/Qt3DSVec3.h"
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ struct ValueTypes
+ {
+ enum Enum {
+ NoEditorValue = 0,
+ String,
+ ObjPtr,
+ ObjPtrList,
+ Vec2,
+ Vec2List,
+ Vec3,
+ Boolean,
+ U32,
+ };
+ };
+
+ template <typename TDataType>
+ struct SValueTypeMap
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::NoEditorValue; }
+ };
+
+ template <>
+ struct SValueTypeMap<TEditorStr>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::String; }
+ };
+ template <>
+ struct SValueTypeMap<TObjPtr>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::ObjPtr; }
+ };
+ template <>
+ struct SValueTypeMap<TObjList>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::ObjPtrList; }
+ };
+ template <>
+ struct SValueTypeMap<QT3DSVec2>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::Vec2; }
+ };
+ template <>
+ struct SValueTypeMap<TVec2List>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::Vec2List; }
+ };
+ template <>
+ struct SValueTypeMap<QT3DSVec3>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::Vec3; }
+ };
+ template <>
+ struct SValueTypeMap<bool>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::Boolean; }
+ };
+ template <>
+ struct SValueTypeMap<QT3DSU32>
+ {
+ static ValueTypes::Enum GetType() { return ValueTypes::U32; }
+ };
+
+ struct SValueUnionTraits
+ {
+ typedef ValueTypes::Enum TIdType;
+ enum {
+ TBufferSize = sizeof(TVec2List),
+ };
+
+ static TIdType getNoDataId() { return ValueTypes::NoEditorValue; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case ValueTypes::String:
+ return inVisitor(*NVUnionCast<TEditorStr *>(inData));
+ case ValueTypes::ObjPtr:
+ return inVisitor(*NVUnionCast<TObjPtr *>(inData));
+ case ValueTypes::ObjPtrList:
+ return inVisitor(*NVUnionCast<TObjList *>(inData));
+ case ValueTypes::Vec2:
+ return inVisitor(*NVUnionCast<QT3DSVec2 *>(inData));
+ case ValueTypes::Vec2List:
+ return inVisitor(*NVUnionCast<TVec2List *>(inData));
+ case ValueTypes::Vec3:
+ return inVisitor(*NVUnionCast<QT3DSVec3 *>(inData));
+ case ValueTypes::Boolean:
+ return inVisitor(*NVUnionCast<bool *>(inData));
+ case ValueTypes::U32:
+ return inVisitor(*NVUnionCast<QT3DSU32 *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case ValueTypes::NoEditorValue:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case ValueTypes::String:
+ return inVisitor(*NVUnionCast<const TEditorStr *>(inData));
+ case ValueTypes::ObjPtr:
+ return inVisitor(*NVUnionCast<const TObjPtr *>(inData));
+ case ValueTypes::ObjPtrList:
+ return inVisitor(*NVUnionCast<const TObjList *>(inData));
+ case ValueTypes::Vec2:
+ return inVisitor(*NVUnionCast<const QT3DSVec2 *>(inData));
+ case ValueTypes::Vec2List:
+ return inVisitor(*NVUnionCast<const TVec2List *>(inData));
+ case ValueTypes::Vec3:
+ return inVisitor(*NVUnionCast<const QT3DSVec3 *>(inData));
+ case ValueTypes::Boolean:
+ return inVisitor(*NVUnionCast<const bool *>(inData));
+ case ValueTypes::U32:
+ return inVisitor(*NVUnionCast<const QT3DSU32 *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case ValueTypes::NoEditorValue:
+ return inVisitor();
+ }
+ }
+ };
+ }
+}
+}
+
+#ifndef _INTEGRITYPLATFORM
+// need some specializations in the original nv foundation namespace
+namespace qt3ds {
+namespace foundation {
+
+ template <>
+ struct DestructTraits<QT3DSVec2>
+ {
+ void destruct(QT3DSVec2 &) {}
+ };
+ template <>
+ struct DestructTraits<QT3DSVec3>
+ {
+ void destruct(QT3DSVec3 &) {}
+ };
+}
+}
+#endif
+
+namespace qt3ds {
+namespace state {
+ namespace editor {
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SValueUnionTraits,
+ SValueUnionTraits::TBufferSize>,
+ SValueUnionTraits::TBufferSize>
+ TEditorUnionType;
+
+ struct SValue : public TEditorUnionType
+ {
+ SValue() {}
+
+ SValue(const SValue &inOther)
+ : TEditorUnionType(static_cast<const TEditorUnionType &>(inOther))
+ {
+ }
+
+ SValue(const char8_t *inOther)
+ : TEditorUnionType(TEditorStr(inOther))
+ {
+ }
+
+ template <typename TDataType>
+ SValue(const TDataType &inDt)
+ : TEditorUnionType(inDt)
+ {
+ }
+
+ SValue &operator=(const SValue &inOther)
+ {
+ TEditorUnionType::operator=(inOther);
+ return *this;
+ }
+
+ bool operator==(const SValue &inOther) const
+ {
+ return TEditorUnionType::operator==(inOther);
+ }
+ bool operator!=(const SValue &inOther) const
+ {
+ return TEditorUnionType::operator!=(inOther);
+ }
+
+ bool empty() const { return getType() == ValueTypes::NoEditorValue; }
+ };
+
+ struct SValueOpt : public Option<SValue>
+ {
+ SValueOpt(const SValueOpt &inOther)
+ : Option<SValue>(inOther)
+ {
+ }
+ SValueOpt(const Empty &)
+ : Option<SValue>()
+ {
+ }
+ SValueOpt() {}
+ template <typename TDataType>
+ SValueOpt(const TDataType &inOther)
+ : Option<SValue>(SValue(inOther))
+ {
+ }
+ SValueOpt &operator=(const SValueOpt &inOther)
+ {
+ Option<SValue>::operator=(inOther);
+ return *this;
+ }
+ };
+ }
+}
+}
+
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.cpp b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.cpp
new file mode 100644
index 00000000..f21fb27b
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.cpp
@@ -0,0 +1,2527 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#include "Qt3DSUIADatamodel.h"
+#include "foundation/IOStreams.h"
+#include "foundation/XML.h"
+#include "foundation/FileTools.h"
+#include "Qt3DSStateEditorFoundation.h"
+#include "Qt3DSStateApplication.h"
+#include "Qt3DSRenderInputStreamFactory.h"
+#include "EASTL/map.h"
+#include "EASTL/sort.h"
+#include "Qt3DSStateEditorTransactionImpl.h"
+#include "Qt3DSUIADatamodelValue.h"
+#include "foundation/StringConversion.h"
+#include "foundation/StringConversionImpl.h"
+#include "Qt3DSDMStringTable.h"
+
+using namespace qt3ds::app;
+using qt3ds::render::IInputStreamFactory;
+
+namespace qt3ds {
+namespace app {
+
+ struct ElementSubTypes
+ {
+ enum Enum {
+ NoSubType = 0,
+ Component = 1,
+ Behavior = 2,
+ };
+ };
+
+ struct SAppElement
+ {
+ TEditorStr m_Path;
+ TEditorStr m_Type;
+ TEditorStr m_Name;
+ TEditorStr m_Id;
+ Q3DStudio::TAttOrArgList m_Attributes;
+ Q3DStudio::TVisualEventList m_VisualEvents;
+ eastl::vector<NVScopedRefCounted<SAppElement>> m_Children;
+ eastl::vector<SDatamodelValue> m_InitialValues;
+
+ private:
+ QT3DSI32 m_RefCount;
+
+ public:
+ SAppElement()
+ : m_RefCount(0)
+ {
+ }
+
+ virtual ~SAppElement() {}
+
+ void addRef() { ++m_RefCount; }
+
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+
+ virtual ElementSubTypes::Enum GetSubType() { return ElementSubTypes::NoSubType; }
+ };
+}
+}
+
+namespace {
+
+typedef eastl::basic_string<char8_t> TStrType;
+struct IPropertyParser
+{
+ virtual ~IPropertyParser() {}
+ virtual Option<TStrType> ParseStr(const char8_t *inName) = 0;
+ virtual Option<QT3DSF32> ParseFloat(const char8_t *inName) = 0;
+ virtual Option<QT3DSVec2> ParseVec2(const char8_t *inName) = 0;
+ virtual Option<QT3DSVec3> ParseVec3(const char8_t *inName) = 0;
+ virtual Option<bool> ParseBool(const char8_t *inName) = 0;
+ virtual Option<QT3DSU32> ParseU32(const char8_t *inName) = 0;
+ virtual Option<QT3DSI32> ParseI32(const char8_t *inName) = 0;
+};
+
+struct SMetaPropertyParser : public IPropertyParser
+{
+ Q3DStudio::IRuntimeMetaData &m_MetaData;
+ IStringTable &m_StringTable;
+ TStrType m_TempStr;
+ qt3ds::foundation::CRegisteredString m_Type;
+ qt3ds::foundation::CRegisteredString m_ClassId;
+
+ SMetaPropertyParser(const char8_t *inType, const char8_t *inClass,
+ Q3DStudio::IRuntimeMetaData &inMeta, IStringTable &stringTable)
+ : m_MetaData(inMeta)
+ , m_StringTable(stringTable)
+ , m_Type(stringTable.RegisterStr(inType))
+ , m_ClassId(stringTable.RegisterStr(inClass))
+ {
+ }
+
+ qt3ds::foundation::CRegisteredString Register(const char8_t *inName)
+ {
+ return m_StringTable.RegisterStr(inName);
+ }
+
+ virtual Option<TStrType> ParseStr(const char8_t *inName)
+ {
+ qt3ds::foundation::CRegisteredString theName(Register(inName));
+ Q3DStudio::ERuntimeDataModelDataType theType(
+ m_MetaData.GetPropertyType(m_Type, theName, m_ClassId));
+ if (theType != Q3DStudio::ERuntimeDataModelDataTypeObjectRef
+ && theType != Q3DStudio::ERuntimeDataModelDataTypeLong4) {
+ return m_MetaData.GetPropertyValueString(m_Type, theName, m_ClassId);
+ }
+ return Empty();
+ }
+ virtual Option<QT3DSF32> ParseFloat(const char8_t *inName)
+ {
+ return m_MetaData.GetPropertyValueFloat(m_Type, Register(inName), m_ClassId);
+ }
+ virtual Option<QT3DSVec2> ParseVec2(const char8_t *inName)
+ {
+ Option<qt3ds::QT3DSVec3> theProperty =
+ m_MetaData.GetPropertyValueVector2(m_Type, Register(inName), m_ClassId);
+ if (theProperty.hasValue()) {
+ return QT3DSVec2(theProperty->x, theProperty->y);
+ }
+ return Empty();
+ }
+ virtual Option<QT3DSVec3> ParseVec3(const char8_t *inName)
+ {
+ Option<qt3ds::QT3DSVec3> theProperty =
+ m_MetaData.GetPropertyValueVector3(m_Type, Register(inName), m_ClassId);
+ if (theProperty.hasValue()) {
+ return *theProperty;
+ }
+ return Empty();
+ }
+ virtual Option<bool> ParseBool(const char8_t *inName)
+ {
+ return m_MetaData.GetPropertyValueBool(m_Type, Register(inName), m_ClassId);
+ }
+
+ virtual Option<QT3DSU32> ParseU32(const char8_t *inName)
+ {
+ Option<long> retval = m_MetaData.GetPropertyValueLong(m_Type, Register(inName), m_ClassId);
+ if (retval.hasValue())
+ return (QT3DSU32)retval.getValue();
+ return Empty();
+ }
+
+ virtual Option<QT3DSI32> ParseI32(const char8_t *inName)
+ {
+ Option<long> retval = m_MetaData.GetPropertyValueLong(m_Type, Register(inName), m_ClassId);
+ if (retval.hasValue())
+ return (QT3DSI32)retval.getValue();
+ return Empty();
+ }
+};
+
+struct SDomReaderPropertyParser : public IPropertyParser
+{
+ IDOMReader &m_Reader;
+ nvvector<char8_t> &m_TempBuf;
+ MemoryBuffer<ForwardingAllocator> &m_ReadBuffer;
+
+ SDomReaderPropertyParser(IDOMReader &reader, nvvector<char8_t> &inTempBuf,
+ MemoryBuffer<ForwardingAllocator> &readBuf)
+ : m_Reader(reader)
+ , m_TempBuf(inTempBuf)
+ , m_ReadBuffer(readBuf)
+ {
+ }
+ virtual Option<TStrType> ParseStr(const char8_t *inName)
+ {
+ const char8_t *retval;
+ if (m_Reader.UnregisteredAtt(inName, retval))
+ return TStrType(retval);
+ return Empty();
+ }
+ virtual Option<QT3DSF32> ParseFloat(const char8_t *inName)
+ {
+ QT3DSF32 retval;
+ if (m_Reader.Att(inName, retval))
+ return retval;
+ return Empty();
+ }
+ virtual Option<QT3DSVec2> ParseVec2(const char8_t *inName)
+ {
+ QT3DSVec2 retval;
+ const char8_t *tempVal;
+
+ if (m_Reader.UnregisteredAtt(inName, tempVal)) {
+ eastl::string tempBuffer(tempVal);
+ Char8TReader theReader(const_cast<char *>(tempBuffer.c_str()), m_ReadBuffer);
+ NVDataRef<QT3DSF32> theDataRef(toDataRef(&retval.x, 2));
+ theReader.ReadRef(theDataRef);
+ return retval;
+ }
+ return Empty();
+ }
+ virtual Option<QT3DSVec3> ParseVec3(const char8_t *inName)
+ {
+ QT3DSVec3 retval;
+ const char8_t *tempVal;
+ if (m_Reader.UnregisteredAtt(inName, tempVal)) {
+ eastl::string tempBuffer(tempVal);
+ Char8TReader theReader(const_cast<char *>(tempBuffer.c_str()), m_ReadBuffer);
+ NVDataRef<QT3DSF32> theDataRef(toDataRef(&retval.x, 3));
+ theReader.ReadRef(theDataRef);
+ return retval;
+ }
+ return Empty();
+ }
+ virtual Option<bool> ParseBool(const char8_t *inName)
+ {
+ bool retval;
+ if (m_Reader.Att(inName, retval))
+ return retval;
+ return Empty();
+ }
+
+ virtual Option<QT3DSU32> ParseU32(const char8_t *inName)
+ {
+ QT3DSU32 retval;
+ if (m_Reader.Att(inName, retval))
+ return retval;
+ return Empty();
+ }
+
+ virtual Option<QT3DSI32> ParseI32(const char8_t *inName)
+ {
+ QT3DSI32 retval;
+ if (m_Reader.Att(inName, retval))
+ return retval;
+ return Empty();
+ }
+};
+
+template <typename TDataType>
+struct SParserHelper
+{
+};
+template <>
+struct SParserHelper<TStrType>
+{
+ static Option<TStrType> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseStr(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSF32>
+{
+ static Option<QT3DSF32> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseFloat(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSVec2>
+{
+ static Option<QT3DSVec2> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseVec2(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSVec3>
+{
+ static Option<QT3DSVec3> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseVec3(inName);
+ }
+};
+template <>
+struct SParserHelper<bool>
+{
+ static Option<bool> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseBool(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSU32>
+{
+ static Option<QT3DSU32> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseU32(inName);
+ }
+};
+template <>
+struct SParserHelper<QT3DSI32>
+{
+ static Option<QT3DSI32> Parse(const char8_t *inName, IPropertyParser &inParser)
+ {
+ return inParser.ParseI32(inName);
+ }
+};
+
+struct SSlideInfo
+{
+ TEditorStr m_Name;
+};
+
+struct SAppComponent : public SAppElement
+{
+ TEditorStrList m_Slides;
+ virtual ElementSubTypes::Enum GetSubType() { return ElementSubTypes::Component; }
+};
+
+struct SAppBehavior : public SAppElement
+{
+ Q3DStudio::THandlerList m_Handlers;
+ virtual ElementSubTypes::Enum GetSubType() { return ElementSubTypes::Behavior; }
+};
+
+struct SVSEditorObject;
+
+struct SVSEditor
+{
+ TFoundationPtr m_Foundation;
+ NVScopedRefCounted<STransactionManagerImpl> m_TransactionManager;
+ NVScopedRefCounted<IStringTable> m_StringTable;
+ SVSEditor(TFoundationPtr fnd, IStringTable &inStringTable)
+ : m_Foundation(fnd)
+ , m_TransactionManager(QT3DS_NEW(m_Foundation->getAllocator(), STransactionManagerImpl)(fnd))
+ , m_StringTable(inStringTable)
+ {
+ }
+
+ virtual void RemoveObjectFromGraph(SVSEditorObject &inObj) = 0;
+ STransaction *GetOpenTransactionImpl()
+ {
+ return m_TransactionManager->GetOpenTransactionImpl();
+ }
+};
+
+struct SVSEditorObject : public IEditorObject
+{
+ TFoundationPtr m_Foundation;
+ SVSEditor &m_Editor;
+ TObjPtr m_ParentObject;
+ TPropertyAccessorList m_PropertyAccessors;
+ QT3DSI32 mRefCount;
+ SVSEditorObject(SVSEditor &editor, TObjPtr parent, const char8_t *typeName,
+ const TPropertyAccessorList &ioList)
+ : IEditorObject(typeName)
+ , m_Foundation(editor.m_Foundation)
+ , m_Editor(editor)
+ , m_ParentObject(parent)
+ , m_PropertyAccessors(ioList)
+ , mRefCount(0)
+ {
+ }
+
+ virtual TEditorStr GetId() { return TEditorStr(); }
+ virtual TEditorStr GetDescription() { return TEditorStr(); }
+ virtual void SetId(const TEditorStr &) { QT3DS_ASSERT(false); }
+ virtual void SetDescription(const TEditorStr &) { QT3DS_ASSERT(false); }
+
+ virtual TObjPtr Parent() { return m_ParentObject; }
+
+ IPropertyAccessor *FindPropertyAccessor(const char8_t *inName)
+ {
+ CRegisteredString nameStr = m_Editor.m_StringTable->RegisterStr(inName);
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx)
+ if (m_PropertyAccessors[idx]->m_Declaration.m_Name == nameStr)
+ return m_PropertyAccessors[idx].mPtr;
+ return NULL;
+ }
+
+ virtual void GetProperties(eastl::vector<SPropertyDeclaration> &outProperties)
+ {
+ outProperties.clear();
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx)
+ outProperties.push_back(m_PropertyAccessors[idx]->m_Declaration);
+ }
+
+ virtual Option<SPropertyDeclaration> FindProperty(const char8_t *propName)
+ {
+ for (size_t idx = 0, end = m_PropertyAccessors.size(); idx < end; ++idx) {
+ if (AreEqual(m_PropertyAccessors[idx]->m_Declaration.m_Name.c_str(), propName))
+ return m_PropertyAccessors[idx]->m_Declaration;
+ }
+ return Empty();
+ }
+
+ virtual eastl::vector<CRegisteredString> GetLegalValues(const char8_t *)
+ {
+ return eastl::vector<CRegisteredString>();
+ }
+
+ virtual Option<SValue> GetPropertyValue(const char8_t *inPropName)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(inPropName);
+ if (accessor) {
+ return accessor->Get(*this);
+ }
+ return Empty();
+ }
+ virtual void SetPropertyValue(const char8_t *inPropName, const SValueOpt &inValue)
+ {
+ IPropertyAccessor *accessor = FindPropertyAccessor(inPropName);
+ if (accessor) {
+ STransaction *theTransaction = m_Editor.GetOpenTransactionImpl();
+ if (theTransaction) {
+ Option<SValue> existing = accessor->Get(*this);
+ theTransaction->m_Changes.push_back(
+ new SChange(existing, inValue, *accessor, *this));
+ }
+ accessor->Set(*this, inValue);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ virtual void RemoveObjectFromGraph() { m_Editor.RemoveObjectFromGraph(*this); }
+ virtual void RemoveIdFromContext() {}
+ virtual void AddIdToContext() {}
+ virtual IEditor &GetEditor() { return m_ParentObject->GetEditor(); }
+};
+
+template <typename TEditorType>
+struct SGenericPropertyStringAccessor : public SPropertyAccessorBase<TEditorType>
+{
+ typedef TEditorStr TDataType;
+ typedef TDataType TEditorType::*TPropertyPtr;
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ TPropertyPtr m_Property;
+
+ SGenericPropertyStringAccessor(TFoundationPtr inAlloc, const SPropertyDeclaration &inDec,
+ TPropertyPtr inPtr)
+ : TBaseType(inAlloc, inDec)
+ , m_Property(inPtr)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TDataType &dtype = inObj.*(this->m_Property);
+ return SValue(dtype);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ TDataType &dtype = inObj.*(this->m_Property);
+ dtype.clear();
+ if (inValue.hasValue() && inValue->getType() == ValueTypes::String)
+ dtype = inValue->getData<TEditorStr>();
+ }
+};
+
+template <typename TEditorType>
+struct SGenericPropertyStringFunctionAccessor : public SPropertyAccessorBase<TEditorType>
+{
+ typedef TEditorStr TDataType;
+ typedef TDataType (TEditorType::*TGetter)() const;
+ typedef void (TEditorType::*TSetter)(const TDataType &);
+ typedef SPropertyAccessorBase<TEditorType> TBaseType;
+
+ TGetter m_Getter;
+ TSetter m_Setter;
+
+ SGenericPropertyStringFunctionAccessor(TFoundationPtr inAlloc,
+ const SPropertyDeclaration &inDec, TGetter inGetter,
+ TSetter inSetter)
+ : TBaseType(inAlloc, inDec)
+ , m_Getter(inGetter)
+ , m_Setter(inSetter)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Allocator, this->mRefCount);
+
+ virtual Option<SValue> DoGet(TEditorType &inObj)
+ {
+ TDataType dtype = (inObj.*m_Getter)();
+ return SValue(dtype);
+ }
+
+ virtual void DoSet(TEditorType &inObj, const Option<SValue> &inValue)
+ {
+ TDataType dtype;
+ dtype.clear();
+ if (inValue.hasValue() && inValue->getType() == ValueTypes::String)
+ dtype = inValue->getData<TEditorStr>();
+
+ (inObj.*m_Setter)(dtype);
+ }
+};
+
+struct SGotoSlideEditor : public SVSEditorObject
+{
+ TEditorStr m_Component;
+ TEditorStr m_Slide;
+ TEditorStr m_Rel;
+ TEditorStr m_Wrap;
+ TEditorStr m_Direction;
+ TEditorStr m_State;
+ TEditorStr m_Mode;
+ TEditorStr m_PlayThroughTo;
+ TEditorStr m_Rate;
+ TEditorStr m_Time;
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SGotoSlideEditor> TStrProp;
+ typedef SGenericPropertyStringFunctionAccessor<SGotoSlideEditor> TStrFunProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("element"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Component));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrFunProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("slide"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::GetSlide, &SGotoSlideEditor::SetSlide));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrFunProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("rel"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::GetRel, &SGotoSlideEditor::SetRel));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("wrap"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Wrap));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("direction"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Direction));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("state"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_State));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("mode"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Mode));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("playthroughto"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_PlayThroughTo));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("rate"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Rate));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("time"),
+ EditorPropertyTypes::String),
+ &SGotoSlideEditor::m_Time));
+ }
+
+ SGotoSlideEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+
+ static const char8_t *ElementName() { return "goto-slide"; }
+
+ void SetSlide(const TEditorStr &inSlide)
+ {
+ if (inSlide.empty() == false) {
+ if (m_Editor.GetOpenTransactionImpl())
+ SetPropertyValue("rel", SValueOpt());
+ else
+ m_Rel.clear();
+ }
+ m_Slide = inSlide;
+ }
+
+ TEditorStr GetSlide() const { return m_Slide; }
+
+ void SetRel(const TEditorStr &inRel)
+ {
+ if (inRel.empty() == false) {
+ if (m_Editor.GetOpenTransactionImpl())
+ SetPropertyValue("slide", SValueOpt());
+ else
+ m_Slide.clear();
+ }
+ m_Rel = inRel;
+ }
+
+ TEditorStr GetRel() const { return m_Rel; }
+};
+
+struct SRunHandlerEditor : public SVSEditorObject
+{
+ TEditorStr m_Behavior;
+ TEditorStr m_Handler;
+ TEditorStr m_ArgumentStr;
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SRunHandlerEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("element"),
+ EditorPropertyTypes::String),
+ &SRunHandlerEditor::m_Behavior));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("handler"),
+ EditorPropertyTypes::String),
+ &SRunHandlerEditor::m_Handler));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("arguments"),
+ EditorPropertyTypes::String),
+ &SRunHandlerEditor::m_ArgumentStr));
+ }
+ SRunHandlerEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "call"; }
+};
+
+struct SSetAttributeEditor : public SVSEditorObject
+{
+ TEditorStr m_Element;
+ TEditorStr m_Attribute;
+ TEditorStr m_Value;
+
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SSetAttributeEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("element"),
+ EditorPropertyTypes::String),
+ &SSetAttributeEditor::m_Element));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("attribute"),
+ EditorPropertyTypes::String),
+ &SSetAttributeEditor::m_Attribute));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("value"),
+ EditorPropertyTypes::String),
+ &SSetAttributeEditor::m_Value));
+ }
+ SSetAttributeEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "set-attribute"; }
+};
+
+struct SFireEventEditor : public SVSEditorObject
+{
+ TEditorStr m_Element;
+ TEditorStr m_Event;
+
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SFireEventEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("element"),
+ EditorPropertyTypes::String),
+ &SFireEventEditor::m_Element));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("event"),
+ EditorPropertyTypes::String),
+ &SFireEventEditor::m_Event));
+ }
+ SFireEventEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "fire-event"; }
+};
+
+struct SSetPresentationEditor : public SVSEditorObject
+{
+ TEditorStr m_Ref;
+ TEditorStr m_Attribute;
+ TEditorStr m_Value;
+
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SSetPresentationEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("ref"),
+ EditorPropertyTypes::String),
+ &SSetPresentationEditor::m_Ref));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation,
+ SPropertyDeclaration(editor.m_StringTable->RegisterStr("attribute"),
+ EditorPropertyTypes::String),
+ &SSetPresentationEditor::m_Attribute));
+
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("value"),
+ EditorPropertyTypes::String),
+ &SSetPresentationEditor::m_Value));
+ }
+ SSetPresentationEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "set-presentation"; }
+};
+
+struct SPlaySoundEditor : public SVSEditorObject
+{
+ TEditorStr m_FilePath;
+
+ static void CreateProperties(TPropertyAccessorList &ioList, SVSEditor &editor)
+ {
+ if (ioList.size())
+ return;
+
+ typedef SGenericPropertyStringAccessor<SPlaySoundEditor> TStrProp;
+ ioList.push_back(QT3DS_NEW(editor.m_Foundation->getAllocator(), TStrProp)(
+ editor.m_Foundation, SPropertyDeclaration(editor.m_StringTable->RegisterStr("file"),
+ EditorPropertyTypes::String),
+ &SPlaySoundEditor::m_FilePath));
+ }
+ SPlaySoundEditor(SVSEditor &editor, TObjPtr parent, const TPropertyAccessorList &ioList)
+ : SVSEditorObject(editor, parent, ElementName(), ioList)
+ {
+ }
+ QT3DS_STATE_IMPLEMENT_REF_COUNT_ADDREF_RELEASE(this->m_Foundation, this->mRefCount);
+ static const char8_t *ElementName() { return "play-sound"; }
+};
+
+struct SVSEntry
+{
+ QT3DSI32 mRefCount;
+ InterpreterEventTypes::Enum m_EventType;
+ TObjList m_Editors;
+
+ SVSEntry(InterpreterEventTypes::Enum evtType = InterpreterEventTypes::UnknownInterpreterEvent)
+ : mRefCount(0)
+ , m_EventType(evtType)
+ {
+ }
+ void addRef() { ++mRefCount; }
+ void release()
+ {
+ --mRefCount;
+ if (mRefCount <= 0)
+ delete this;
+ }
+};
+
+struct SVSEntryListChange : public IChange
+{
+ TObjPtr m_Object;
+ NVScopedRefCounted<SVSEntry> m_Entry;
+ QT3DSI32 m_Index;
+ bool m_AddOnDo;
+ TObjPtr m_EditorObj;
+ QT3DSI32 m_RefCount;
+
+ SVSEntryListChange(TObjPtr inObj, TObjPtr inPositionObj, SVSEntry &entry, bool addOnDo,
+ TObjPtr inEditorObj)
+ : m_Object(inObj)
+ , m_Entry(entry)
+ , m_AddOnDo(addOnDo)
+ , m_EditorObj(inEditorObj)
+ , m_RefCount(0)
+ {
+ TObjList::iterator iter =
+ eastl::find(entry.m_Editors.begin(), entry.m_Editors.end(), inPositionObj);
+ m_Index = iter - entry.m_Editors.begin();
+ }
+ void addRef() { ++m_RefCount; }
+ void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+ void add() { m_Entry->m_Editors.insert(m_Entry->m_Editors.begin() + m_Index, m_Object); }
+ void remove()
+ {
+ TObjList::iterator iter =
+ eastl::find(m_Entry->m_Editors.begin(), m_Entry->m_Editors.end(), m_Object);
+ if (iter != m_Entry->m_Editors.end())
+ m_Entry->m_Editors.erase(iter);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ virtual void Do()
+ {
+ if (m_AddOnDo)
+ add();
+ else
+ remove();
+ }
+ virtual void Undo()
+ {
+ if (m_AddOnDo)
+ remove();
+ else
+ add();
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObj; }
+};
+
+typedef eastl::pair<NVScopedRefCounted<SVSEntry>, NVScopedRefCounted<SVSEntry>> TEntryExitPair;
+typedef eastl::map<TEditorStr, TEntryExitPair> TIdEntryExitMap;
+typedef eastl::map<TEditorStr, TIdEntryExitMap> TIdStateMapMap;
+
+struct SIdEntryExitDeleteChange : public IChange
+{
+ TIdEntryExitMap &m_Map;
+ TEditorStr m_Id;
+ TEntryExitPair m_Data;
+ QT3DSI32 m_RefCount;
+ TObjPtr m_EditorObject;
+ bool m_RemoveOnDo;
+ SIdEntryExitDeleteChange(TIdEntryExitMap &inMap, const TEditorStr &inId, TObjPtr inObj,
+ bool removeOnDo = true)
+ : m_Map(inMap)
+ , m_Id(inId)
+ , m_RefCount(0)
+ , m_EditorObject(inObj)
+ , m_RemoveOnDo(removeOnDo)
+ {
+ m_Data = m_Map[m_Id];
+ }
+ virtual void addRef() { ++m_RefCount; }
+ virtual void release()
+ {
+ --m_RefCount;
+ if (m_RefCount <= 0)
+ delete this;
+ }
+ void remove()
+ {
+ TIdEntryExitMap::iterator iter = m_Map.find(m_Id);
+ if (iter != m_Map.end())
+ m_Map.erase(iter);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ void insert() { m_Map[m_Id] = m_Data; }
+ virtual void Do()
+ {
+ if (m_RemoveOnDo)
+ remove();
+ else
+ insert();
+ }
+ virtual void Undo()
+ {
+ if (m_RemoveOnDo)
+ insert();
+ else
+ remove();
+ }
+ virtual TObjPtr GetEditor() { return m_EditorObject; }
+};
+
+struct SEditorEntry
+{
+ TEditorPtr m_Editor;
+ TEditorStr m_Id;
+ TEditorStr m_FilePath;
+};
+
+template <typename TDataType>
+SDatamodelValue ParseInitialProperty(const char8_t *inName, IPropertyParser &metaParser,
+ IPropertyParser &runtimeParser)
+{
+ Option<TDataType> val = SParserHelper<TDataType>::Parse(inName, runtimeParser);
+ if (val.isEmpty())
+ val = SParserHelper<TDataType>::Parse(inName, metaParser);
+ if (val.hasValue())
+ return SDatamodelValue(*val);
+ return SDatamodelValue();
+}
+
+template <typename TDataType>
+SDatamodelValue ParseSlideProperty(const char8_t *inName, IPropertyParser &runtimeParser)
+{
+ Option<TDataType> val = SParserHelper<TDataType>::Parse(inName, runtimeParser);
+ if (val.hasValue())
+ return *val;
+ return SDatamodelValue();
+}
+
+struct DatamodelImpl : public IDatamodel,
+ public ITransManagerImplListener,
+ public SVSEditor,
+ public IEditorCopyPasteListener
+{
+ TEditorStr m_FilePath;
+ QT3DSI32 mRefCount;
+ NVScopedRefCounted<IInputStreamFactory> m_InputStreamFactory;
+ eastl::pair<NVScopedRefCounted<IDOMWriter>, NVScopedRefCounted<IDOMReader>> m_UIADocument;
+ nvvector<SNamespacePair> m_UIANamespaces;
+ bool m_Dirty;
+ nvvector<NVScopedRefCounted<SAppElement>> m_Elements;
+ NVScopedRefCounted<SAppElement> m_FinderElement;
+ nvvector<SEditorEntry> m_Editors;
+ TIdStateMapMap m_IdToStateMaps;
+ eastl::vector<SPresentation> m_Presentations;
+ nvvector<char8_t> m_ParseBuf;
+ Q3DStudio::TAttOrArgList m_SlideProperties;
+ eastl::string m_LoadingErrorString;
+ IStateMachineEditorManager &m_StateMachineEditorManager;
+
+ DatamodelImpl(TFoundationPtr inFoundation, const TEditorStr &inPath, const TEditorStr &inAppDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IStringTable &inStringTable)
+ : SVSEditor(inFoundation, inStringTable)
+ , m_FilePath(inPath)
+ , mRefCount(0)
+ , m_InputStreamFactory(IInputStreamFactory::Create(m_Foundation->getFoundation()))
+ , m_UIANamespaces(m_Foundation->getAllocator(), "m_UIANamespaces")
+ , m_Dirty(false)
+ , m_Elements(m_Foundation->getAllocator(), "m_Elements")
+ , m_Editors(m_Foundation->getAllocator(), "m_Editors")
+ , m_ParseBuf(inFoundation->getAllocator(), "tempbuf")
+ , m_StateMachineEditorManager(inStateMachineEditorManager)
+ {
+ TEditorStr ext;
+ CFileTools::GetExtension(m_FilePath.c_str(), ext);
+ if (ext.comparei("uia") != 0) {
+ m_FilePath = IApplication::GetLaunchFile(inPath.c_str());
+ }
+ // Check extension, if it isn't what we expect then just set it to uia
+ ext.clear();
+ CFileTools::GetExtension(m_FilePath.c_str(), ext);
+ if (ext.comparei("uia") != 0)
+ CFileTools::SetExtension(m_FilePath, "uia");
+
+ m_InputStreamFactory->AddSearchDirectory(inAppDir.c_str());
+
+ m_TransactionManager->m_ObjListener = this;
+ }
+
+ ~DatamodelImpl() {}
+
+ void addRef() { atomicIncrement(&mRefCount); }
+
+ void release()
+ {
+ TFoundationPtr tempFoundation(m_Foundation);
+ atomicDecrement(&mRefCount);
+ if (mRefCount <= 0) {
+ NVDelete(m_Foundation->getAllocator(), this);
+ }
+ }
+ typedef eastl::map<TEditorStr, Q3DStudio::THandlerList> TIdHandlerMap;
+ typedef eastl::map<TEditorStr, Q3DStudio::TVisualEventList> TIdVisualEventMap;
+ typedef eastl::map<TEditorStr, NVScopedRefCounted<SAppElement>> TIdElemMap;
+
+ SAppElement &ParseElement(IDOMReader &inReader, Q3DStudio::IRuntimeMetaData &inMetaData,
+ eastl::vector<NVScopedRefCounted<SAppElement>> &ioChildList,
+ TIdElemMap &ioElemMap)
+ {
+ IDOMReader::Scope __elemScope(inReader);
+ const char8_t *className = inReader.GetElementName();
+ const char8_t *classRef;
+ inReader.UnregisteredAtt("class", classRef);
+ const char8_t *id;
+ inReader.UnregisteredAtt("id", id);
+ Q3DStudio::SElementInfo elemInfo = inMetaData.LoadElement(className, classRef, id);
+ NVScopedRefCounted<SAppElement> newElem;
+ if (elemInfo.m_ClassName.compare("Behavior") == 0) {
+ SAppBehavior *behavior = new SAppBehavior();
+ behavior->m_Handlers = inMetaData.GetCustomHandlers(id);
+ newElem = behavior;
+ } else if (elemInfo.m_IsComponent) {
+ SAppComponent *component = new SAppComponent();
+ newElem = component;
+ } else {
+ newElem = new SAppElement();
+ }
+ newElem->m_Attributes = elemInfo.m_Attributes;
+ newElem->m_VisualEvents = inMetaData.GetVisualEvents(id);
+ newElem->m_Type = elemInfo.m_ClassName;
+ newElem->m_Id = id;
+ const char8_t *elemName;
+ if (inReader.UnregisteredAtt("name", elemName))
+ newElem->m_Name.assign(elemName);
+ else {
+ Q3DStudio::TRuntimeMetaDataStrType str = inMetaData.GetPropertyValueString(
+ inMetaData.Register(className), inMetaData.Register("name"),
+ inMetaData.Register(id));
+ newElem->m_Name = str;
+ }
+
+ ioChildList.push_back(newElem);
+ m_Elements.push_back(newElem);
+ ioElemMap.insert(eastl::make_pair(TEditorStr(id), newElem));
+ {
+ SMetaPropertyParser theMetaParser(newElem->m_Type.c_str(), classRef, inMetaData,
+ inMetaData.GetStringTable()->GetRenderStringTable());
+ SDomReaderPropertyParser theDOMParser(inReader, m_ParseBuf, inReader.m_TempBuf);
+ for (size_t idx = 0, end = newElem->m_Attributes.size(); idx < end; ++idx) {
+ const SAttOrArg &theAtt(newElem->m_Attributes[idx]);
+ SDatamodelValue newValue;
+ switch (theAtt.m_DataType) {
+ case ERuntimeDataModelDataTypeFloat:
+ newValue = ParseInitialProperty<float>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeFloat2:
+ newValue = ParseInitialProperty<QT3DSVec2>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeFloat3:
+ newValue = ParseInitialProperty<QT3DSVec3>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeLong:
+ newValue = ParseInitialProperty<QT3DSI32>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeString:
+ newValue = ParseInitialProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theMetaParser, theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeBool:
+ newValue = ParseInitialProperty<bool>(theAtt.m_Name.c_str(), theMetaParser,
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeStringRef:
+ newValue = ParseInitialProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theMetaParser, theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeObjectRef:
+ newValue =
+ ParseSlideProperty<eastl::string>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ default:
+ break;
+ }
+ newElem->m_InitialValues.push_back(newValue);
+ }
+ }
+ for (bool success = inReader.MoveToFirstChild(); success;
+ success = inReader.MoveToNextSibling())
+ ParseElement(inReader, inMetaData, newElem->m_Children, ioElemMap);
+
+ return *newElem;
+ }
+
+ // Get the item names if not overridden
+ // Pull the names off so we can construct the item path.
+ void LoadSlide(IDOMReader &inReader, TIdElemMap &inMap)
+ {
+ IDOMReader::Scope __topScope(inReader);
+ for (bool commandSuccess = inReader.MoveToFirstChild(); commandSuccess;
+ commandSuccess = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __commandScope(inReader);
+ const char8_t *elemName = inReader.GetElementName();
+ if (AreEqual(elemName, "Set") || AreEqual(elemName, "Add")) {
+ const char8_t *name;
+ TIdElemMap::iterator iter = inMap.end();
+ const char8_t *itemRef;
+ inReader.UnregisteredAtt("ref", itemRef);
+ if (!isTrivial(itemRef)) {
+ if (itemRef[0] == '#')
+ ++itemRef;
+ iter = inMap.find(TEditorStr(itemRef));
+ }
+
+ if (inReader.UnregisteredAtt("name", name)) {
+ if (iter != inMap.end()) {
+ iter->second->m_Name.assign(name);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ if (iter != inMap.end() && AreEqual(elemName, "Add")) {
+ nvvector<char8_t> theTempBuf(this->m_Foundation->getAllocator(), "tempbuf");
+ SDomReaderPropertyParser theDOMParser(inReader, m_ParseBuf, inReader.m_TempBuf);
+ SAppElement *newElem = iter->second.mPtr;
+ for (size_t idx = 0, end = newElem->m_Attributes.size(); idx < end; ++idx) {
+ const SAttOrArg &theAtt(newElem->m_Attributes[idx]);
+ SDatamodelValue newValue;
+ switch (theAtt.m_DataType) {
+ case ERuntimeDataModelDataTypeFloat:
+ newValue =
+ ParseSlideProperty<float>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeFloat2:
+ newValue =
+ ParseSlideProperty<QT3DSVec2>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeFloat3:
+ newValue =
+ ParseSlideProperty<QT3DSVec3>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeLong:
+ newValue =
+ ParseSlideProperty<QT3DSI32>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeString:
+ newValue = ParseSlideProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeBool:
+ newValue =
+ ParseSlideProperty<bool>(theAtt.m_Name.c_str(), theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeStringRef:
+ newValue = ParseSlideProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theDOMParser);
+ break;
+ case ERuntimeDataModelDataTypeObjectRef:
+ newValue = ParseSlideProperty<eastl::string>(theAtt.m_Name.c_str(),
+ theDOMParser);
+ break;
+ default:
+ break;
+ }
+ if (newValue.getType() != ERuntimeDataModelDataTypeNone)
+ newElem->m_InitialValues[idx] = newValue;
+ }
+ }
+ }
+ }
+ }
+
+ void ResolveElemPath(SAppElement &inElem, const eastl::string &parentId)
+ {
+ inElem.m_Path = parentId;
+ if (inElem.m_Path.back() != ':')
+ inElem.m_Path.append(1, '.');
+ inElem.m_Path.append(inElem.m_Name);
+ for (size_t idx = 0, end = inElem.m_Children.size(); idx < end; ++idx)
+ ResolveElemPath(*inElem.m_Children[idx], inElem.m_Path);
+ }
+
+ void LoadUIPFile(const char *inRelativePath, const char *inUIPId,
+ Q3DStudio::IRuntimeMetaData &inMetaData)
+ {
+ eastl::string appDir(m_FilePath);
+ CFileTools::GetDirectory(appDir);
+ eastl::string uipPath;
+ CFileTools::CombineBaseAndRelative(appDir.c_str(), inRelativePath, uipPath);
+ CFileSeekableIOStream uipStream(uipPath.c_str(), FileReadFlags());
+ if (!uipStream.IsOpen()) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_Foundation->getAllocator(), m_StringTable));
+ SDOMElement *domElem = CDOMSerializer::Read(*theFactory, uipStream).second;
+ if (!domElem) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ NVScopedRefCounted<IDOMReader> theReader(IDOMReader::CreateDOMReader(
+ m_Foundation->getAllocator(), *domElem, m_StringTable, theFactory));
+ if (!theReader->MoveToFirstChild("Project")) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ Q3DStudio::IRuntimeMetaData &theMetaData(inMetaData);
+ theMetaData.ClearPerProjectData();
+
+ TEditorStr rootPath(inUIPId);
+ rootPath.append(1, ':');
+ eastl::vector<NVScopedRefCounted<SAppElement>> topElements;
+ TIdElemMap idElemMap;
+ m_Presentations.push_back(SPresentation());
+ SPresentation &currentPresentation(m_Presentations.back());
+ currentPresentation.m_Id.assign(inUIPId);
+ currentPresentation.m_SrcPath.assign(inRelativePath);
+ {
+ IDOMReader::Scope __projectScope(*theReader);
+ if (theReader->MoveToFirstChild("ProjectSettings")) {
+ const char8_t *temp;
+ theReader->UnregisteredAtt("author", temp);
+ currentPresentation.m_Author.assign(nonNull(temp));
+ theReader->UnregisteredAtt("company", temp);
+ currentPresentation.m_Company.assign(nonNull(temp));
+ theReader->Att("presentationWidth", currentPresentation.m_Width);
+ theReader->Att("presentationHeight", currentPresentation.m_Height);
+ }
+ // Presentation width and height as specified in the file.
+ }
+ // Classes/handlers
+ {
+ IDOMReader::Scope __projectScope(*theReader);
+ if (theReader->MoveToFirstChild("Classes")) {
+ // Load each class system into the meta data and then pull back the handlers
+ for (bool success = theReader->MoveToFirstChild(); success;
+ success = theReader->MoveToNextSibling()) {
+ const char8_t *id, *srcPath, *name;
+ theReader->UnregisteredAtt("id", id);
+ theReader->UnregisteredAtt("sourcepath", srcPath);
+ theReader->UnregisteredAtt("name", name);
+ eastl::string classItemPath;
+ CFileTools::CombineBaseAndRelative(appDir.c_str(), srcPath, classItemPath);
+
+ if (AreEqual(theReader->GetElementName().c_str(), "Behavior")) {
+ bool theScriptFile =
+ theMetaData.LoadScriptFile("Behavior", id, name, classItemPath.c_str());
+ QT3DS_ASSERT(theScriptFile);
+ (void)theScriptFile;
+ } else if (AreEqual(theReader->GetElementName().c_str(), "Effect")) {
+ bool theEffectFile = theMetaData.LoadEffectXMLFile("Effect", id, name,
+ classItemPath.c_str());
+ QT3DS_ASSERT(theEffectFile);
+ (void)theEffectFile;
+ } else if (AreEqual(theReader->GetElementName().c_str(), "RenderPlugin")) {
+ theMetaData.LoadPluginXMLFile("RenderPlugin", id, name,
+ classItemPath.c_str());
+ } else if (AreEqual(theReader->GetElementName().c_str(), "CustomMaterial")) {
+ theMetaData.LoadMaterialXMLFile("CustomMaterial", id, name,
+ classItemPath.c_str());
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+ }
+
+ // Graph
+ {
+ IDOMReader::Scope __projectScope(*theReader);
+ if (theReader->MoveToFirstChild("Graph")) {
+ for (bool success = theReader->MoveToFirstChild(); success;
+ success = theReader->MoveToNextSibling()) {
+ currentPresentation.m_Scene =
+ &ParseElement(*theReader, theMetaData, topElements, idElemMap);
+ }
+ }
+ }
+ // States/Slides
+ {
+ // This is where the name *may* be set,
+ IDOMReader::Scope __projectScope(*theReader);
+ if (theReader->MoveToFirstChild("Logic")) {
+ // Slides are just slightly hierarchical, master and then nonmaster children.
+ for (bool success = theReader->MoveToFirstChild(); success;
+ success = theReader->MoveToNextSibling()) {
+ IDOMReader::Scope __masterScope(*theReader);
+ LoadSlide(*theReader, idElemMap);
+ const char8_t *component;
+ theReader->UnregisteredAtt("component", component);
+ NVScopedRefCounted<SAppComponent> theComponent;
+ if (!isTrivial(component)) {
+ if (component[0] == '#')
+ ++component;
+ TIdElemMap::iterator iter = idElemMap.find(TEditorStr(component));
+ if (iter != idElemMap.end()) {
+ if (iter->second->GetSubType() == ElementSubTypes::Component)
+ theComponent = static_cast<SAppComponent *>(iter->second.mPtr);
+ else {
+ QT3DS_ASSERT(false);
+ }
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+
+ for (bool childSuccess = theReader->MoveToFirstChild("State"); childSuccess;
+ childSuccess = theReader->MoveToNextSibling("State")) {
+ IDOMReader::Scope __slideScope(*theReader);
+ const char8_t *slideName;
+ if (theReader->UnregisteredAtt("name", slideName))
+ theComponent->m_Slides.push_back(TEditorStr(slideName));
+ LoadSlide(*theReader, idElemMap);
+ }
+ }
+ }
+ }
+ // Now resolve all the names to create full paths.
+ for (size_t idx = 0, end = topElements.size(); idx < end; ++idx) {
+ ResolveElemPath(*topElements[idx], rootPath);
+ }
+ }
+
+ struct SElemLessThan
+ {
+ bool operator()(NVScopedRefCounted<SAppElement> lhs,
+ NVScopedRefCounted<SAppElement> rhs) const
+ {
+ return lhs->m_Path < rhs->m_Path;
+ }
+ };
+
+ static const char *GetNamespace() { return "http://qt.io/qt3dstudio/uia"; }
+ static const char *GetOldNamespace() { return "http://qt.io/qt3dstudio/uicomposer"; }
+
+ void LoadUIADatabase()
+ {
+ CFileSeekableIOStream theStream(m_FilePath.c_str(), FileReadFlags());
+ if (theStream.IsOpen()) {
+ LoadUIADatabaseFromStream(theStream);
+ }
+ }
+
+ struct SXmlErrorHandler : public CXmlErrorHandler
+ {
+ SXmlErrorHandler(const eastl::string &inFilePath, eastl::string &inErrorString)
+ : m_FilePath(inFilePath)
+ , m_ErrorString(inErrorString)
+ {
+ }
+ virtual ~SXmlErrorHandler() {}
+ virtual void OnXmlError(TXMLCharPtr errorName, int line, int column)
+ {
+ if (m_ErrorString.empty())
+ m_ErrorString.sprintf("%s(%d, %d): %s", m_FilePath.c_str(), line, column,
+ errorName);
+ }
+ eastl::string m_FilePath;
+ eastl::string &m_ErrorString;
+ };
+
+ void LoadUIADatabaseFromStream(IInStream &inStream)
+ {
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_Foundation->getAllocator(), m_StringTable));
+ m_UIADocument.first = NULL;
+ m_UIADocument.second = NULL;
+ m_UIANamespaces.clear();
+ m_Elements.clear();
+ m_IdToStateMaps.clear();
+ m_Editors.clear();
+ m_Presentations.clear();
+ m_SlideProperties.clear();
+ m_LoadingErrorString.clear();
+ eastl::string appDir(m_FilePath);
+ CFileTools::GetDirectory(appDir);
+
+ SXmlErrorHandler theXmlErrorWriter(m_FilePath, m_LoadingErrorString);
+ eastl::pair<SNamespacePairNode *, SDOMElement *> parseResult =
+ CDOMSerializer::Read(*theFactory, inStream, &theXmlErrorWriter);
+ if (parseResult.second != NULL) {
+ qt3ds::foundation::CRegisteredString theRegisteredOldNamespace =
+ m_StringTable->RegisterStr(GetOldNamespace());
+ qt3ds::foundation::CRegisteredString theRegisteredNamespace =
+ m_StringTable->RegisterStr(GetNamespace());
+
+ ReplaceDOMNamespace(*parseResult.second, theRegisteredOldNamespace,
+ theRegisteredNamespace);
+
+ m_UIADocument =
+ IDOMWriter::CreateDOMWriter(theFactory, *parseResult.second, m_StringTable);
+ for (SNamespacePairNode *nodePtr = parseResult.first; nodePtr;
+ nodePtr = nodePtr->m_NextNode) {
+ if (nodePtr->m_Namespace == theRegisteredOldNamespace)
+ nodePtr->m_Namespace = theRegisteredNamespace;
+ m_UIANamespaces.push_back(*nodePtr);
+ }
+ }
+
+ if (m_UIADocument.first.mPtr == NULL) {
+ m_UIADocument = IDOMWriter::CreateDOMWriter(m_Foundation->getAllocator(), "application",
+ m_StringTable, GetNamespace());
+ m_Dirty = true;
+ }
+ if (m_UIANamespaces.empty())
+ m_UIANamespaces.push_back(
+ SNamespacePair(m_StringTable->RegisterStr(GetNamespace())));
+
+ {
+ NVScopedReleasable<Q3DStudio::IRuntimeMetaData> theMetaData(
+ Q3DStudio::IRuntimeMetaData::Create(*m_InputStreamFactory));
+
+ if (theMetaData) {
+ m_SlideProperties = theMetaData->GetSlideAttributes();
+
+ eastl::vector<eastl::pair<TEditorStr, TEditorStr>> machinePathsAndIds;
+ IDOMReader::Scope __appScope(m_UIADocument.second);
+
+ if (m_UIADocument.second->MoveToFirstChild("assets")) {
+ IDOMReader::Scope __assetsScope(m_UIADocument.second);
+ for (bool success = m_UIADocument.second->MoveToFirstChild(); success;
+ success = m_UIADocument.second->MoveToNextSibling()) {
+ IDOMReader::Scope __assetScope(m_UIADocument.second);
+ const char8_t *elemName = m_UIADocument.second->GetElementName();
+ if (AreEqual(elemName, "presentation")) {
+ const char8_t *id, *relativePath;
+ m_UIADocument.second->UnregisteredAtt("id", id);
+ m_UIADocument.second->UnregisteredAtt("src", relativePath);
+ LoadUIPFile(relativePath, id, *theMetaData);
+ } else if (AreEqual(elemName, "statemachine")) {
+ const char8_t *id, *relativePath;
+ m_UIADocument.second->UnregisteredAtt("id", id);
+ m_UIADocument.second->UnregisteredAtt("src", relativePath);
+ TEditorStr fullPath;
+ CFileTools::CombineBaseAndRelative(appDir.c_str(), relativePath,
+ fullPath);
+ CreateSCXMLEditor(fullPath, nonNull(id));
+ }
+ }
+ }
+
+ // Now sort our list of elements by path after we have loaded all uip files.
+ eastl::sort(m_Elements.begin(), m_Elements.end(), SElemLessThan());
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ {
+ IDOMReader::Scope __appScope(m_UIADocument.second);
+ for (bool success = m_UIADocument.second->MoveToFirstChild("statemachine"); success;
+ success = m_UIADocument.second->MoveToNextSibling("statemachine")) {
+ IDOMReader::Scope __machineScope(m_UIADocument.second);
+ const char8_t *idref;
+ m_UIADocument.second->UnregisteredAtt("ref", idref);
+ if (!isTrivial(idref)) {
+ if (idref[0] == '#')
+ ++idref;
+ for (size_t idx = 0, end = m_Editors.size(); idx < end; ++idx) {
+ if (m_Editors[idx].m_Id.compare(idref) == 0)
+ ParseStateMachine(m_Editors[idx].m_Id, m_Editors[idx].m_Editor);
+ }
+ }
+ }
+ }
+ }
+
+ virtual bool IsDirty() const { return m_Dirty; }
+ // General queries of the dataset defined by the uia file and all uip files and scxml files it
+ // includes.
+
+ SAppElement *FindElementByPath(const TEditorStr &inPath)
+ {
+ if (!m_FinderElement)
+ m_FinderElement = new SAppElement();
+
+ m_FinderElement->m_Path = inPath;
+
+ nvvector<NVScopedRefCounted<SAppElement>>::iterator iter = eastl::lower_bound(
+ m_Elements.begin(), m_Elements.end(), m_FinderElement, SElemLessThan());
+ if (iter != m_Elements.end() && (*iter)->m_Path == m_FinderElement->m_Path) {
+ return iter->mPtr;
+ }
+ return NVScopedRefCounted<SAppElement>();
+ }
+
+ virtual TEditorStrList GetComponents()
+ {
+ TEditorStrList retval;
+ for (size_t idx = 0, end = m_Elements.size(); idx < end; ++idx) {
+ if (m_Elements[idx]->GetSubType() == ElementSubTypes::Component)
+ retval.push_back(m_Elements[idx]->m_Path);
+ }
+ return retval;
+ }
+
+ virtual TEditorStrList GetComponentSlides(const TEditorStr &inComponent)
+ {
+ SAppElement *elem = FindElementByPath(inComponent);
+ if (elem != NULL && elem->GetSubType() == ElementSubTypes::Component)
+ return static_cast<SAppComponent *>(elem)->m_Slides;
+ return TEditorStrList();
+ }
+
+ virtual TEditorStrList GetBehaviors()
+ {
+ TEditorStrList retval;
+ for (size_t idx = 0, end = m_Elements.size(); idx < end; ++idx) {
+ if (m_Elements[idx]->GetSubType() == ElementSubTypes::Behavior)
+ retval.push_back(m_Elements[idx]->m_Path);
+ }
+ return retval;
+ }
+
+ virtual Q3DStudio::THandlerList GetHandlers(const TEditorStr &inBehavior)
+ {
+ SAppElement *elem = FindElementByPath(inBehavior);
+ if (elem != NULL && elem->GetSubType() == ElementSubTypes::Behavior)
+ return static_cast<SAppBehavior *>(elem)->m_Handlers;
+ return Q3DStudio::THandlerList();
+ }
+
+ virtual Q3DStudio::TVisualEventList GetVisualEvents(const TEditorStr &inElement)
+ {
+ SAppElement *elem = FindElementByPath(inElement);
+ if (elem)
+ return elem->m_VisualEvents;
+ return Q3DStudio::TVisualEventList();
+ }
+
+ virtual TEditorStrList GetElements()
+ {
+ TEditorStrList retval;
+ for (size_t idx = 0, end = m_Elements.size(); idx < end; ++idx)
+ retval.push_back(m_Elements[idx]->m_Path);
+ return retval;
+ }
+
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(const TEditorStr &inElement)
+ {
+ SAppElement *elem = FindElementByPath(inElement);
+ if (elem)
+ return elem->m_Attributes;
+ return Q3DStudio::TAttOrArgList();
+ }
+
+ TEditorPtr CreateSCXMLEditor(const TEditorStr &inPath, const TEditorStr &inId)
+ {
+ TEditorPtr newEditor = m_StateMachineEditorManager.GetOrCreateEditor(inPath, 0);
+ if (newEditor) {
+ SEditorEntry theEntry;
+ theEntry.m_Editor = newEditor.mPtr;
+ theEntry.m_FilePath = inPath;
+ theEntry.m_Id = inId;
+ m_Editors.push_back(theEntry);
+ }
+ return newEditor.mPtr;
+ }
+
+ virtual TEditorPtr GetOrCreateEditor(const TEditorStr &inFullPath, bool *outLoadStatus)
+ {
+ (void)outLoadStatus;
+ TEditorStr normalizedPath(inFullPath);
+ CFileTools::NormalizePath(normalizedPath);
+ for (size_t idx = 0, end = m_Editors.size(); idx < end; ++idx) {
+ if (m_Editors[idx].m_FilePath == normalizedPath)
+ return m_Editors[idx].m_Editor;
+ }
+
+ // Is file full path under our application directory;
+ TEditorStr appDir(m_FilePath);
+ CFileTools::GetDirectory(appDir);
+ TEditorStr editorDir(inFullPath);
+ CFileTools::GetDirectory(editorDir);
+
+ // For scxml files outside our app dir, let the user create an editor manually.
+ // We will have nothing to do with it.
+ if (editorDir.find(appDir) == eastl::string::npos)
+ return TEditorPtr();
+
+ // Get an ID for the editor
+ IDOMReader::Scope __appScope(*m_UIADocument.second);
+ if (!m_UIADocument.second->MoveToFirstChild("assets")) {
+ m_UIADocument.first->Begin("assets", GetNamespace());
+ }
+
+ {
+ IDOMReader::Scope __assetsScope(*m_UIADocument.second);
+ for (bool success = m_UIADocument.second->MoveToFirstChild("statemachine"); success;
+ success = m_UIADocument.second->MoveToNextSibling("statemachine")) {
+ const char8_t *srcPath;
+ const char8_t *id;
+ m_UIADocument.second->UnregisteredAtt("src", srcPath);
+ m_UIADocument.second->UnregisteredAtt("id", id);
+ TEditorStr docPath;
+ CFileTools::CombineBaseAndRelative(appDir.c_str(), srcPath, docPath);
+ CFileTools::NormalizePath(docPath);
+ if (docPath == normalizedPath) {
+ return CreateSCXMLEditor(normalizedPath, nonNull(id));
+ }
+ }
+ }
+
+ eastl::string dirname, fname, extension;
+ CFileTools::Split(normalizedPath.c_str(), dirname, fname, extension);
+ m_UIADocument.first->Begin("statemachine", GetNamespace());
+ m_UIADocument.first->Att("id", fname.c_str(), GetNamespace());
+ eastl::string relativePath;
+ CFileTools::GetRelativeFromBase(appDir, normalizedPath, relativePath);
+ m_UIADocument.first->Att("src", relativePath.c_str());
+ m_Dirty = true;
+ return CreateSCXMLEditor(normalizedPath, nonNull(fname.c_str()));
+ }
+
+ static bool CouldHaveVisualStateExecutableContent(TObjPtr inObject)
+ {
+ return inObject->GetExecutableContentTypes().size() > 0;
+ }
+ template <typename TEditorType>
+ TEditorType &CreateEditor(TObjPtr inParent)
+ {
+ TPropertyAccessorList accessors;
+ TEditorType::CreateProperties(accessors, *this);
+ return *QT3DS_NEW(m_Foundation->getAllocator(), TEditorType)(*this, inParent, accessors);
+ }
+
+ SVSEntry *GetEntryForPair(TEntryExitPair &inPair, InterpreterEventTypes::Enum inType)
+ {
+ SVSEntry *theEntry;
+ if (inType == InterpreterEventTypes::StateEnter
+ || inType == InterpreterEventTypes::Transition) {
+ if (inPair.first == NULL)
+ inPair.first = new SVSEntry(inType);
+ theEntry = inPair.first.mPtr;
+ } else {
+ if (inPair.second == NULL)
+ inPair.second = new SVSEntry(inType);
+ theEntry = inPair.second.mPtr;
+ }
+ return theEntry;
+ }
+
+ void ParseExecutableContent(IDOMReader &inReader, const TEditorStr &inId,
+ TIdEntryExitMap &ioMap, InterpreterEventTypes::Enum inType,
+ TObjPtr parentPtr)
+ {
+ IDOMReader::Scope __contentScope(inReader);
+ {
+ IDOMReader::Scope __testScope(inReader);
+ // See if there is any executable content to begin with.
+ if (inReader.MoveToFirstChild() == false)
+ return;
+ }
+
+ TEntryExitPair &thePair =
+ ioMap.insert(eastl::make_pair(inId, TEntryExitPair())).first->second;
+ SVSEntry *theEntry = GetEntryForPair(thePair, inType);
+ QT3DS_ASSERT(theEntry->m_EventType == inType);
+ for (bool success = inReader.MoveToFirstChild(); success;
+ success = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __elemScope(inReader);
+ const char8_t *elemName(inReader.GetElementName());
+ if (AreEqual(elemName, SGotoSlideEditor::ElementName())) {
+ const char8_t *component, *slide, *rel, *wrap, *direction, *state, *mode,
+ *playthroughto, *rate, *time;
+ inReader.UnregisteredAtt("element", component);
+ if (inReader.UnregisteredAtt("slide", slide) == false)
+ inReader.UnregisteredAtt("rel", rel);
+ else
+ rel = "";
+ inReader.UnregisteredAtt("wrap", wrap);
+ inReader.UnregisteredAtt("direction", direction);
+ inReader.UnregisteredAtt("state", state);
+ inReader.UnregisteredAtt("mode", mode);
+ inReader.UnregisteredAtt("playthroughto", playthroughto);
+ inReader.UnregisteredAtt("rate", rate);
+ inReader.UnregisteredAtt("time", time);
+ SGotoSlideEditor &theEditor = CreateEditor<SGotoSlideEditor>(parentPtr);
+ theEditor.m_Component.assign(nonNull(component));
+ theEditor.m_Slide.assign(nonNull(slide));
+ theEditor.m_Rel.assign(nonNull(rel));
+ theEditor.m_Wrap.assign(nonNull(wrap));
+ theEditor.m_Direction.assign(nonNull(direction));
+ theEditor.m_State.assign(nonNull(state));
+ theEditor.m_Mode.assign(nonNull(mode));
+ theEditor.m_PlayThroughTo.assign(nonNull(playthroughto));
+ theEditor.m_Rate.assign(nonNull(rate));
+ theEditor.m_Time.assign(nonNull(time));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SSetAttributeEditor::ElementName())) {
+ const char8_t *element, *att, *val;
+ inReader.UnregisteredAtt("element", element);
+ inReader.UnregisteredAtt("attribute", att);
+ inReader.UnregisteredAtt("value", val);
+ SSetAttributeEditor &theEditor = CreateEditor<SSetAttributeEditor>(parentPtr);
+ theEditor.m_Element.assign(nonNull(element));
+ theEditor.m_Attribute.assign(nonNull(att));
+ theEditor.m_Value.assign(nonNull(val));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SFireEventEditor::ElementName())) {
+ const char8_t *element, *evt;
+ inReader.UnregisteredAtt("element", element);
+ inReader.UnregisteredAtt("event", evt);
+ SFireEventEditor &theEditor = CreateEditor<SFireEventEditor>(parentPtr);
+ theEditor.m_Element.assign(nonNull(element));
+ theEditor.m_Event.assign(nonNull(evt));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SRunHandlerEditor::ElementName())) {
+ const char8_t *element, *handler, *args;
+ inReader.UnregisteredAtt("element", element);
+ inReader.UnregisteredAtt("handler", handler);
+ inReader.UnregisteredAtt("arguments", args);
+ SRunHandlerEditor &theEditor = CreateEditor<SRunHandlerEditor>(parentPtr);
+ theEditor.m_Behavior.assign(nonNull(element));
+ theEditor.m_Handler.assign(nonNull(handler));
+ theEditor.m_ArgumentStr.assign(nonNull(args));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SSetPresentationEditor::ElementName())) {
+ const char8_t *ref, *attribute, *value;
+ inReader.UnregisteredAtt("ref", ref);
+ inReader.UnregisteredAtt("attribute", attribute);
+ inReader.UnregisteredAtt("value", value);
+ SSetPresentationEditor &theEditor = CreateEditor<SSetPresentationEditor>(parentPtr);
+ theEditor.m_Ref.assign(nonNull(ref));
+ theEditor.m_Attribute.assign(nonNull(attribute));
+ theEditor.m_Value.assign(nonNull(value));
+ theEntry->m_Editors.push_back(theEditor);
+ } else if (AreEqual(elemName, SPlaySoundEditor::ElementName())) {
+ const char8_t *file;
+ inReader.UnregisteredAtt("file", file);
+ SPlaySoundEditor &theEditor = CreateEditor<SPlaySoundEditor>(parentPtr);
+ theEditor.m_FilePath.assign(nonNull(file));
+ theEntry->m_Editors.push_back(theEditor);
+ }
+ }
+ }
+
+ void ParseVisualStateEntry(IDOMReader &inReader, TEditorPtr inEditor, TIdEntryExitMap &retval,
+ const char8_t *stateId)
+ {
+ TObjPtr parentPtr = inEditor->GetObjectById(stateId);
+ if (parentPtr) {
+ if (AreEqual(inReader.GetElementName().c_str(), "state")) {
+ for (bool entryExitSucces = inReader.MoveToFirstChild(); entryExitSucces;
+ entryExitSucces = inReader.MoveToNextSibling()) {
+ const char8_t *signalName(inReader.GetElementName());
+ if (AreEqual(signalName, "enter")) {
+ ParseExecutableContent(inReader, stateId, retval,
+ InterpreterEventTypes::StateEnter, parentPtr);
+ } else if (AreEqual(signalName, "exit")) {
+ ParseExecutableContent(inReader, stateId, retval,
+ InterpreterEventTypes::StateExit, parentPtr);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ } else if (AreEqual(inReader.GetElementName().c_str(), "transition")) {
+ ParseExecutableContent(inReader, stateId, retval, InterpreterEventTypes::Transition,
+ parentPtr);
+ } else {
+ QT3DS_ASSERT(false);
+ }
+ }
+ }
+
+ TIdEntryExitMap &ParseStateMachine(const TEditorStr &inId, TEditorPtr inEditor)
+ {
+ TIdEntryExitMap &retval =
+ m_IdToStateMaps.insert(eastl::make_pair(inId, TIdEntryExitMap())).first->second;
+ if (m_UIADocument.second->MoveToFirstChild("visual-states")) {
+ for (bool stateSuccess = m_UIADocument.second->MoveToFirstChild(); stateSuccess;
+ stateSuccess = m_UIADocument.second->MoveToNextSibling()) {
+ IDOMReader::Scope __stateScope(*m_UIADocument.second);
+ const char8_t *stateId;
+ m_UIADocument.second->UnregisteredAtt("ref", stateId);
+ if (!isTrivial(stateId)) {
+ if (stateId[0] == '#')
+ ++stateId;
+
+ ParseVisualStateEntry(*m_UIADocument.second, inEditor, retval, stateId);
+ }
+ }
+ }
+ return retval; // found a statemachine with the right id.
+ }
+
+ TIdEntryExitMap &GetOrCreateStateMap(const TEditorStr &inId, TEditorPtr inEditor)
+ {
+ TIdEntryExitMap &retval =
+ m_IdToStateMaps.insert(eastl::make_pair(inId, TIdEntryExitMap())).first->second;
+
+ if (retval.empty()) {
+ IDOMReader::Scope __appScope(*m_UIADocument.second);
+ for (bool success = m_UIADocument.second->MoveToFirstChild("statemachine");
+ success && retval.empty();
+ success = m_UIADocument.second->MoveToNextSibling("statemachine")) {
+ IDOMReader::Scope __machineScope(*m_UIADocument.second);
+ const char8_t *idref;
+ m_UIADocument.second->UnregisteredAtt("ref", idref);
+ if (!isTrivial(idref)) {
+ if (idref[0] == '#')
+ ++idref;
+ if (inId.compare(idref) == 0) {
+ return ParseStateMachine(inId, inEditor);
+ }
+ }
+ } // for (statemachines)
+ }
+
+ return retval;
+ }
+
+ TIdEntryExitMap *GetStateMapForObject(TObjPtr inObject)
+ {
+ if (!CouldHaveVisualStateExecutableContent(inObject))
+ return NULL;
+
+ TEditorPtr theEditor = inObject->GetEditor();
+ // Find the id for the editor
+ TIdEntryExitMap *stateMap(NULL);
+
+ for (size_t idx = 0, end = m_Editors.size(); idx < end && stateMap == NULL; ++idx) {
+ if (m_Editors[idx].m_Editor == theEditor)
+ return &GetOrCreateStateMap(m_Editors[idx].m_Id, m_Editors[idx].m_Editor);
+ }
+ return NULL;
+ }
+
+ // The editor obj has a remove from graph function that really is delete
+ virtual TObjList GetVisualStateExecutableContent(TObjPtr inObject,
+ InterpreterEventTypes::Enum inEventType)
+ {
+ TIdEntryExitMap *stateMap = GetStateMapForObject(inObject);
+ if (stateMap == NULL)
+ return TObjList();
+
+ TEditorStr objId(inObject->GetId());
+ TIdEntryExitMap::iterator iter = stateMap->find(objId);
+ for (TIdEntryExitMap::iterator temp = stateMap->begin(), end = stateMap->end(); temp != end;
+ ++temp) {
+ objId = temp->first;
+ objId.clear();
+ }
+ if (iter != stateMap->end()) {
+ TEntryExitPair thePair = iter->second;
+ if (thePair.first && thePair.first->m_EventType == inEventType)
+ return thePair.first->m_Editors;
+ else if (thePair.second && thePair.second->m_EventType == inEventType)
+ return thePair.second->m_Editors;
+ }
+
+ return TObjList();
+ }
+
+ // Type name is the element name, so set-attribute, goto-slide, or fire-event
+ virtual TObjPtr AppendVisualStateExecutableContent(TObjPtr inObject,
+ InterpreterEventTypes::Enum inEventType,
+ const char8_t *inElementName)
+ {
+ TIdEntryExitMap *stateMap = GetStateMapForObject(inObject);
+ if (stateMap == NULL)
+ return TObjPtr();
+
+ NVConstDataRef<InterpreterEventTypes::Enum> supportedTypes =
+ inObject->GetExecutableContentTypes();
+ bool foundEventType = false;
+ for (size_t idx = 0, end = supportedTypes.size(); idx < end && foundEventType == false;
+ ++idx)
+ if (inEventType == supportedTypes[idx])
+ foundEventType = true;
+
+ if (foundEventType == false) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ TEntryExitPair &thePair =
+ stateMap->insert(eastl::make_pair(inObject->GetId(), TEntryExitPair())).first->second;
+ SVSEntry *entry = GetEntryForPair(thePair, inEventType);
+ if (!entry) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ if (AreEqual(inElementName, SGotoSlideEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SGotoSlideEditor>(inObject));
+ } else if (AreEqual(inElementName, SRunHandlerEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SRunHandlerEditor>(inObject));
+ } else if (AreEqual(inElementName, SSetAttributeEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SSetAttributeEditor>(inObject));
+ } else if (AreEqual(inElementName, SFireEventEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SFireEventEditor>(inObject));
+ } else if (AreEqual(inElementName, SSetPresentationEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SSetPresentationEditor>(inObject));
+ } else if (AreEqual(inElementName, SPlaySoundEditor::ElementName())) {
+ entry->m_Editors.push_back(CreateEditor<SPlaySoundEditor>(inObject));
+ } else {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ TObjPtr retval = entry->m_Editors.back();
+ STransaction *theTrans = GetOpenTransactionImpl();
+ if (theTrans) {
+ SVSEntryListChange *theChange =
+ new SVSEntryListChange(TObjPtr(), retval, *entry, true, inObject);
+ theTrans->m_Changes.push_back(theChange);
+ }
+ return retval;
+ }
+
+ virtual TObjPtr ChangeVisualStateExecutableContentName(TObjPtr inContent,
+ const char8_t *inElementName)
+ {
+ TObjPtr theParent = inContent->Parent();
+ if (!CouldHaveVisualStateExecutableContent(theParent)) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ TIdEntryExitMap *stateMap = GetStateMapForObject(theParent);
+ if (stateMap == NULL) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ TIdEntryExitMap::iterator iter = stateMap->find(theParent->GetId());
+ if (iter == stateMap->end()) {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+ TEntryExitPair thePair = iter->second;
+ NVScopedRefCounted<SVSEntry> theEntryFound;
+ if (!theEntryFound) {
+ NVScopedRefCounted<SVSEntry> theEntry = thePair.first;
+ TObjPtr theObj = inContent;
+ if (theEntry) {
+ TObjList::iterator iter =
+ eastl::find(theEntry->m_Editors.begin(), theEntry->m_Editors.end(), theObj);
+ if (iter != theEntry->m_Editors.end()) {
+ theEntryFound = theEntry;
+ }
+ }
+ }
+ if (!theEntryFound) {
+ NVScopedRefCounted<SVSEntry> theEntry = thePair.second;
+ TObjPtr theObj = inContent;
+ if (theEntry) {
+ TObjList::iterator iter =
+ eastl::find(theEntry->m_Editors.begin(), theEntry->m_Editors.end(), theObj);
+ if (iter != theEntry->m_Editors.end()) {
+ theEntryFound = theEntry;
+ }
+ }
+ }
+ if (!theEntryFound)
+ return TObjPtr();
+
+ TObjPtr theRetval;
+ if (AreEqual(inElementName, SGotoSlideEditor::ElementName())) {
+ theRetval = CreateEditor<SGotoSlideEditor>(theParent);
+ } else if (AreEqual(inElementName, SRunHandlerEditor::ElementName())) {
+ theRetval = CreateEditor<SRunHandlerEditor>(theParent);
+ } else if (AreEqual(inElementName, SSetAttributeEditor::ElementName())) {
+ theRetval = CreateEditor<SSetAttributeEditor>(theParent);
+ } else if (AreEqual(inElementName, SFireEventEditor::ElementName())) {
+ theRetval = CreateEditor<SFireEventEditor>(theParent);
+ } else if (AreEqual(inElementName, SSetPresentationEditor::ElementName())) {
+ theRetval = CreateEditor<SSetPresentationEditor>(theParent);
+ } else if (AreEqual(inElementName, SPlaySoundEditor::ElementName())) {
+ theRetval = CreateEditor<SPlaySoundEditor>(theParent);
+ } else {
+ QT3DS_ASSERT(false);
+ return TObjPtr();
+ }
+
+ NVScopedRefCounted<SVSEntryListChange> theOldChange =
+ new SVSEntryListChange(inContent, inContent, *theEntryFound, false, theParent);
+ NVScopedRefCounted<SVSEntryListChange> theNewChange =
+ new SVSEntryListChange(theRetval, inContent, *theEntryFound, true, theParent);
+ theOldChange->Do();
+ theNewChange->Do();
+ if (GetOpenTransactionImpl()) {
+ GetOpenTransactionImpl()->m_Changes.push_back(theOldChange.mPtr);
+ GetOpenTransactionImpl()->m_Changes.push_back(theNewChange.mPtr);
+ }
+
+ return theRetval;
+ }
+
+ // Called when the source uia changes.
+ virtual void RefreshFile() { LoadUIADatabase(); }
+
+ virtual void RefreshFromStream(qt3ds::foundation::IInStream &inStream)
+ {
+ LoadUIADatabaseFromStream(inStream);
+ }
+
+ // Returns the path that was passed in on create.
+ virtual TEditorStr GetFilePath() { return m_FilePath; }
+
+ void CopyDOM(IDOMReader &inReader, IDOMWriter &inWriter)
+ {
+ IDOMReader::Scope __itemScope(inReader);
+ const SDOMElement &theElement(*inReader.GetElement());
+ IDOMWriter::Scope __writeScope(inWriter, theElement.m_Name, theElement.m_Namespace);
+ if (theElement.m_Attributes.empty() && theElement.m_Children.empty()) {
+ if (!isTrivial(theElement.m_Value))
+ inWriter.Value(theElement.m_Value);
+ } else {
+ for (TAttributeList::iterator iter = theElement.m_Attributes.begin(),
+ end = theElement.m_Attributes.end();
+ iter != end; ++iter) {
+ inWriter.Att(iter->m_Name, iter->m_Value, iter->m_Namespace);
+ }
+
+ for (bool success = inReader.MoveToFirstChild(); success;
+ success = inReader.MoveToNextSibling()) {
+ IDOMReader::Scope __loopScope(inReader);
+ CopyDOM(inReader, inWriter);
+ }
+ }
+ }
+ void ReplaceDOMNamespace(SDOMElement &inDom,
+ qt3ds::foundation::CRegisteredString &inNamespaceToReplace,
+ qt3ds::foundation::CRegisteredString &inNewNamespace)
+ {
+ if (!inNamespaceToReplace.IsValid() || !inNewNamespace.IsValid())
+ return;
+ if (inDom.m_Namespace == inNamespaceToReplace)
+ inDom.m_Namespace = inNewNamespace;
+ // Set attributes namespace
+ for (qt3ds::foundation::TAttributeList::iterator theIter = inDom.m_Attributes.begin(),
+ theEnd = inDom.m_Attributes.end();
+ theIter != theEnd; ++theIter) {
+ if (theIter->m_Namespace == inNamespaceToReplace)
+ theIter->m_Namespace = inNewNamespace;
+ }
+ // Recursive
+ for (qt3ds::foundation::SDOMElement::TElementChildList::iterator
+ theIter = inDom.m_Children.begin(),
+ theEnd = inDom.m_Children.end();
+ theIter != theEnd; ++theIter) {
+ ReplaceDOMNamespace(*theIter, inNamespaceToReplace, inNewNamespace);
+ }
+ }
+ struct SEditorIDFinder
+ {
+ TEditorStr m_Id;
+ SEditorIDFinder(const TEditorStr &id)
+ : m_Id(id)
+ {
+ }
+ bool operator()(const SEditorEntry &entry) const { return m_Id == entry.m_Id; }
+ };
+
+ void WriteExecutableContent(IDOMWriter &inWriter, NVScopedRefCounted<SVSEntry> inEntry)
+ {
+ if (inEntry == NULL)
+ return;
+ const char *elemName = "";
+ switch (inEntry->m_EventType) {
+ case InterpreterEventTypes::Transition:
+ break;
+ case InterpreterEventTypes::StateEnter:
+ elemName = "enter";
+ break;
+ case InterpreterEventTypes::StateExit:
+ elemName = "exit";
+ break;
+ default:
+ QT3DS_ASSERT(false);
+ break;
+ }
+ if (!isTrivial(elemName))
+ inWriter.Begin(elemName, GetNamespace());
+
+ eastl::vector<SPropertyDeclaration> properties;
+ for (size_t idx = 0, end = inEntry->m_Editors.size(); idx < end; ++idx) {
+ TObjPtr editor(inEntry->m_Editors[idx]);
+ IDOMWriter::Scope __contentScope(inWriter, editor->TypeName(), GetNamespace());
+ editor->GetProperties(properties);
+ bool theNoProperty = true;
+ for (size_t idx = 0, end = properties.size(); idx < end; ++idx) {
+ TEditorStr theProp =
+ editor->GetPropertyValue(properties[idx].m_Name)->getData<TEditorStr>();
+ if (theProp.empty() == false) {
+ inWriter.Att(properties[idx].m_Name.c_str(), theProp.c_str(),
+ GetNamespace());
+ theNoProperty = false;
+ }
+ }
+ if (theNoProperty && properties.size() > 0)
+ inWriter.Att(properties[0].m_Name.c_str(), "", GetNamespace());
+ }
+
+ if (!isTrivial(elemName))
+ inWriter.End();
+ }
+
+ void WriteStateEntry(TIdEntryExitMap::const_iterator stateIter, IDOMWriter &writer,
+ const char *typeName, const char *id)
+ {
+ const char8_t *stateName = "state";
+ if (AreEqual(typeName, "transition"))
+ stateName = "transition";
+ eastl::string tempAtt;
+ IDOMWriter::Scope __stateScope(writer, stateName, GetNamespace());
+ tempAtt.assign("#");
+ tempAtt.append(id);
+ writer.Att("ref", tempAtt.c_str());
+ WriteExecutableContent(writer, stateIter->second.first);
+ WriteExecutableContent(writer, stateIter->second.second);
+ }
+
+ // Returns false if unable to save, ask users to check the file out.
+ bool SaveInner(qt3ds::foundation::IOutStream &inStream)
+ {
+ IDOMReader::Scope __saveScope(m_UIADocument.second);
+ NVScopedRefCounted<IDOMFactory> theFactory(
+ IDOMFactory::CreateDOMFactory(m_Foundation->getAllocator(), m_StringTable));
+ NVScopedRefCounted<IDOMWriter> outgoingDoc =
+ IDOMWriter::CreateDOMWriter(m_Foundation->getAllocator(), "application", m_StringTable,
+ GetNamespace())
+ .first;
+ {
+ m_UIADocument.second->MoveToFirstChild("application");
+ for (SDOMAttribute *theAtt = m_UIADocument.second->GetFirstAttribute(); theAtt;
+ theAtt = m_UIADocument.second->GetNextAttribute()) {
+ outgoingDoc->Att(theAtt->m_Name, theAtt->m_Value, theAtt->m_Namespace);
+ }
+ }
+
+ {
+ IDOMReader::Scope __appScope(m_UIADocument.second);
+ if (m_UIADocument.second->MoveToFirstChild("assets")) {
+ CopyDOM(*m_UIADocument.second, *outgoingDoc);
+ }
+ }
+ {
+ eastl::string tempAtt;
+ for (TIdStateMapMap::const_iterator iter = m_IdToStateMaps.begin(),
+ end = m_IdToStateMaps.end();
+ iter != end; ++iter) {
+ nvvector<SEditorEntry>::iterator editorEntry = eastl::find_if(
+ m_Editors.begin(), m_Editors.end(), SEditorIDFinder(iter->first));
+ if (editorEntry == m_Editors.end()) {
+ QT3DS_ASSERT(false);
+ return false;
+ }
+ IDOMWriter::Scope __machineScope(*outgoingDoc, "statemachine", GetNamespace());
+ tempAtt.assign("#");
+ tempAtt.append(iter->first);
+ outgoingDoc->Att("ref", tempAtt.c_str());
+ IDOMWriter::Scope __vsScope(*outgoingDoc, "visual-states", GetNamespace());
+ const TIdEntryExitMap &itemMap = iter->second;
+ for (TIdEntryExitMap::const_iterator stateIter = itemMap.begin(),
+ stateEnd = itemMap.end();
+ stateIter != stateEnd; ++stateIter) {
+ TObjPtr editorObj =
+ editorEntry->m_Editor->GetObjectById(stateIter->first.c_str());
+ if (!editorObj) {
+ QT3DS_ASSERT(false);
+ continue;
+ }
+ WriteStateEntry(stateIter, *outgoingDoc, editorObj->TypeName(),
+ stateIter->first.c_str());
+ }
+ }
+ }
+
+ SDOMElement *topElem = outgoingDoc->GetTopElement();
+ CDOMSerializer::WriteXMLHeader(inStream);
+ CDOMSerializer::Write(m_Foundation->getAllocator(), *topElem, inStream, *m_StringTable,
+ m_UIANamespaces);
+ m_Dirty = false;
+ return true;
+ }
+
+ virtual bool Save()
+ {
+ CFileSeekableIOStream theStream(m_FilePath.c_str(), FileWriteFlags());
+ if (!theStream.IsOpen())
+ return false;
+ return SaveInner(theStream);
+ }
+
+ virtual bool Save(qt3ds::foundation::IOutStream &inStream) { return SaveInner(inStream); }
+
+ virtual eastl::vector<SPresentation> GetPresentations() { return m_Presentations; }
+ virtual eastl::string GetElementType(SAppElement &elem) { return elem.m_Type; }
+
+ virtual eastl::string GetElementId(SAppElement &elem) { return elem.m_Id; }
+ virtual bool IsComponent(SAppElement &elem)
+ {
+ return elem.GetSubType() == ElementSubTypes::Component;
+ }
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(SAppElement &elem)
+ {
+ return elem.m_Attributes;
+ }
+ virtual Q3DStudio::TAttOrArgList GetSlideAttributes() { return m_SlideProperties; }
+ virtual eastl::vector<SDatamodelValue> GetElementAttributeInitialValues(SAppElement &elem)
+ {
+ return elem.m_InitialValues;
+ }
+ virtual eastl::vector<SAppElement *> GetElementChildren(SAppElement &elem)
+ {
+ eastl::vector<SAppElement *> retval;
+ retval.resize(elem.m_Children.size());
+ for (size_t idx = 0, end = elem.m_Children.size(); idx < end; ++idx)
+ retval[idx] = elem.m_Children[idx].mPtr;
+ return retval;
+ }
+ virtual eastl::string GetLastLoadingErrorString() { return m_LoadingErrorString; }
+ // The file may either exist or not. You can pass in a uip file as well as a uia file.
+
+ virtual NVFoundationBase &GetFoundation() { return m_Foundation->getFoundation(); }
+
+ virtual IStringTable &GetStringTable() { return *m_StringTable; }
+
+ // ITransactionManager
+ // Undo/redo is supported via a transparent transaction system.
+ // calls are reentrant but last call will close the transaction object.
+ // Any changes to any editor objects will go through this transaction when they happen.
+ virtual TSignalConnectionPtr AddChangeListener(IEditorChangeListener &inListener)
+ {
+ return m_TransactionManager->AddChangeListener(inListener);
+ }
+ virtual TTransactionPtr BeginTransaction(const TEditorStr &inName)
+ {
+ return m_TransactionManager->BeginTransaction(inName);
+ }
+
+ virtual TTransactionPtr GetOpenTransaction()
+ {
+ return m_TransactionManager->GetOpenTransaction();
+ }
+
+ virtual void RollbackTransaction() { m_TransactionManager->RollbackTransaction(); }
+ virtual void EndTransaction() { m_TransactionManager->EndTransaction(); }
+
+ virtual void OnObjectCreated(TObjPtr) {}
+
+ virtual void OnObjectDeleted(TObjPtr inObject)
+ {
+ // don't care.
+ if (!CouldHaveVisualStateExecutableContent(inObject))
+ return;
+
+ TIdEntryExitMap *stateMap = GetStateMapForObject(inObject);
+ if (stateMap == NULL)
+ return;
+
+ TIdEntryExitMap::iterator iter = stateMap->find(inObject->GetId());
+ if (iter == stateMap->end())
+ return;
+ NVScopedRefCounted<SIdEntryExitDeleteChange> theChange =
+ new SIdEntryExitDeleteChange(*stateMap, inObject->GetId(), inObject);
+ theChange->Do();
+ if (GetOpenTransactionImpl())
+ GetOpenTransactionImpl()->m_Changes.push_back(theChange.mPtr);
+ }
+ bool RemoveObjectFromEntry(NVScopedRefCounted<SVSEntry> inEntry, SVSEditorObject &inObj)
+ {
+ if (inEntry) {
+ TObjList::iterator iter =
+ eastl::find(inEntry->m_Editors.begin(), inEntry->m_Editors.end(), TObjPtr(inObj));
+ if (iter != inEntry->m_Editors.end()) {
+ NVScopedRefCounted<SVSEntryListChange> theChange = new SVSEntryListChange(
+ TObjPtr(inObj), TObjPtr(inObj), *inEntry, false, inObj.m_ParentObject);
+ theChange->Do();
+ if (GetOpenTransactionImpl())
+ GetOpenTransactionImpl()->m_Changes.push_back(theChange.mPtr);
+ return true;
+ }
+ }
+ return false;
+ }
+
+ virtual void RemoveObjectFromGraph(SVSEditorObject &inObj)
+ {
+ TObjPtr parentPtr = inObj.m_ParentObject;
+ if (!CouldHaveVisualStateExecutableContent(parentPtr)) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ TIdEntryExitMap *stateMap = GetStateMapForObject(parentPtr);
+ if (stateMap == NULL) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+
+ TIdEntryExitMap::iterator iter = stateMap->find(parentPtr->GetId());
+ if (iter == stateMap->end()) {
+ QT3DS_ASSERT(false);
+ return;
+ }
+ TEntryExitPair thePair = iter->second;
+ if (!RemoveObjectFromEntry(thePair.first, inObj)) {
+ RemoveObjectFromEntry(thePair.second, inObj);
+ }
+ }
+
+ struct SEditorFinder
+ {
+ TEditorPtr m_Editor;
+ SEditorFinder(TEditorPtr editor)
+ : m_Editor(editor)
+ {
+ }
+ bool operator()(const SEditorEntry &entry) const { return entry.m_Editor == m_Editor; }
+ };
+
+ void CopyStateNode(SStateNode &inNode, TIdEntryExitMap &idMap, IDOMWriter &writer)
+ {
+ TIdEntryExitMap::iterator iter = idMap.find(inNode.m_Id.c_str());
+ if (iter != idMap.end()) {
+ const char *typeName = "state";
+ if (inNode.m_Type == StateNodeTypes::Transition)
+ typeName = "transition";
+ WriteStateEntry(iter, writer, typeName, inNode.m_Id.c_str());
+ }
+ TStateNodeList *childList = inNode.GetChildren();
+ if (childList) {
+ for (TStateNodeList::iterator iter = childList->begin(), end = childList->end();
+ iter != end; ++iter) {
+ CopyStateNode(*iter, idMap, writer);
+ }
+ }
+ }
+
+ virtual void OnCopy(TEditorPtr inEditor, eastl::vector<SStateNode *> &ioCopiedRoots,
+ IDOMWriter &ioWriter, eastl::vector<SNamespacePair> &ioNamespaces)
+ {
+ ioNamespaces.push_back(SNamespacePair(m_StringTable->RegisterStr(GetNamespace()),
+ m_StringTable->RegisterStr("uia")));
+ eastl::vector<SEditorEntry>::iterator entry =
+ eastl::find_if(m_Editors.begin(), m_Editors.end(), SEditorFinder(inEditor));
+ if (entry == m_Editors.end())
+ return;
+ TIdStateMapMap::iterator mapEntry = m_IdToStateMaps.find(entry->m_Id);
+ if (mapEntry == m_IdToStateMaps.end())
+ return;
+ IDOMWriter::Scope __modelScope(ioWriter, "datamodel_fragment", GetNamespace());
+ for (size_t idx = 0, end = ioCopiedRoots.size(); idx < end; ++idx) {
+ CopyStateNode(*ioCopiedRoots[idx], mapEntry->second, ioWriter);
+ }
+ }
+
+ virtual void OnPaste(TEditorPtr inEditor, IDOMReader &ioReader,
+ CXMLIO::TIdRemapMap &inStateIdRemapMap)
+ {
+ eastl::vector<SEditorEntry>::iterator entry =
+ eastl::find_if(m_Editors.begin(), m_Editors.end(), SEditorFinder(inEditor));
+ if (entry == m_Editors.end())
+ return;
+
+ IDOMReader::Scope __fragmentScope(ioReader);
+ if (ioReader.MoveToFirstChild("datamodel_fragment")) {
+ TIdEntryExitMap &stateMap =
+ m_IdToStateMaps.insert(eastl::make_pair(entry->m_Id, TIdEntryExitMap()))
+ .first->second;
+ for (bool success = ioReader.MoveToFirstChild(); success;
+ success = ioReader.MoveToNextSibling()) {
+ IDOMReader::Scope __childScope(ioReader);
+ const char8_t *idRef;
+ ioReader.UnregisteredAtt("ref", idRef);
+ if (isTrivial(idRef))
+ continue;
+ if (idRef[0] == '#')
+ ++idRef;
+
+ CXMLIO::TIdRemapMap::iterator finder = inStateIdRemapMap.find(idRef);
+ if (finder != inStateIdRemapMap.end())
+ idRef = finder->second.c_str();
+
+ TEditorStr idStr(idRef);
+ TObjPtr parentObj = inEditor->GetObjectById(idRef);
+ if (parentObj) {
+ ParseVisualStateEntry(ioReader, inEditor, stateMap, idRef);
+ if (stateMap.find(idStr) != stateMap.end()
+ && m_TransactionManager->GetOpenTransactionImpl()) {
+ m_TransactionManager->GetOpenTransactionImpl()->m_Changes.push_back(
+ new SIdEntryExitDeleteChange(stateMap, idStr, parentObj, false));
+ }
+ }
+ }
+ }
+ }
+
+ virtual void OnIDChange(TEditorPtr inEditor, SStateNode &inNode, const char8_t *inOldId)
+ {
+ eastl::vector<SEditorEntry>::iterator entry =
+ eastl::find_if(m_Editors.begin(), m_Editors.end(), SEditorFinder(inEditor));
+ if (entry == m_Editors.end())
+ return;
+
+ TIdEntryExitMap &stateMap =
+ m_IdToStateMaps.insert(eastl::make_pair(entry->m_Id, TIdEntryExitMap())).first->second;
+ TEditorStr oldIdStr(inOldId);
+ TEditorStr newIdStr(inNode.m_Id.c_str());
+ TIdEntryExitMap::iterator iter = stateMap.find(oldIdStr);
+ if (iter != stateMap.end()) {
+ TEntryExitPair thePair(iter->second);
+ stateMap.erase(iter);
+ stateMap.insert(eastl::make_pair(newIdStr, thePair));
+ }
+ }
+ virtual bool OnDeleteState(TObjPtr inObject)
+ {
+ // don't care.
+ if (!CouldHaveVisualStateExecutableContent(inObject))
+ return false;
+
+ TIdEntryExitMap *stateMap = GetStateMapForObject(inObject);
+ if (stateMap == NULL)
+ return false;
+
+ TIdEntryExitMap::iterator iter = stateMap->find(inObject->GetId());
+ if (iter == stateMap->end())
+ return false;
+ NVScopedRefCounted<SIdEntryExitDeleteChange> theChange =
+ new SIdEntryExitDeleteChange(*stateMap, inObject->GetId(), inObject);
+ theChange->Do();
+ if (GetOpenTransactionImpl())
+ GetOpenTransactionImpl()->m_Changes.push_back(theChange.mPtr);
+ return true;
+ }
+ virtual bool OnReloadStateMachine(TEditorPtr inStateMachineEditor)
+ {
+ bool theRetval = false;
+ TEditorPtr theEditor = inStateMachineEditor;
+
+ for (size_t idx = 0, end = m_Editors.size(); idx < end; ++idx) {
+ if (m_Editors[idx].m_Editor == theEditor) {
+ TIdStateMapMap::iterator theFind = m_IdToStateMaps.find(m_Editors[idx].m_Id);
+ if (theFind != m_IdToStateMaps.end()) {
+ TIdEntryExitMap &theStateMap = theFind->second;
+ for (TIdEntryExitMap::iterator theIter = theStateMap.begin(),
+ theEnd = theStateMap.end();
+ theIter != theEnd;) {
+ TEditorStr theStateId = theIter->first;
+ TObjPtr theState = inStateMachineEditor->GetObjectById(theStateId.c_str());
+ if (theState) {
+ {
+ NVScopedRefCounted<SVSEntry> theEntry = theIter->second.first;
+ if (theEntry) {
+ for (TObjList::iterator theIter = theEntry->m_Editors.begin(),
+ theEnd = theEntry->m_Editors.end();
+ theIter != theEnd; ++theIter) {
+ SVSEditorObject *theExecutableContent =
+ static_cast<SVSEditorObject *>(theIter->mPtr);
+ if (theExecutableContent) {
+ theExecutableContent->m_ParentObject = theState;
+ }
+ }
+ }
+ }
+ {
+ NVScopedRefCounted<SVSEntry> theEntry = theIter->second.second;
+ if (theEntry) {
+ for (TObjList::iterator theIter = theEntry->m_Editors.begin(),
+ theEnd = theEntry->m_Editors.end();
+ theIter != theEnd; ++theIter) {
+ SVSEditorObject *theExecutableContent =
+ static_cast<SVSEditorObject *>(theIter->mPtr);
+ if (theExecutableContent) {
+ theExecutableContent->m_ParentObject = theState;
+ }
+ }
+ }
+ }
+ ++theIter;
+ } else {
+ theStateMap.erase(theIter++);
+ theRetval = true;
+ }
+ }
+ }
+ break;
+ }
+ }
+ return theRetval;
+ }
+ virtual void RegisterChangeListener(IStateMachineChangeListener &) {}
+ virtual void UnregisterChangeListener(IStateMachineChangeListener &) {}
+};
+}
+
+IDatamodel &IDatamodel::Create(qt3ds::state::editor::TFoundationPtr inFoundation,
+ const TEditorStr &inPath, const TEditorStr &inAppDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IInStream *inStream)
+{
+ return IDatamodel::Create(inFoundation, inPath, inAppDir, inStateMachineEditorManager,
+ IStringTable::CreateStringTable(inFoundation->getAllocator()),
+ inStream);
+}
+
+IDatamodel &IDatamodel::Create(qt3ds::state::editor::TFoundationPtr inFoundation,
+ const TEditorStr &inPath, const TEditorStr &inAppDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IStringTable &inStringTable, IInStream *inStream)
+{
+ DatamodelImpl &theDatamodel = *QT3DS_NEW(inFoundation->getAllocator(), DatamodelImpl)(
+ inFoundation, inPath, inAppDir, inStateMachineEditorManager, inStringTable);
+ if (inStream)
+ theDatamodel.LoadUIADatabaseFromStream(*inStream);
+ else
+ theDatamodel.LoadUIADatabase();
+ return theDatamodel;
+}
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.h b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.h
new file mode 100644
index 00000000..e064529a
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodel.h
@@ -0,0 +1,174 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef UIA_DATAMODEL_H
+#define UIA_DATAMODEL_H
+#include "Qt3DSState.h"
+#include "Qt3DSStateEditor.h"
+#include "Qt3DSMetadata.h"
+#include "Qt3DSStateInterpreter.h"
+#include "Qt3DSStateEditorFoundation.h"
+
+namespace qt3ds {
+namespace app {
+ using namespace qt3ds::state;
+ using namespace qt3ds::state::editor;
+
+ typedef eastl::pair<Q3DStudio::ERuntimeDataModelDataType,
+ Q3DStudio::ERuntimeAdditionalMetaDataType>
+ TDataType;
+
+ struct SDatamodelValue;
+
+ struct SAppElement;
+
+ struct SPresentation
+ {
+ eastl::string m_Id;
+ eastl::string m_SrcPath;
+ eastl::string m_Author;
+ eastl::string m_Company;
+ QT3DSU32 m_Width;
+ QT3DSU32 m_Height;
+ SAppElement *m_Scene;
+ SPresentation()
+ : m_Width(800)
+ , m_Height(480)
+ , m_Scene(NULL)
+ {
+ }
+ };
+
+ class IStateMachineChangeListener
+ {
+ public:
+ virtual ~IStateMachineChangeListener() {}
+ virtual bool OnDeleteState(TObjPtr inObject) = 0;
+ virtual bool OnReloadStateMachine(TEditorPtr inStateMachineEditor) = 0;
+ };
+
+ class IStateMachineEditorManager
+ {
+ public:
+ virtual ~IStateMachineEditorManager() {}
+ virtual TEditorPtr GetOrCreateEditor(const TEditorStr &inFullPath, bool *outLoadStatus) = 0;
+ virtual void RegisterChangeListener(IStateMachineChangeListener &inListener) = 0;
+ virtual void UnregisterChangeListener(IStateMachineChangeListener &inListener) = 0;
+ };
+
+ class IDatamodel : public ITransactionManager,
+ public IStateMachineEditorManager,
+ public IStateMachineChangeListener
+ {
+ protected:
+ virtual ~IDatamodel() {}
+ public:
+ // Returns true if we had to create the file. Users should always keep track of the
+ // transaction stack also
+ // this does not include that.
+ virtual bool IsDirty() const = 0;
+ // General queries of the dataset defined by the uia file and all uip files and scxml files
+ // it includes.
+
+ virtual TEditorStrList GetComponents() = 0;
+ virtual TEditorStrList GetComponentSlides(const TEditorStr &inComponent) = 0;
+
+ virtual TEditorStrList GetBehaviors() = 0;
+
+ virtual Q3DStudio::THandlerList GetHandlers(const TEditorStr &inBehavior) = 0;
+
+ virtual Q3DStudio::TVisualEventList GetVisualEvents(const TEditorStr &inElement) = 0;
+
+ virtual TEditorStrList GetElements() = 0;
+
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(const TEditorStr &inElement) = 0;
+
+ // Necessary to share transaction info and trigger deletes of related information but not
+ // going to get
+ // done right now.
+ // virtual TEditorPtr GetOrCreateEditor( const TEditorStr& inFullPath ) = 0;
+
+ // The editor obj has a remove from graph function that really is delete
+ virtual TObjList
+ GetVisualStateExecutableContent(TObjPtr inObject,
+ InterpreterEventTypes::Enum inEventType) = 0;
+ // Type name is the element name, so set-attribute, goto-slide, or fire-event
+ virtual TObjPtr AppendVisualStateExecutableContent(TObjPtr inObject,
+ InterpreterEventTypes::Enum inEventType,
+ const char8_t *inElementName) = 0;
+ virtual TObjPtr ChangeVisualStateExecutableContentName(TObjPtr inContent,
+ const char8_t *inElementName) = 0;
+
+ // Called when the source uia changes.
+ virtual void RefreshFile() = 0;
+ virtual void RefreshFromStream(qt3ds::foundation::IInStream &inStream) = 0;
+ // Returns the path that was passed in on create.
+ virtual TEditorStr GetFilePath() = 0;
+
+ // Returns false if unable to save, ask users to check the file out.
+ virtual bool Save() = 0;
+ virtual bool Save(qt3ds::foundation::IOutStream &inStream) = 0;
+
+ // The section below allows someone to build an initial scene graph.
+ // Note that components have additional properties
+ // Get a list of presentations found while parsing uia file.
+ virtual eastl::vector<SPresentation> GetPresentations() = 0;
+ virtual eastl::string GetElementType(SAppElement &elem) = 0;
+ virtual eastl::string GetElementId(SAppElement &elem) = 0;
+ virtual bool IsComponent(SAppElement &elem) = 0;
+ virtual Q3DStudio::TAttOrArgList GetElementAttributes(SAppElement &elem) = 0;
+ // These are found either on the slide or on the component depending on if you are working
+ // in uip space or runtime space.
+ virtual Q3DStudio::TAttOrArgList GetSlideAttributes() = 0;
+ virtual eastl::vector<SDatamodelValue>
+ GetElementAttributeInitialValues(SAppElement &elem) = 0;
+ virtual eastl::vector<SAppElement *> GetElementChildren(SAppElement &elem) = 0;
+
+ virtual eastl::string GetLastLoadingErrorString() = 0;
+
+ virtual NVFoundationBase &GetFoundation() = 0;
+ virtual IStringTable &GetStringTable() = 0;
+
+ // inPath may either exist or not. You can pass in a uip file as well as a uia file.
+ // application dir is so we can find the meta data.
+ // inStream provides a way to load from memory, it will be used if it's not NULL.
+ static IDatamodel &Create(qt3ds::state::editor::TFoundationPtr inFoundation,
+ const TEditorStr &inPath, const TEditorStr &inApplicationDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IInStream *inStream = 0);
+ static IDatamodel &Create(qt3ds::state::editor::TFoundationPtr inFoundation,
+ const TEditorStr &inPath, const TEditorStr &inApplicationDir,
+ IStateMachineEditorManager &inStateMachineEditorManager,
+ IStringTable &inStringTable, IInStream *inStream = 0);
+ };
+
+ typedef NVScopedRefCounted<IDatamodel> TDatamodelPtr;
+}
+}
+#endif
diff --git a/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodelValue.h b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodelValue.h
new file mode 100644
index 00000000..7698809c
--- /dev/null
+++ b/src/Runtime/Source/stateapplication/editor/Qt3DSUIADatamodelValue.h
@@ -0,0 +1,291 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 NVIDIA Corporation.
+** Copyright (C) 2017 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of Qt 3D Studio.
+**
+** $QT_BEGIN_LICENSE:GPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 3 or (at your option) any later version
+** approved by the KDE Free Qt Foundation. The licenses are as published by
+** the Free Software Foundation and appearing in the file LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+#ifndef UIA_DATAMODEL_VALUE_H
+#define UIA_DATAMODEL_VALUE_H
+#include "foundation/Qt3DSDiscriminatedUnion.h"
+#include "Qt3DSUIADatamodel.h"
+
+namespace qt3ds {
+namespace app {
+
+ using namespace Q3DStudio;
+
+ template <typename TDatatype>
+ struct SDatamodelValueTypeMap
+ {
+ };
+
+#define QT3DS_UIA_DATAMODEL_TYPE_MAP(type, enumname) \
+ template <> \
+ struct SDatamodelValueTypeMap<type> \
+ { \
+ static Q3DStudio::ERuntimeDataModelDataType GetType() { return Q3DStudio::enumname; } \
+ };
+
+ struct SGuid
+ {
+ long m_Data[4];
+ };
+
+ struct SObjectRef
+ {
+ CRegisteredString m_Presentation;
+ CRegisteredString m_Id;
+ };
+
+ class CStringOrInt
+ {
+ bool m_IsString;
+ CRegisteredString m_String;
+ int m_IntValue;
+
+ public:
+ CStringOrInt(CRegisteredString val = CRegisteredString())
+ : m_IsString(true)
+ , m_String(val)
+ , m_IntValue(0)
+ {
+ }
+ CStringOrInt(int val)
+ : m_IsString(false)
+ , m_IntValue(val)
+ {
+ }
+ CStringOrInt(const CStringOrInt &other)
+ : m_IsString(other.m_IsString)
+ , m_String(other.m_String)
+ , m_IntValue(other.m_IntValue)
+ {
+ }
+ CStringOrInt &operator=(const CStringOrInt &other)
+ {
+ m_IsString = other.m_IsString;
+ m_String = other.m_String;
+ m_IntValue = other.m_IntValue;
+ return *this;
+ }
+ bool IsString() const { return m_IsString; }
+ CRegisteredString StringValue() const
+ {
+ QT3DS_ASSERT(m_IsString);
+ return m_String;
+ }
+ int IntValue() const
+ {
+ QT3DS_ASSERT(m_IsString == false);
+ return m_IntValue;
+ }
+ };
+
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(float, ERuntimeDataModelDataTypeFloat);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(QT3DSVec2, ERuntimeDataModelDataTypeFloat2);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(QT3DSVec3, ERuntimeDataModelDataTypeFloat3);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(QT3DSI32, ERuntimeDataModelDataTypeLong);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(eastl::string, ERuntimeDataModelDataTypeString);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(bool, ERuntimeDataModelDataTypeBool);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(SGuid, ERuntimeDataModelDataTypeLong4);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(CRegisteredString, ERuntimeDataModelDataTypeStringRef);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(SObjectRef, ERuntimeDataModelDataTypeObjectRef);
+ QT3DS_UIA_DATAMODEL_TYPE_MAP(CStringOrInt, ERuntimeDataModelDataTypeStringOrInt);
+
+ struct SDatamodelValueUnionTraits
+ {
+ typedef ERuntimeDataModelDataType TIdType;
+ enum {
+ TBufferSize = sizeof(eastl::string),
+ };
+
+ static TIdType getNoDataId() { return ERuntimeDataModelDataTypeNone; }
+
+ template <typename TDataType>
+ static TIdType getType()
+ {
+ return SDatamodelValueTypeMap<TDataType>().GetType();
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case ERuntimeDataModelDataTypeFloat:
+ return inVisitor(*NVUnionCast<float *>(inData));
+ case ERuntimeDataModelDataTypeFloat2:
+ return inVisitor(*NVUnionCast<QT3DSVec2 *>(inData));
+ case ERuntimeDataModelDataTypeFloat3:
+ return inVisitor(*NVUnionCast<QT3DSVec3 *>(inData));
+ case ERuntimeDataModelDataTypeLong:
+ return inVisitor(*NVUnionCast<QT3DSI32 *>(inData));
+ case ERuntimeDataModelDataTypeString:
+ return inVisitor(*NVUnionCast<eastl::string *>(inData));
+ case ERuntimeDataModelDataTypeBool:
+ return inVisitor(*NVUnionCast<bool *>(inData));
+ case ERuntimeDataModelDataTypeLong4:
+ return inVisitor(*NVUnionCast<SGuid *>(inData));
+ case ERuntimeDataModelDataTypeStringRef:
+ return inVisitor(*NVUnionCast<CRegisteredString *>(inData));
+ case ERuntimeDataModelDataTypeObjectRef:
+ return inVisitor(*NVUnionCast<SObjectRef *>(inData));
+ case ERuntimeDataModelDataTypeStringOrInt:
+ return inVisitor(*NVUnionCast<CStringOrInt *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case ERuntimeDataModelDataTypeNone:
+ return inVisitor();
+ }
+ }
+
+ template <typename TRetType, typename TVisitorType>
+ static TRetType visit(const char *inData, TIdType inType, TVisitorType inVisitor)
+ {
+ switch (inType) {
+ case ERuntimeDataModelDataTypeFloat:
+ return inVisitor(*NVUnionCast<const float *>(inData));
+ case ERuntimeDataModelDataTypeFloat2:
+ return inVisitor(*NVUnionCast<const QT3DSVec2 *>(inData));
+ case ERuntimeDataModelDataTypeFloat3:
+ return inVisitor(*NVUnionCast<const QT3DSVec3 *>(inData));
+ case ERuntimeDataModelDataTypeLong:
+ return inVisitor(*NVUnionCast<const QT3DSI32 *>(inData));
+ case ERuntimeDataModelDataTypeString:
+ return inVisitor(*NVUnionCast<const eastl::string *>(inData));
+ case ERuntimeDataModelDataTypeBool:
+ return inVisitor(*NVUnionCast<const bool *>(inData));
+ case ERuntimeDataModelDataTypeLong4:
+ return inVisitor(*NVUnionCast<const SGuid *>(inData));
+ case ERuntimeDataModelDataTypeStringRef:
+ return inVisitor(*NVUnionCast<const CRegisteredString *>(inData));
+ case ERuntimeDataModelDataTypeObjectRef:
+ return inVisitor(*NVUnionCast<const SObjectRef *>(inData));
+ case ERuntimeDataModelDataTypeStringOrInt:
+ return inVisitor(*NVUnionCast<const CStringOrInt *>(inData));
+ default:
+ QT3DS_ASSERT(false);
+ case ERuntimeDataModelDataTypeNone:
+ return inVisitor();
+ }
+ }
+ };
+}
+}
+
+// need some specializations in the original nv foundation namespace
+namespace qt3ds {
+namespace foundation {
+
+ template <>
+ struct DestructTraits<qt3ds::app::SGuid>
+ {
+ void destruct(qt3ds::app::SGuid &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::app::SObjectRef>
+ {
+ void destruct(qt3ds::app::SObjectRef &) {}
+ };
+ template <>
+ struct DestructTraits<qt3ds::app::CStringOrInt>
+ {
+ void destruct(qt3ds::app::CStringOrInt &) {}
+ };
+ template <>
+ struct DestructTraits<CRegisteredString>
+ {
+ void destruct(CRegisteredString &) {}
+ };
+}
+}
+
+namespace qt3ds {
+namespace app {
+
+ typedef qt3ds::foundation::
+ DiscriminatedUnion<qt3ds::foundation::
+ DiscriminatedUnionGenericBase<SDatamodelValueUnionTraits,
+ SDatamodelValueUnionTraits::
+ TBufferSize>,
+ SDatamodelValueUnionTraits::TBufferSize>
+ TDatamodelUnionType;
+
+ struct SDatamodelValue : public TDatamodelUnionType
+ {
+ SDatamodelValue() {}
+ SDatamodelValue(const SDatamodelValue &other)
+ : TDatamodelUnionType(static_cast<const TDatamodelUnionType &>(other))
+ {
+ }
+ SDatamodelValue(float other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(QT3DSVec2 other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(QT3DSVec3 other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(QT3DSI32 other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(const eastl::string &other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(bool other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(SGuid other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(CRegisteredString other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(SObjectRef other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue(CStringOrInt other)
+ : TDatamodelUnionType(other)
+ {
+ }
+ SDatamodelValue &operator=(const SDatamodelValue &other)
+ {
+ TDatamodelUnionType::operator=(other);
+ return *this;
+ }
+ };
+}
+}
+#endif