diff options
Diffstat (limited to 'src/Runtime/Source/stateapplication/editor')
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 ¤tPresentation(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 |