diff options
Diffstat (limited to 'src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemBinding.cpp')
-rw-r--r-- | src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemBinding.cpp | 1164 |
1 files changed, 1164 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemBinding.cpp b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemBinding.cpp new file mode 100644 index 00000000..d71ef582 --- /dev/null +++ b/src/Authoring/Qt3DStudio/Palettes/Timeline/Bindings/Qt3DSDMTimelineItemBinding.cpp @@ -0,0 +1,1164 @@ +/**************************************************************************** +** +** Copyright (C) 2008 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-EXCEPT$ +** 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 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** 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 "Qt3DSCommonPrecompile.h" +#include "Qt3DSDMTimelineItemBinding.h" +#include "TimelineTranslationManager.h" +#include "TimeEditDlg.h" +#include "EmptyTimelineTimebar.h" +#include "Qt3DSDMTimelineTimebar.h" +#include "StudioApp.h" +#include "Core.h" +#include "Dialogs.h" +#include "GraphUtils.h" +#include "Qt3DSDMDataCore.h" +#include "DropTarget.h" + +// Data model specific +#include "IDoc.h" +#include "ClientDataModelBridge.h" +#include "Dispatch.h" +#include "DropSource.h" +#include "Qt3DSDMTimelineItemProperty.h" +#include "Qt3DSDMSlides.h" +#include "Qt3DSDMStudioSystem.h" +#include "Qt3DSDMSlideGraphCore.h" +#include "Qt3DSDMActionCore.h" +#include "Qt3DSDMAnimation.h" +#include "CmdDataModelChangeKeyframe.h" +#include "RelativePathTools.h" +#include "IDocumentEditor.h" +#include "Qt3DSFileTools.h" +#include "ImportUtils.h" + +#include <QtWidgets/qmessagebox.h> + +using namespace qt3dsdm; + +Qt3DSDMTimelineItemBinding::Qt3DSDMTimelineItemBinding(CTimelineTranslationManager *inMgr, + Qt3DSDMInstanceHandle inDataHandle) + : m_TransMgr(inMgr) + , m_DataHandle(inDataHandle) + , m_Parent(nullptr) + , m_TimelineTimebar(nullptr) + +{ + m_StudioSystem = m_TransMgr->GetStudioSystem(); +} + +Qt3DSDMTimelineItemBinding::Qt3DSDMTimelineItemBinding(CTimelineTranslationManager *inMgr) + : m_TransMgr(inMgr) + , m_DataHandle(0) + , m_Parent(nullptr) + , m_TimelineTimebar(nullptr) +{ + m_StudioSystem = m_TransMgr->GetStudioSystem(); +} + +Qt3DSDMTimelineItemBinding::~Qt3DSDMTimelineItemBinding() +{ + RemoveAllPropertyBindings(); + delete m_TimelineTimebar; +} + +// helpers +bool Qt3DSDMTimelineItemBinding::GetBoolean(qt3dsdm::Qt3DSDMPropertyHandle inProperty) const +{ + qt3dsdm::IPropertySystem *thePropertySystem = m_StudioSystem->GetPropertySystem(); + SValue theValue; + thePropertySystem->GetInstancePropertyValue(m_DataHandle, inProperty, theValue); + return qt3dsdm::get<bool>(theValue); +} + +void Qt3DSDMTimelineItemBinding::SetBoolean(qt3dsdm::Qt3DSDMPropertyHandle inProperty, + bool inValue, const QString &inNiceText) const +{ + CDoc *theDoc = dynamic_cast<CDoc *>(g_StudioApp.GetCore()->GetDoc()); + Q3DStudio::SCOPED_DOCUMENT_EDITOR(*theDoc, inNiceText) + ->SetInstancePropertyValue(m_DataHandle, inProperty, inValue); +} + +void Qt3DSDMTimelineItemBinding::SetInstanceHandle(qt3dsdm::Qt3DSDMInstanceHandle inDataHandle) +{ + m_DataHandle = inDataHandle; +} + +EStudioObjectType Qt3DSDMTimelineItemBinding::GetObjectType() const +{ + return m_StudioSystem->GetClientDataModelBridge()->GetObjectType(m_DataHandle); +} + +bool Qt3DSDMTimelineItemBinding::IsMaster() const +{ + CDoc *theDoc = dynamic_cast<CDoc *>(g_StudioApp.GetCore()->GetDoc()); + Q3DStudio::IDocumentReader &theReader(theDoc->GetDocumentReader()); + if (GetObjectType() == OBJTYPE_IMAGE) { + CClientDataModelBridge *theBridge = m_StudioSystem->GetClientDataModelBridge(); + Qt3DSDMInstanceHandle theParent; + Qt3DSDMPropertyHandle theProperty; + bool isPropertyLinked; + + theBridge->GetMaterialFromImageInstance(GetInstance(), theParent, theProperty); + isPropertyLinked = theReader.IsPropertyLinked(theParent, theProperty); + + // Also check light probe + if (!isPropertyLinked) { + theBridge->GetLayerFromImageProbeInstance(GetInstance(), theParent, theProperty); + isPropertyLinked = theReader.IsPropertyLinked(theParent, theProperty); + } + + return isPropertyLinked; + } + Qt3DSDMInstanceHandle theQueryHandle(m_DataHandle); + if (GetObjectType() == OBJTYPE_PATHANCHORPOINT) + theQueryHandle = theReader.GetParent(m_DataHandle); + + // logic: you can't unlink name, so if name is linked then, this is master. + Qt3DSDMPropertyHandle theNamePropHandle = + m_StudioSystem->GetPropertySystem()->GetAggregateInstancePropertyByName(theQueryHandle, + L"name"); + return theReader.IsPropertyLinked(theQueryHandle, theNamePropHandle); +} + +bool Qt3DSDMTimelineItemBinding::IsShy() const +{ + return GetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Shy); +} +void Qt3DSDMTimelineItemBinding::SetShy(bool inShy) +{ + SetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Shy, inShy, + QObject::tr("Shy Toggle")); +} +bool Qt3DSDMTimelineItemBinding::IsLocked() const +{ + return GetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Locked); +} + +bool Qt3DSDMTimelineItemBinding::IsVisibilityControlled() const +{ + if (!m_StudioSystem->IsInstance(m_DataHandle)) + return false; + + Qt3DSDMPropertyHandle theNamePropHandle = + m_StudioSystem->GetPropertySystem()->GetAggregateInstancePropertyByName( + m_DataHandle, L"controlledproperty"); + + if (!theNamePropHandle) + return false; + SValue theNameValue; + m_StudioSystem->GetPropertySystem()->GetInstancePropertyValue(m_DataHandle, theNamePropHandle, + theNameValue); + TDataStrPtr theName = qt3dsdm::get<TDataStrPtr>(theNameValue); + + return (wcsstr(theName->GetData(), L"eyeball")); +} + +void ToggleChildrenLock(Q3DStudio::ScopedDocumentEditor &scopedDocEditor, + Qt3DSDMTimelineItemBinding *inTimelineItemBinding, + SDataModelSceneAsset inSceneAsset, bool inLocked) +{ + scopedDocEditor->SetInstancePropertyValue(inTimelineItemBinding->GetInstanceHandle(), + inSceneAsset.m_Locked, inLocked); + const QList<ITimelineItemBinding *> children = inTimelineItemBinding->GetChildren(); + for (auto child : children) { + ToggleChildrenLock(scopedDocEditor, static_cast<Qt3DSDMTimelineItemBinding *>(child), + inSceneAsset, inLocked); + } +} + +void Qt3DSDMTimelineItemBinding::SetLocked(bool inLocked) +{ + CDoc *theDoc = dynamic_cast<CDoc *>(g_StudioApp.GetCore()->GetDoc()); + Q3DStudio::ScopedDocumentEditor scopedDocEditor(*theDoc, QObject::tr("SetLock"), __FILE__, + __LINE__); + + SDataModelSceneAsset sceneAsset = m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset(); + ToggleChildrenLock(scopedDocEditor, this, sceneAsset, inLocked); + + if (inLocked) + g_StudioApp.GetCore()->GetDoc()->NotifySelectionChanged(); +} + +bool Qt3DSDMTimelineItemBinding::IsVisible() const +{ + return GetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Eyeball); +} + +void Qt3DSDMTimelineItemBinding::SetVisible(bool inVisible) +{ + SetBoolean(m_StudioSystem->GetClientDataModelBridge()->GetSceneAsset().m_Eyeball, + inVisible, QObject::tr("Visibility Toggle")); +} + +bool Qt3DSDMTimelineItemBinding::HasAction(bool inMaster) +{ + TActionHandleList theActions; + CDoc *theDoc = g_StudioApp.GetCore()->GetDoc(); + + Qt3DSDMSlideHandle theSlide = theDoc->GetActiveSlide(); + qt3dsdm::ISlideCore &theSlideCore(*m_StudioSystem->GetSlideCore()); + if (theSlideCore.IsSlide(theSlide)) { + if (inMaster) { + theSlide = + m_StudioSystem->GetSlideSystem()->GetMasterSlide(theSlide); // use the master slide + } + + m_StudioSystem->GetActionCore()->GetActions(theSlide, m_DataHandle, theActions); + } + return theActions.size() > 0; +} + +bool Qt3DSDMTimelineItemBinding::ChildrenHasAction(bool inMaster) +{ + // Get all the instances in this slidegraph + // check whehter it's an action instance and is in the slide of interst + // check also it's owner is a descendent of the viewed instances + CDoc *theDoc = g_StudioApp.GetCore()->GetDoc(); + IActionCore *theActionCore(m_StudioSystem->GetActionCore()); + CClientDataModelBridge *theBridge(m_StudioSystem->GetClientDataModelBridge()); + + Qt3DSDMSlideHandle theSlide = theDoc->GetActiveSlide(); + qt3dsdm::ISlideCore &theSlideCore(*m_StudioSystem->GetSlideCore()); + if (theSlideCore.IsSlide(theSlide)) { + if (inMaster) { + theSlide = + m_StudioSystem->GetSlideSystem()->GetMasterSlide(theSlide); // use the master slide + } + + TSlideInstancePairList theGraphInstances; + m_StudioSystem->GetSlideSystem()->GetAssociatedInstances(theSlide, theGraphInstances); + + qt3dsdm::Qt3DSDMInstanceHandle theObservedInstance = GetInstance(); + if (theObservedInstance.Valid()) { + for (TSlideInstancePairList::const_iterator theIter = theGraphInstances.begin(); + theIter != theGraphInstances.end(); ++theIter) { + if (theIter->first == theSlide && theBridge->IsActionInstance(theIter->second)) { + Qt3DSDMActionHandle theAction = + theActionCore->GetActionByInstance(theIter->second); + SActionInfo theActionInfo = theActionCore->GetActionInfo(theAction); + Qt3DSDMInstanceHandle theAcionOwner = theActionInfo.m_Owner; + if (theAcionOwner.Valid() + && IsAscendant(theAcionOwner, theObservedInstance, theDoc->GetAssetGraph())) + return true; + } + } + } + } + + return false; +} + +bool Qt3DSDMTimelineItemBinding::ComponentHasAction(bool inMaster) +{ + // Get all the instances in this component slidegraph + // check whether the instance is an action instance + // if inMaster is true, we only interest with those that are in the master slide, else we want + // those that are not in the master slide + CClientDataModelBridge *theBridge(m_StudioSystem->GetClientDataModelBridge()); + if (!theBridge->IsComponentInstance(m_DataHandle)) + return false; + + Q3DStudio::CId theAssetId = theBridge->GetGUID(m_DataHandle); + Qt3DSDMSlideHandle theMasterSlide = + m_StudioSystem->GetSlideSystem()->GetMasterSlideByComponentGuid(GuidtoSLong4(theAssetId)); + + TSlideInstancePairList theGraphInstances; + m_StudioSystem->GetSlideSystem()->GetAssociatedInstances(theMasterSlide, theGraphInstances); + + for (TSlideInstancePairList::const_iterator theIter = theGraphInstances.begin(); + theIter != theGraphInstances.end(); ++theIter) { + if (((inMaster && theIter->first == theMasterSlide) + || (!inMaster && theIter->first != theMasterSlide)) + && theBridge->IsActionInstance(theIter->second)) { + return true; + } + } + return false; +} + +bool Qt3DSDMTimelineItemBinding::hasSubpresentation() const +{ + CClientDataModelBridge *bridge(m_StudioSystem->GetClientDataModelBridge()); + IPropertySystem *propSystem = m_StudioSystem->GetPropertySystem(); + EStudioObjectType objType = GetObjectType(); + + if (objType == OBJTYPE_LAYER) { + SValue sourcePathValue; + propSystem->GetInstancePropertyValue(m_DataHandle, bridge->GetSourcePathProperty(), + sourcePathValue); + return get<TDataStrPtr>(sourcePathValue)->GetLength() > 0; + } else if (objType == OBJTYPE_IMAGE) { + SValue subPresValue; + propSystem->GetInstancePropertyValue(m_DataHandle, + bridge->GetSceneImage().m_SubPresentation, + subPresValue); + return get<TDataStrPtr>(subPresValue)->GetLength() > 0; + } + + return false; +} + +ITimelineTimebar *Qt3DSDMTimelineItemBinding::GetTimebar() +{ + if (!m_TimelineTimebar) + m_TimelineTimebar = CreateTimelineTimebar(); + return m_TimelineTimebar; +} + +Q3DStudio::CString Qt3DSDMTimelineItemBinding::GetName() const +{ + if (m_StudioSystem->IsInstance(m_DataHandle) == false) + return L""; + Qt3DSDMPropertyHandle theNamePropHandle = + m_StudioSystem->GetPropertySystem()->GetAggregateInstancePropertyByName(m_DataHandle, + L"name"); + SValue theNameValue; + m_StudioSystem->GetPropertySystem()->GetInstancePropertyValue(m_DataHandle, theNamePropHandle, + theNameValue); + TDataStrPtr theName = qt3dsdm::get<TDataStrPtr>(theNameValue); + + return (theName) ? Q3DStudio::CString(theName->GetData()) : ""; +} + +void Qt3DSDMTimelineItemBinding::SetName(const Q3DStudio::CString &inName) +{ + // Ignore if setting the name to what it currently is to avoid duplicate undo points + if (inName == GetName()) + return; + + // Display warning dialog if user tried to enter an empty string + if (inName.IsEmpty()) { + QString theTitle = QObject::tr("Rename Object Error"); + QString theString = QObject::tr("Object name cannot be an empty string."); + g_StudioApp.GetDialogs()->DisplayMessageBox(theTitle, theString, + Qt3DSMessageBox::ICON_ERROR, false); + + return; + } + + CClientDataModelBridge *theBridge = m_StudioSystem->GetClientDataModelBridge(); + const auto doc = g_StudioApp.GetCore()->GetDoc(); + + // Display warning if the name and path are the same as the material container + if (theBridge->GetParentInstance(m_DataHandle) == doc->GetSceneInstance() + && inName.toQString() == theBridge->getMaterialContainerName()) { + QString theTitle = QObject::tr("Rename Object Error"); + QString theString = theBridge->getMaterialContainerName() + + QObject::tr(" is a reserved name."); + g_StudioApp.GetDialogs()->DisplayMessageBox(theTitle, theString, + Qt3DSMessageBox::ICON_ERROR, false); + // The timeline still shows the new name so refresh the name property + m_StudioSystem->GetFullSystemSignalSender()->SendInstancePropertyValue( + m_DataHandle, theBridge->GetNameProperty()); + return; + } + + // Display warning if we had to modify the user-given name to make it unique + if (!theBridge->CheckNameUnique(theBridge->GetParentInstance(m_DataHandle), + m_DataHandle, inName)) { + // Find unique name based on the input string + Q3DStudio::SCOPED_DOCUMENT_EDITOR( + *m_TransMgr->GetDoc(), QObject::tr("Set Name"))->SetName(m_DataHandle, inName, true); + + g_StudioApp.GetDialogs()->DisplayObjectRenamed( + inName.toQString(), theBridge->GetName(m_DataHandle).toQString()); + return; + } + + Q3DStudio::SCOPED_DOCUMENT_EDITOR( + *m_TransMgr->GetDoc(), QObject::tr("Set Name"))->SetName(m_DataHandle, inName, true); +} + +ITimelineItem *Qt3DSDMTimelineItemBinding::GetTimelineItem() +{ + return this; +} + +RowTree *Qt3DSDMTimelineItemBinding::getRowTree() const +{ + return m_rowTree; +} + +void Qt3DSDMTimelineItemBinding::setRowTree(RowTree *row) +{ + m_rowTree = row; +} + +void Qt3DSDMTimelineItemBinding::SetSelected(bool inMultiSelect) +{ + if (!inMultiSelect) + g_StudioApp.GetCore()->GetDoc()->SelectDataModelObject(m_DataHandle); + else + g_StudioApp.GetCore()->GetDoc()->ToggleDataModelObjectToSelection(m_DataHandle); +} + +void Qt3DSDMTimelineItemBinding::OnCollapsed() +{ + // Preserves legacy behavior where collapsing a tree will select that root, if any of its + // descendant was selected + // TODO: This won't work for Image (because Image is Material's property, not child) + qt3dsdm::Qt3DSDMInstanceHandle theInstance = GetInstance(); + if (theInstance.Valid()) { + CDoc *theDoc = m_TransMgr->GetDoc(); + qt3dsdm::Qt3DSDMInstanceHandle theSelectedInstance = theDoc->GetSelectedInstance(); + if (theSelectedInstance.Valid() + && IsAscendant(theSelectedInstance, theInstance, theDoc->GetAssetGraph())) + SetSelected(false); + } +} + +bool Qt3DSDMTimelineItemBinding::OpenAssociatedEditor() +{ + return false; // nothing to do by default +} + +inline qt3dsdm::Qt3DSDMInstanceHandle Qt3DSDMTimelineItemBinding::GetInstance() const +{ + return m_DataHandle; +} + +void Qt3DSDMTimelineItemBinding::SetDropTarget(CDropTarget *inTarget) +{ + qt3dsdm::Qt3DSDMInstanceHandle theInstance = GetInstance(); + inTarget->SetInstance(theInstance); +} + +long Qt3DSDMTimelineItemBinding::GetChildrenCount() +{ + qt3dsdm::Qt3DSDMInstanceHandle theInstance = GetInstance(); + if (theInstance.Valid()) { + Q3DStudio::CGraphIterator theChildren; + Qt3DSDMSlideHandle theActiveSlide = m_TransMgr->GetDoc()->GetActiveSlide(); + GetAssetChildrenInTimeParent(theInstance, m_TransMgr->GetDoc(), AmITimeParent(), + theChildren, theActiveSlide); + return (long)theChildren.GetCount(); + } + return 0; +} + +ITimelineItemBinding *Qt3DSDMTimelineItemBinding::GetChild(long inIndex) +{ + qt3dsdm::Qt3DSDMInstanceHandle theInstance = GetInstance(); + if (theInstance.Valid()) { + Q3DStudio::CGraphIterator theChildren; + Qt3DSDMSlideHandle theActiveSlide = m_TransMgr->GetDoc()->GetActiveSlide(); + GetAssetChildrenInTimeParent(theInstance, m_TransMgr->GetDoc(), AmITimeParent(), + theChildren, theActiveSlide); + theChildren += inIndex; + + qt3dsdm::Qt3DSDMInstanceHandle theChildInstance = theChildren.GetCurrent(); + if (theChildInstance.Valid()) + return m_TransMgr->GetOrCreate(theChildInstance); + } + return nullptr; +} + +QList<ITimelineItemBinding *> Qt3DSDMTimelineItemBinding::GetChildren() +{ + QList<ITimelineItemBinding *> retlist; + qt3dsdm::Qt3DSDMInstanceHandle theInstance = GetInstance(); + if (theInstance.Valid()) { + Q3DStudio::CGraphIterator theChildren; + Qt3DSDMSlideHandle theActiveSlide = m_TransMgr->GetDoc()->GetActiveSlide(); + GetAssetChildrenInTimeParent(theInstance, m_TransMgr->GetDoc(), AmITimeParent(), + theChildren, theActiveSlide); + int childCount = int(theChildren.GetCount()); + retlist.reserve(childCount); + + for (int i = 0; i < childCount; ++i) { + qt3dsdm::Qt3DSDMInstanceHandle theChildInstance = theChildren.GetCurrent(); + if (theChildInstance.Valid()) + retlist.append(m_TransMgr->GetOrCreate(theChildInstance)); + ++theChildren; + } + } + + return retlist; +} + +ITimelineItemBinding *Qt3DSDMTimelineItemBinding::GetParent() +{ + return m_Parent; +} +void Qt3DSDMTimelineItemBinding::SetParent(ITimelineItemBinding *parent) +{ + if (parent != m_Parent) { + ASSERT(parent == nullptr || m_Parent == nullptr); + m_Parent = parent; + } +} + +long Qt3DSDMTimelineItemBinding::GetPropertyCount() +{ + long theCount = 0; + if (m_StudioSystem->IsInstance(m_DataHandle)) { + TPropertyHandleList theProperties; + m_StudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(m_DataHandle, + theProperties); + for (size_t thePropertyIndex = 0; thePropertyIndex < theProperties.size(); + ++thePropertyIndex) { + if (m_StudioSystem->GetAnimationSystem()->IsPropertyAnimated( + m_DataHandle, theProperties[thePropertyIndex])) { + ++theCount; + } + } + } + return theCount; +} + +ITimelineItemProperty *Qt3DSDMTimelineItemBinding::GetProperty(long inIndex) +{ + TPropertyHandleList theProperties; + m_StudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(m_DataHandle, + theProperties); + long theIndex = -1; + size_t thePropertyIndex = 0; + for (; thePropertyIndex < theProperties.size(); ++thePropertyIndex) { + if (m_StudioSystem->GetAnimationSystem()->IsPropertyAnimated( + m_DataHandle, theProperties[thePropertyIndex])) { + ++theIndex; + if (theIndex == inIndex) + break; + } + } + ASSERT(thePropertyIndex < theProperties.size()); // no reason why this would be out of range!! + return GetOrCreatePropertyBinding(theProperties[thePropertyIndex]); +} + +bool Qt3DSDMTimelineItemBinding::ShowToggleControls() const +{ + return true; +} + +bool Qt3DSDMTimelineItemBinding::IsLockedEnabled() const +{ + return IsLocked(); +} + +bool Qt3DSDMTimelineItemBinding::IsVisibleEnabled() const +{ + // You can only toggle visible if you aren't on the master slide. + return m_StudioSystem->GetSlideSystem()->GetSlideIndex(m_TransMgr->GetDoc()->GetActiveSlide()) + != 0; +} + +bool Qt3DSDMTimelineItemBinding::IsValidTransaction(EUserTransaction inTransaction) +{ + qt3dsdm::Qt3DSDMInstanceHandle theInstance = GetInstance(); + const auto bridge = m_StudioSystem->GetClientDataModelBridge(); + switch (inTransaction) { + case EUserTransaction_Rename: + return (GetObjectType() != OBJTYPE_SCENE && GetObjectType() != OBJTYPE_IMAGE + && (bridge->GetObjectType(theInstance) != OBJTYPE_REFERENCEDMATERIAL + || bridge->GetSourcePath(theInstance).isEmpty())); + + case EUserTransaction_Duplicate: + if (theInstance.Valid()) + return m_StudioSystem->GetClientDataModelBridge()->IsDuplicateable(theInstance); + break; + + case EUserTransaction_Cut: + return g_StudioApp.CanCut(); + + case EUserTransaction_Copy: + return g_StudioApp.CanCopy(); + + case EUserTransaction_Paste: + return m_TransMgr->GetDoc()->canPasteObjects(); + + case EUserTransaction_Delete: + if (theInstance.Valid()) + return m_StudioSystem->GetClientDataModelBridge()->CanDelete(theInstance); + break; + + case EUserTransaction_MakeComponent: { + bool theCanMakeFlag = false; + if (theInstance.Valid()) { + CClientDataModelBridge *theBridge = m_StudioSystem->GetClientDataModelBridge(); + EStudioObjectType theObjectType = theBridge->GetObjectType(theInstance); + + if (!IsLocked()) { + // Any assets that are attached to the Scene directly must not be wrapped in a + // component. + // This may include behavior assets which may be directly attached to the Scene. + // This is because by principal, components cannot exist on the Scene directly. + qt3dsdm::Qt3DSDMInstanceHandle theParentInstance = + theBridge->GetParentInstance(theInstance); + if (theObjectType != OBJTYPE_LAYER && theObjectType != OBJTYPE_SCENE + && theObjectType != OBJTYPE_MATERIAL && theObjectType != OBJTYPE_IMAGE + && theObjectType != OBJTYPE_EFFECT && theObjectType != OBJTYPE_COMPONENT + && (theParentInstance.Valid() + && theBridge->GetObjectType(theParentInstance) + != OBJTYPE_SCENE)) // This checks if the object is + // AttachedToSceneDirectly + { + theCanMakeFlag = true; + } + } + } + return theCanMakeFlag; + } + + case EUserTransaction_EditComponent: + return (GetObjectType() == OBJTYPE_COMPONENT); + + case EUserTransaction_MakeAnimatable: + if (theInstance.Valid()) { + CClientDataModelBridge *bridge = m_StudioSystem->GetClientDataModelBridge(); + EStudioObjectType type = bridge->GetObjectType(theInstance); + return !IsLocked() && type == OBJTYPE_REFERENCEDMATERIAL; + } + return false; + + case EUserTransaction_Group: + return g_StudioApp.canGroupSelectedObjects(); + + case EUserTransaction_Ungroup: + return g_StudioApp.canUngroupSelectedObjects(); + + case EUserTransaction_AddLayer: + return (GetObjectType() == OBJTYPE_SCENE); + + default: // not handled + break; + } + + return false; +} + +using namespace Q3DStudio; + +inline void DoCut(CDoc &inDoc, const qt3dsdm::TInstanceHandleList &inInstances) +{ + inDoc.DeselectAllKeyframes(); + inDoc.CutObject(inInstances); +} + +inline void DoDelete(CDoc &inDoc, const qt3dsdm::TInstanceHandleList &inInstances) +{ + inDoc.DeselectAllKeyframes(); + inDoc.DeleteObject(inInstances); +} + +inline void DoMakeComponent(CDoc &inDoc, const qt3dsdm::TInstanceHandleList &inInstances) +{ + SCOPED_DOCUMENT_EDITOR(inDoc, QObject::tr("Make Component"))->MakeComponent(inInstances); +} + +inline void doMakeAnimatable(CDoc &doc, const qt3dsdm::TInstanceHandleList &instances) +{ + SCOPED_DOCUMENT_EDITOR(doc, QObject::tr("Make Animatable"))->makeAnimatable(instances); +} + +inline void DoGroupObjects(CDoc &inDoc, const qt3dsdm::TInstanceHandleList &inInstances) +{ + g_StudioApp.groupSelectedObjects(); +} + +inline void DoUngroupObjects(CDoc &inDoc, const qt3dsdm::TInstanceHandleList &inInstances) +{ + g_StudioApp.ungroupSelectedObjects(); +} + +inline void doAddLayer(CDoc &inDoc, const qt3dsdm::TInstanceHandleList &inInstances) +{ + qt3dsdm::Qt3DSDMSlideHandle slide = inDoc.GetActiveSlide(); + qt3dsdm::Qt3DSDMInstanceHandle parent = inDoc.GetActiveLayer(); + + SCOPED_DOCUMENT_EDITOR(inDoc, QObject::tr("Add Layer")) + ->CreateSceneGraphInstance(qt3dsdm::ComposerObjectTypes::Layer, parent, slide, + DocumentEditorInsertType::PreviousSibling, + CPt(), PRIMITIVETYPE_UNKNOWN, -1); +} + +void Qt3DSDMTimelineItemBinding::PerformTransaction(EUserTransaction inTransaction) +{ + CDoc *theDoc = m_TransMgr->GetDoc(); + qt3dsdm::TInstanceHandleList theInstances = theDoc->GetSelectedValue().GetSelectedInstances(); + if (theInstances.empty()) + return; + CDispatch &theDispatch(*theDoc->GetCore()->GetDispatch()); + + // Transactions that could result in *this* object being deleted need to be executed + // via postmessage, not in this context because it could result in the currently + // active timeline row being deleted while in its own mouse handler. + switch (inTransaction) { + case EUserTransaction_Duplicate: { + theDoc->DeselectAllKeyframes(); + SCOPED_DOCUMENT_EDITOR(*theDoc, + QObject::tr("Duplicate Object"))->DuplicateInstances(theInstances); + } break; + case EUserTransaction_Cut: { + theDispatch.FireOnAsynchronousCommand( + std::bind(DoCut, std::ref(*theDoc), theInstances)); + } break; + case EUserTransaction_Copy: { + theDoc->DeselectAllKeyframes(); + theDoc->CopyObject(theInstances); + } break; + case EUserTransaction_Paste: { + theDoc->DeselectAllKeyframes(); + theDoc->PasteObject(theDoc->getPasteTarget(GetInstance())); + } break; + case EUserTransaction_Delete: { + theDispatch.FireOnAsynchronousCommand( + std::bind(DoDelete, std::ref(*theDoc), theInstances)); + } break; + case EUserTransaction_MakeComponent: { + theDispatch.FireOnAsynchronousCommand( + std::bind(DoMakeComponent, std::ref(*theDoc), theInstances)); + } break; + case EUserTransaction_MakeAnimatable: { + theDispatch.FireOnAsynchronousCommand( + std::bind(doMakeAnimatable, std::ref(*theDoc), theInstances)); + } break; + case EUserTransaction_Group: { + theDispatch.FireOnAsynchronousCommand( + std::bind(DoGroupObjects, std::ref(*theDoc), theInstances)); + } break; + case EUserTransaction_Ungroup: { + theDispatch.FireOnAsynchronousCommand( + std::bind(DoUngroupObjects, std::ref(*theDoc), theInstances)); + } break; + case EUserTransaction_AddLayer: { + theDispatch.FireOnAsynchronousCommand( + std::bind(doAddLayer, std::ref(*theDoc), theInstances)); + } break; + default: // not handled + break; + } +} + +Q3DStudio::CString Qt3DSDMTimelineItemBinding::GetObjectPath() +{ + CDoc *theDoc = m_TransMgr->GetDoc(); + // Because we are getting absolute path, the base id doesn't matter. + return CRelativePathTools::BuildAbsoluteReferenceString(m_DataHandle, theDoc); +} + +int Qt3DSDMTimelineItemBinding::getAnimatedPropertyIndex(int propertyHandle) const +{ + TPropertyHandleList theProperties; + m_StudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(m_DataHandle, + theProperties); + int index = -1; + for (size_t i = 0; i < theProperties.size(); ++i) { + if (m_StudioSystem->GetAnimationSystem()->IsPropertyAnimated( + m_DataHandle, theProperties[i])) { + index++; + } + if (theProperties[i].GetHandleValue() == propertyHandle) + return index; + } + + return -1; +} + +void Qt3DSDMTimelineItemBinding::getTimeContextIndices(const QSet<int> &children, + QMap<int, int> &indexMap) +{ + qt3dsdm::Qt3DSDMInstanceHandle instance = GetInstance(); + if (instance.Valid()) { + Q3DStudio::CGraphIterator graphChildren; + Qt3DSDMSlideHandle activeSlide = m_TransMgr->GetDoc()->GetActiveSlide(); + GetAssetChildrenInTimeParent(instance, m_TransMgr->GetDoc(), AmITimeParent(), + graphChildren, activeSlide); + const size_t count = graphChildren.GetCount(); + for (size_t current = 0; current < count; ++current) { + auto handle = graphChildren.GetResult(current); + if (children.contains(handle)) + indexMap.insert(int(current), int(handle)); + } + } +} + +void Qt3DSDMTimelineItemBinding::InsertKeyframe() +{ + if (m_PropertyBindingMap.empty()) + return; + + TPropertyBindingMap::const_iterator theIter = m_PropertyBindingMap.begin(); + ScopedDocumentEditor editor(*g_StudioApp.GetCore()->GetDoc(), QObject::tr("Insert Keyframe"), + __FILE__, __LINE__); + for (; theIter != m_PropertyBindingMap.end(); ++theIter) + editor->KeyframeProperty(m_DataHandle, theIter->first, false); +} + +void Qt3DSDMTimelineItemBinding::DeleteAllChannelKeyframes() +{ + if (m_PropertyBindingMap.empty()) + return; + + CDoc *theDoc = m_TransMgr->GetDoc(); + Q3DStudio::ScopedDocumentEditor editor(*theDoc, QObject::tr("Delete Channel Keyframes"), + __FILE__, __LINE__); + for (auto &kv : m_PropertyBindingMap) + kv.second->DeleteAllKeys(); +} + +IKeyframe *Qt3DSDMTimelineItemBinding::GetKeyframeByTime(long inTime) const +{ + TAssetKeyframeList::const_iterator theIter = m_Keyframes.begin(); + for (; theIter != m_Keyframes.end(); ++theIter) { + if ((*theIter).GetTime() == inTime) + return const_cast<Qt3DSDMAssetTimelineKeyframe *>(&(*theIter)); + } + return nullptr; +} + +Qt3DSDMInstanceHandle Qt3DSDMTimelineItemBinding::GetInstanceHandle() const +{ + return m_DataHandle; +} + +long Qt3DSDMTimelineItemBinding::GetFlavor() const +{ + return QT3DS_FLAVOR_ASSET_TL; +} + +ITimelineTimebar *Qt3DSDMTimelineItemBinding::CreateTimelineTimebar() +{ + return new Qt3DSDMTimelineTimebar(m_TransMgr, m_DataHandle); +} + +ITimelineItemProperty * +Qt3DSDMTimelineItemBinding::GetPropertyBinding(Qt3DSDMPropertyHandle inPropertyHandle) +{ + TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.find(inPropertyHandle); + // check if it already exists + if (theIter != m_PropertyBindingMap.end()) + return theIter->second; + return nullptr; +} + +bool Qt3DSDMTimelineItemBinding::isRootComponent() const +{ + auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge(); + return bridge->IsActiveComponent(m_DataHandle); +} + +bool Qt3DSDMTimelineItemBinding::isDefaultMaterial() const +{ + auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge(); + return bridge->isDefaultMaterial(m_DataHandle); +} + +ITimelineItemProperty * +Qt3DSDMTimelineItemBinding::GetOrCreatePropertyBinding(Qt3DSDMPropertyHandle inPropertyHandle) +{ + ITimelineItemProperty *theProperty = GetPropertyBinding(inPropertyHandle); + // check if it already exists + if (theProperty) + return theProperty; + + // Create + Qt3DSDMTimelineItemProperty *theTimelineProperty = + new Qt3DSDMTimelineItemProperty(m_TransMgr, inPropertyHandle, m_DataHandle); + m_PropertyBindingMap.insert(std::make_pair(inPropertyHandle, theTimelineProperty)); + + return theTimelineProperty; +} + +//============================================================================= +/** + * Add a new property row for this property. + * @param inAppend true to skip the check to find where to insert. ( true if this is a + * loading/initializing step, where the call is already done in order ) + */ +void Qt3DSDMTimelineItemBinding::AddPropertyRow(Qt3DSDMPropertyHandle inPropertyHandle, + bool inAppend /*= false */) +{ + ITimelineItemProperty *theTimelineProperty = GetPropertyBinding(inPropertyHandle); + if (theTimelineProperty) // if created, bail + return; + + if (!theTimelineProperty) + theTimelineProperty = GetOrCreatePropertyBinding(inPropertyHandle); + + // Find the row to insert this new property, if any, this preserves the order the property rows + // is displayed in the timeline. + ITimelineItemProperty *theNextProperty = nullptr; + if (!inAppend) { + TPropertyHandleList theProperties; + m_StudioSystem->GetPropertySystem()->GetAggregateInstanceProperties(m_DataHandle, + theProperties); + size_t thePropertyIndex = 0; + size_t thePropertyCount = theProperties.size(); + for (; thePropertyIndex < thePropertyCount; ++thePropertyIndex) { + if (theProperties[thePropertyIndex] == inPropertyHandle) { + ++thePropertyIndex; + break; + } + } + // Not all properties are displayed, so another loop to search for the first one that maps + // to a existing propertyrow + for (; thePropertyIndex < thePropertyCount; ++thePropertyIndex) { + TPropertyBindingMap::iterator theNextPropIter = + m_PropertyBindingMap.find(theProperties[thePropertyIndex]); + if (theNextPropIter != m_PropertyBindingMap.end()) { + theNextProperty = theNextPropIter->second; + break; + } + } + } + + // Update keyframes + AddKeyframes(theTimelineProperty); +} + +void Qt3DSDMTimelineItemBinding::RemovePropertyRow(Qt3DSDMPropertyHandle inPropertyHandle) +{ + TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.find(inPropertyHandle); + if (theIter != m_PropertyBindingMap.end()) { + ITimelineItemProperty *thePropertyBinding = theIter->second; + + DeleteAssetKeyframesWhereApplicable(thePropertyBinding); + m_PropertyBindingMap.erase(theIter); + } +} + +// called when a keyframe is inserted, deleted or updated in the data model +void Qt3DSDMTimelineItemBinding::RefreshPropertyKeyframe( + qt3dsdm::Qt3DSDMPropertyHandle inPropertyHandle, qt3dsdm::Qt3DSDMKeyframeHandle inKeyframe, + ETimelineKeyframeTransaction inTransaction) +{ + TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.find(inPropertyHandle); + if (theIter != m_PropertyBindingMap.end()) { + Qt3DSDMTimelineItemProperty *theProperty = theIter->second; + if (theProperty) { + if (theProperty->RefreshKeyframe(inKeyframe, inTransaction)) { + // Update asset keyframes + UpdateKeyframe(theProperty->GetKeyframeByHandle(inKeyframe), inTransaction); + } + } + } +} + +void Qt3DSDMTimelineItemBinding::OnPropertyChanged(Qt3DSDMPropertyHandle inPropertyHandle) +{ +} + +void Qt3DSDMTimelineItemBinding::OnPropertyLinked(Qt3DSDMPropertyHandle inPropertyHandle) +{ + if (m_StudioSystem->GetAnimationSystem()->IsPropertyAnimated(m_DataHandle, inPropertyHandle)) { + // Refresh property row by delete and recreate + RemovePropertyRow(inPropertyHandle); + AddPropertyRow(inPropertyHandle); + } +} + +bool Qt3DSDMTimelineItemBinding::HasDynamicKeyframes(long inTime) +{ + if (inTime == -1) { + if (GetPropertyCount() == 0) + return false; + + for (long i = 0; i < GetPropertyCount(); ++i) { + ITimelineItemProperty *theTimelineItemProperty = GetProperty(i); + if (!theTimelineItemProperty->IsDynamicAnimation()) + return false; + } + return true; + } else { + TPropertyBindingMap::const_iterator theIter = m_PropertyBindingMap.begin(); + for (; theIter != m_PropertyBindingMap.end(); ++theIter) { + IKeyframe *theKeyframe = theIter->second->GetKeyframeByTime(inTime); + if (theKeyframe && theKeyframe->IsDynamic()) + return true; + } + } + return false; +} + +void Qt3DSDMTimelineItemBinding::SetDynamicKeyframes(long inTime, bool inDynamic) +{ + TPropertyBindingMap::const_iterator theIter = m_PropertyBindingMap.begin(); + for (; theIter != m_PropertyBindingMap.end(); ++theIter) { + IKeyframe *theKeyframe = theIter->second->GetKeyframeByTime(inTime); + if (theKeyframe) + theKeyframe->SetDynamic(inDynamic); // TODO: we want this in 1 batch command + } +} + +Q3DStudio::CId Qt3DSDMTimelineItemBinding::GetGuid() const +{ + CClientDataModelBridge *theClientBridge = m_StudioSystem->GetClientDataModelBridge(); + qt3dsdm::IPropertySystem *thePropertySystem = m_StudioSystem->GetPropertySystem(); + SValue theValue; + if (thePropertySystem->GetInstancePropertyValue(m_DataHandle, theClientBridge->GetIdProperty(), + theValue)) { + SLong4 theLong4 = qt3dsdm::get<SLong4>(theValue); + return Q3DStudio::CId(theLong4.m_Longs[0], theLong4.m_Longs[1], theLong4.m_Longs[2], + theLong4.m_Longs[3]); + } + return Q3DStudio::CId(); +} + +// Delete asset keyframes at time t if no property keyframes exist at time t +//@param inSkipPropertyBinding property that to skip, e.g. in cases where property is deleted +//@return true if there are asset keyframes deleted. +bool Qt3DSDMTimelineItemBinding::DeleteAssetKeyframesWhereApplicable( + ITimelineItemProperty *inSkipPropertyBinding /*= nullptr */) +{ + // iterate through m_Keyframes because we cannot obtain time information from the Animation + // keyframes anymore, since they are deleted. + std::vector<long> theDeleteIndicesList; + for (size_t theIndex = 0; theIndex < m_Keyframes.size(); ++theIndex) { + TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.begin(); + for (; theIter != m_PropertyBindingMap.end(); ++theIter) { + if ((!inSkipPropertyBinding || theIter->second != inSkipPropertyBinding) + && theIter->second->GetKeyframeByTime(m_Keyframes[theIndex].GetTime())) { + // done! + break; + } + } + if (theIter == m_PropertyBindingMap.end()) + theDeleteIndicesList.push_back((long)theIndex); + } + // start with the last item, so that the indices remain valid. + for (long i = (long)theDeleteIndicesList.size() - 1; i >= 0; --i) { + TAssetKeyframeList::iterator theKeyIter = m_Keyframes.begin(); + std::advance(theKeyIter, theDeleteIndicesList[i]); + m_Keyframes.erase(theKeyIter); + } + + return !theDeleteIndicesList.empty(); +} + +void Qt3DSDMTimelineItemBinding::RemoveAllPropertyBindings() +{ + TPropertyBindingMap::iterator theIter = m_PropertyBindingMap.begin(); + for (; theIter != m_PropertyBindingMap.end(); ++theIter) + delete theIter->second; + m_PropertyBindingMap.clear(); +} + +void Qt3DSDMTimelineItemBinding::AddKeyframes(ITimelineItemProperty *inPropertyBinding) +{ + for (long i = 0; i < inPropertyBinding->GetKeyframeCount(); ++i) + UpdateKeyframe(inPropertyBinding->GetKeyframeByIndex(i), ETimelineKeyframeTransaction_Add); +} + +// Update the asset keyframes based on the properties' keyframes. +void Qt3DSDMTimelineItemBinding::UpdateKeyframe(IKeyframe *inKeyframe, + ETimelineKeyframeTransaction inTransaction) +{ + bool theDoAddFlag = (inTransaction == ETimelineKeyframeTransaction_Add); + bool theDoDeleteFlag = (inTransaction == ETimelineKeyframeTransaction_Delete); + + // For update, if there isn't already a asset keyframe at the associated time, create one + if (inTransaction == ETimelineKeyframeTransaction_Update) { + theDoAddFlag = inKeyframe && !GetKeyframeByTime(inKeyframe->GetTime()); + theDoDeleteFlag = true; // plus, since we don't keep track of indiviual property keyframes + // here, iterate and make sure list is correct. + } + + if (theDoDeleteFlag) + DeleteAssetKeyframesWhereApplicable(); + + // Add when a new keyframe is added or MAYBE when a keyframe is moved + if (theDoAddFlag && inKeyframe) { + long theKeyframeTime = inKeyframe->GetTime(); + if (theKeyframeTime >= 0) { + bool theAppend = true; + // insert this in the order that it should be. and we trust the + TAssetKeyframeList::iterator theIter = m_Keyframes.begin(); + for (; theIter != m_Keyframes.end(); ++theIter) { + long theTime = (*theIter).GetTime(); + if (theTime == theKeyframeTime) { + theAppend = false; + break; // already exists, we are done. Because we only need 1 to represent ALL + // properties + } + } + if (theAppend) + m_Keyframes.push_back(Qt3DSDMAssetTimelineKeyframe(this, theKeyframeTime)); + } + } +} + +void Qt3DSDMTimelineItemBinding::OnAddChild(Qt3DSDMInstanceHandle inInstance) +{ + CDoc *theDoc = m_TransMgr->GetDoc(); + CClientDataModelBridge *theBridge = m_StudioSystem->GetClientDataModelBridge(); + ISlideSystem *theSlideSystem = m_StudioSystem->GetSlideSystem(); + + qt3dsdm::Qt3DSDMSlideHandle theSlide = theSlideSystem->GetAssociatedSlide(inInstance); + if (theBridge->IsInActiveComponent(inInstance) + && (theSlideSystem->IsMasterSlide(theSlide) || theSlide == theDoc->GetActiveSlide())) { + // Only add if the asset is in the current active component, and it's a master asset or in + // the current slide + ITimelineItemBinding *theNextItem = nullptr; + qt3dsdm::Qt3DSDMInstanceHandle theParentInstance = GetInstance(); + // Figure out where to insert this row, if applicable. + // CAsset has a list of children, and not necessarily all are active in this slide (e.g. + // non-master children) + Q3DStudio::TIdentifier theNextChild = 0; + if (theParentInstance.Valid()) { + // Get the next prioritized child in the same slide + Q3DStudio::CGraphIterator theChildren; + GetAssetChildrenInSlide(theDoc, theParentInstance, theDoc->GetActiveSlide(), + theChildren); + theNextChild = GetSibling(inInstance, true, theChildren); + } + + if (theNextChild != 0) + theNextItem = m_TransMgr->GetOrCreate(theNextChild); + } +} + +void Qt3DSDMTimelineItemBinding::OnDeleteChild(Qt3DSDMInstanceHandle inInstance) +{ +} + +void Qt3DSDMTimelineItemBinding::UpdateActionStatus() +{ +} + +//============================================================================= +/** + * Open the associated item as though it was double-clicked in explorer + * Respective subclasses (for example Image and Behavior) can call this function + */ +bool Qt3DSDMTimelineItemBinding::OpenSourcePathFile() +{ + // Get source path property value + CClientDataModelBridge *theClientBridge = m_StudioSystem->GetClientDataModelBridge(); + qt3dsdm::IPropertySystem *thePropertySystem = m_StudioSystem->GetPropertySystem(); + SValue theValue; + if (thePropertySystem->GetInstancePropertyValue( + m_DataHandle, theClientBridge->GetSourcePathProperty(), theValue)) { + // Open the respective file + Q3DStudio::CFilePath theSourcePath(qt3dsdm::get<qt3dsdm::TDataStrPtr>(theValue)->GetData()); + Qt3DSFile theFile(m_TransMgr->GetDoc()->GetResolvedPathToDoc(theSourcePath)); + theFile.Execute(); + return true; + } + return false; +} |