summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp')
-rw-r--r--src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp1337
1 files changed, 1337 insertions, 0 deletions
diff --git a/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp
new file mode 100644
index 00000000..e15258cd
--- /dev/null
+++ b/src/Authoring/Qt3DStudio/Palettes/TimelineGraphicsView/ui/RowTree.cpp
@@ -0,0 +1,1337 @@
+/****************************************************************************
+**
+** Copyright (C) 2018 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 "RowTree.h"
+#include "RowTimeline.h"
+#include "RowManager.h"
+#include "TimelineConstants.h"
+#include "StudioObjectTypes.h"
+#include "TimelineGraphicsScene.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "Bindings/Qt3DSDMTimelineItemBinding.h"
+#include "Qt3DSString.h"
+#include "TreeHeader.h"
+#include "StudioPreferences.h"
+#include "KeyframeManager.h"
+#include "StudioApp.h"
+#include "MainFrm.h"
+#include "Core.h"
+#include "Doc.h"
+#include "ClientDataModelBridge.h"
+#include "Qt3DSDMStudioSystem.h"
+#include "Qt3DSDMSlides.h"
+#include "StudioUtils.h"
+#include "TimelineToolbar.h"
+
+#include <QtGui/qpainter.h>
+#include "QtGui/qtextcursor.h"
+#include <QtWidgets/qgraphicslinearlayout.h>
+#include <QtWidgets/qgraphicssceneevent.h>
+
+// object row constructor
+RowTree::RowTree(TimelineGraphicsScene *timelineScene, EStudioObjectType objType,
+ const QString &label)
+ : m_rowTimeline(new RowTimeline())
+ , m_scene(timelineScene)
+ , m_objectType(objType)
+ , m_label(label)
+{
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ m_onMasterSlide = doc->GetStudioSystem()->GetSlideSystem()
+ ->IsMasterSlide(doc->GetActiveSlide());
+
+ initialize();
+}
+
+// property row constructor
+RowTree::RowTree(TimelineGraphicsScene *timelineScene, const QString &propType)
+ : InteractiveTimelineItem()
+ , m_rowTimeline(new RowTimeline())
+ , m_isProperty(true)
+ , m_scene(timelineScene)
+ , m_propertyType(propType)
+ , m_label(propType)
+{
+ m_rowTimeline->m_isProperty = true;
+
+ initialize();
+}
+
+RowTree::~RowTree()
+{
+ delete m_rowTimeline; // this will also delete the keyframes
+ m_rowTimeline = nullptr;
+}
+
+ITimelineItemBinding *RowTree::getBinding() const
+{
+ return m_binding;
+}
+
+// object instance handle
+ qt3dsdm::Qt3DSDMInstanceHandle RowTree::instance() const
+{
+ if (m_isProperty || !m_binding)
+ return 0;
+
+ return static_cast<Qt3DSDMTimelineItemBinding *>(m_binding)->GetInstance();
+}
+
+void RowTree::initialize()
+{
+ setTimelineRow(m_rowTimeline);
+ m_rowTimeline->setRowTree(this);
+
+ setMinimumWidth(TimelineConstants::TREE_BOUND_W);
+
+ initializeAnimations();
+
+ m_labelItem.setParentItem(this);
+ m_labelItem.setParentRow(this);
+ m_labelItem.setLabel(m_label);
+ updateLabelPosition();
+
+ // Default all rows to collapsed
+ setRowVisible(false);
+ m_expandState = ExpandState::HiddenCollapsed;
+
+ connect(&m_labelItem, &RowTreeLabelItem::labelChanged, this,
+ [this](const QString &label) {
+ // Update label on timeline and on model
+ m_label = label;
+ // TODO: Get rid of CString APIs
+ auto clabel = Q3DStudio::CString::fromQString(m_label);
+ m_binding->GetTimelineItem()->SetName(clabel);
+ });
+}
+
+void RowTree::initializeAnimations()
+{
+ // Init left side expand animations
+ m_expandHeightAnimation = new QPropertyAnimation(this, "maximumSize");
+ m_expandHeightAnimation->setDuration(TimelineConstants::EXPAND_ANIMATION_DURATION);
+ m_expandAnimation.addAnimation(m_expandHeightAnimation);
+ m_expandOpacityAnimation = new QPropertyAnimation(this, "opacity");
+ m_expandOpacityAnimation->setDuration(TimelineConstants::EXPAND_ANIMATION_DURATION / 3);
+ m_expandAnimation.addAnimation(m_expandOpacityAnimation);
+
+ // Init right side expand animations
+ m_expandTimelineHeightAnimation = new QPropertyAnimation(m_rowTimeline, "maximumSize");
+ m_expandTimelineHeightAnimation->setDuration(TimelineConstants::EXPAND_ANIMATION_DURATION);
+ m_expandAnimation.addAnimation(m_expandTimelineHeightAnimation);
+ m_expandTimelineOpacityAnimation = new QPropertyAnimation(m_rowTimeline, "opacity");
+ m_expandTimelineOpacityAnimation->setDuration(TimelineConstants::EXPAND_ANIMATION_DURATION / 3);
+ m_expandAnimation.addAnimation(m_expandTimelineOpacityAnimation);
+
+ connect(&m_expandAnimation, &QAbstractAnimation::stateChanged,
+ [this](const QAbstractAnimation::State newState) {
+ if (m_rowTimeline) {
+ if (newState == QAbstractAnimation::Running) {
+ setVisible(true);
+ m_rowTimeline->setVisible(true);
+ } else if (newState == QAbstractAnimation::Stopped) {
+ if (this->maximumHeight() == 0) {
+ setVisible(false);
+ m_rowTimeline->setVisible(false);
+ }
+ }
+ }
+ });
+}
+
+void RowTree::animateExpand(ExpandState state)
+{
+ int endHeight = 0; // hidden states
+ float endOpacity = 0;
+ if (state == ExpandState::Expanded) {
+ endHeight = m_isPropertyExpanded ? TimelineConstants::ROW_H_EXPANDED
+ : TimelineConstants::ROW_H;
+ endOpacity = 1;
+ } else if (state == ExpandState::Collapsed) {
+ endHeight = TimelineConstants::ROW_H;
+ endOpacity = 1;
+ }
+ // Changing end values while animation is running does not affect currently running animation,
+ // so let's make sure the animation is stopped first.
+ m_expandAnimation.stop();
+
+ m_expandHeightAnimation->setEndValue(QSizeF(size().width(), endHeight));
+ m_expandTimelineHeightAnimation->setEndValue(QSizeF(m_rowTimeline->size().width(),
+ endHeight));
+ m_expandOpacityAnimation->setEndValue(endOpacity);
+ m_expandTimelineOpacityAnimation->setEndValue(endOpacity);
+
+ m_expandAnimation.start();
+}
+
+void RowTree::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
+{
+ Q_UNUSED(option)
+ Q_UNUSED(widget)
+
+ bool hiResIcons = StudioUtils::devicePixelRatio(widget->window()->windowHandle()) > 1.0;
+
+ if (!y()) // prevents flickering when the row is just inserted to the layout
+ return;
+
+ static const int ICON_SIZE = 16;
+ static const int LEFT_DIVIDER = 18;
+ const int offset = 5 + m_depth * TimelineConstants::ROW_DEPTH_STEP;
+ const int iconY = (TimelineConstants::ROW_H / 2) - (ICON_SIZE / 2);
+
+ // update button bounds rects
+ m_rectArrow .setRect(offset, iconY, ICON_SIZE, ICON_SIZE);
+ m_rectType .setRect(offset + ICON_SIZE, iconY, ICON_SIZE, ICON_SIZE);
+ m_rectShy .setRect(treeWidth() - 16 * 3.3, iconY, ICON_SIZE, ICON_SIZE);
+ m_rectVisible.setRect(treeWidth() - 16 * 2.2, iconY, ICON_SIZE, ICON_SIZE);
+ m_rectLocked .setRect(treeWidth() - 16 * 1.1, iconY, ICON_SIZE, ICON_SIZE);
+
+ // Background
+ QColor bgColor;
+ if (m_dndState == DnDState::Source)
+ bgColor = CStudioPreferences::timelineRowColorDndSource();
+ else if (m_dndState == DnDState::SP_TARGET)
+ bgColor = CStudioPreferences::timelineRowColorDndTargetSP();
+ else if (m_isProperty)
+ bgColor = CStudioPreferences::timelineRowColorNormalProp();
+ else if (m_dndHover)
+ bgColor = CStudioPreferences::timelineRowColorDndTarget();
+ else if (m_state == Selected)
+ bgColor = CStudioPreferences::timelineRowColorSelected();
+ else if (m_state == Hovered && !m_locked)
+ bgColor = CStudioPreferences::timelineRowColorOver();
+ else
+ bgColor = CStudioPreferences::timelineRowColorNormal();
+
+ painter->fillRect(QRect(0, 0, size().width(), size().height() - 1), bgColor);
+
+ // left divider
+ painter->setPen(CStudioPreferences::timelineWidgetBgColor());
+ painter->drawLine(LEFT_DIVIDER, 0, LEFT_DIVIDER, size().height() - 1);
+
+ // Shy, eye, lock separator
+ painter->fillRect(QRect(treeWidth() - TimelineConstants::TREE_ICONS_W,
+ 0, 1, size().height()),
+ CStudioPreferences::timelineWidgetBgColor());
+
+ // Shy, eye, lock
+ static const QPixmap pixEmpty = QPixmap(":/images/Toggle-Empty.png");
+ static const QPixmap pixShy = QPixmap(":/images/Toggle-Shy.png");
+ static const QPixmap pixHide = QPixmap(":/images/Toggle-HideShow.png");
+ static const QPixmap pixHideDisabled = QPixmap(":/images/Toggle-HideShow-disabled.png");
+ static const QPixmap pixHideCtrld = QPixmap(":/images/Toggle-HideShowControlled.png");
+ static const QPixmap pixLock = QPixmap(":/images/Toggle-Lock.png");
+ static const QPixmap pixEmpty2x = QPixmap(":/images/Toggle-Empty@2x.png");
+ static const QPixmap pixShy2x = QPixmap(":/images/Toggle-Shy@2x.png");
+ static const QPixmap pixHide2x = QPixmap(":/images/Toggle-HideShow@2x.png");
+ static const QPixmap pixHideDisabled2x = QPixmap(":/images/Toggle-HideShow-disabled@2x.png");
+ static const QPixmap pixHideCtrld2x = QPixmap(":/images/Toggle-HideShowControlled@2x.png");
+ static const QPixmap pixLock2x = QPixmap(":/images/Toggle-Lock@2x.png");
+ if (hasActionButtons()) {
+ painter->drawPixmap(m_rectShy, hiResIcons ? (m_shy ? pixShy2x : pixEmpty2x)
+ : (m_shy ? pixShy : pixEmpty));
+ // Eyeball visibility follows the visibility setting for the object even if it has
+ // datainput controller
+ // Disable eyeball from master slide
+ if (m_onMasterSlide) {
+ painter->drawPixmap(m_rectVisible, hiResIcons ? pixHideDisabled2x
+ : pixHideDisabled);
+ } else if (m_visibilityCtrld) {
+ painter->drawPixmap(m_rectVisible, hiResIcons
+ ? (m_visible ? pixHideCtrld2x : pixEmpty2x)
+ : (m_visible ? pixHideCtrld : pixEmpty));
+ } else {
+ painter->drawPixmap(m_rectVisible, hiResIcons
+ ? (m_visible ? pixHide2x : pixEmpty2x)
+ : (m_visible ? pixHide : pixEmpty));
+ }
+ painter->drawPixmap(m_rectLocked, hiResIcons ? (m_locked ? pixLock2x : pixEmpty2x)
+ : (m_locked ? pixLock : pixEmpty));
+ }
+
+ static const QPixmap pixInsertLeft = QPixmap(":/images/Insert-Left.png");
+ static const QPixmap pixInsertRight = QPixmap(":/images/Insert-Right.png");
+ static const QPixmap pixInsertLeft2x = QPixmap(":/images/Insert-Left@2x.png");
+ static const QPixmap pixInsertRight2x = QPixmap(":/images/Insert-Right@2x.png");
+ if (m_dndState == DnDState::SP_TARGET) { // Candidate target of a subpresentation drop
+ painter->drawPixmap(19, 2, hiResIcons ? pixInsertLeft2x : pixInsertLeft);
+ painter->drawPixmap(treeWidth() - TimelineConstants::TREE_ICONS_W - 8, 2, hiResIcons
+ ? pixInsertRight2x : pixInsertRight);
+ } else if (m_dndState == DnDState::Parent) { // Candidate parent of a dragged row
+ painter->setPen(QPen(CStudioPreferences::timelineRowMoverColor(), 1));
+ painter->drawRect(QRect(1, 1, treeWidth() - 2, size().height() - 3));
+ }
+
+ // Action indicators
+ static const QPixmap pixMasterAction = QPixmap(":/images/Action-MasterAction.png");
+ static const QPixmap pixAction = QPixmap(":/images/Action-Action.png");
+ static const QPixmap pixChildMasterAction = QPixmap(":/images/Action-ChildMasterAction.png");
+ static const QPixmap pixChildAction = QPixmap(":/images/Action-ChildAction.png");
+ static const QPixmap pixCompMasterAction = QPixmap(":/images/Action-ComponentMasterAction.png");
+ static const QPixmap pixCompAction = QPixmap(":/images/Action-ComponentAction.png");
+ static const QPixmap pixMasterAction2x = QPixmap(":/images/Action-MasterAction@2x.png");
+ static const QPixmap pixAction2x = QPixmap(":/images/Action-Action@2x.png");
+ static const QPixmap pixChildMasterAction2x
+ = QPixmap(":/images/Action-ChildMasterAction@2x.png");
+ static const QPixmap pixChildAction2x = QPixmap(":/images/Action-ChildAction@2x.png");
+ static const QPixmap pixCompMasterAction2x
+ = QPixmap(":/images/Action-ComponentMasterAction@2x.png");
+ static const QPixmap pixCompAction2x = QPixmap(":/images/Action-ComponentAction@2x.png");
+
+ if (!isProperty()) {
+ // subpresentation indicators
+ if (m_hasSubpresentation) {
+ painter->fillRect(QRect(0, 0, LEFT_DIVIDER, size().height() - 1),
+ CStudioPreferences::timelineRowSubpColor());
+ } else if (!expanded() && m_numDescendantSubpresentations > 0) {
+ painter->fillRect(QRect(0, 0, LEFT_DIVIDER, size().height() - 1),
+ CStudioPreferences::timelineRowSubpDescendantColor());
+ }
+
+ if (m_actionStates & ActionState::MasterAction) // has master action
+ painter->drawPixmap(0, 0, hiResIcons ? pixMasterAction2x : pixMasterAction);
+ else if (m_actionStates & ActionState::Action) // has action
+ painter->drawPixmap(0, 0, hiResIcons ? pixAction2x : pixAction);
+
+ if (!expanded()) {
+ if (m_actionStates & ActionState::MasterChildAction) {
+ // children have master action
+ painter->drawPixmap(0, 0, hiResIcons ? pixChildMasterAction2x
+ : pixChildMasterAction);
+ } else if (m_actionStates & ActionState::ChildAction) {
+ // children have action
+ painter->drawPixmap(0, 0, hiResIcons ? pixChildAction2x : pixChildAction);
+ }
+ }
+
+ if (m_actionStates & ActionState::MasterComponentAction) // component has master action
+ painter->drawPixmap(0, 0, hiResIcons ? pixCompMasterAction2x : pixCompMasterAction);
+ else if (m_actionStates & ActionState::ComponentAction) // component has action
+ painter->drawPixmap(0, 0, hiResIcons ? pixCompAction2x : pixCompAction);
+ }
+
+ // variants indicator
+ if (m_variantsGroups.size() > 0) {
+ const auto variantsDef = g_StudioApp.GetCore()->getProjectFile().variantsDef();
+ for (int i = 0; i < m_variantsGroups.size(); ++i) {
+ painter->fillRect(QRect(clipX() + 2 + i * 8, 6, 6, 6),
+ variantsDef[m_variantsGroups[i]].m_color);
+ painter->setPen(CStudioPreferences::timelineWidgetBgColor());
+ painter->drawRect(QRect(clipX() + 2 + i * 8, 6, 6, 6));
+ }
+ }
+
+ // The following items need to be clipped so that they do not draw overlapping shy etc. buttons
+
+ painter->setClipRect(0, 0, clipX(), TimelineConstants::ROW_H);
+
+ // expand/collapse arrow
+ static const QPixmap pixArrow = QPixmap(":/images/arrow.png");
+ static const QPixmap pixArrowDown = QPixmap(":/images/arrow_down.png");
+ static const QPixmap pixArrow2x = QPixmap(":/images/arrow@2x.png");
+ static const QPixmap pixArrowDown2x = QPixmap(":/images/arrow_down@2x.png");
+ if (m_arrowVisible) {
+ painter->drawPixmap(m_rectArrow, hiResIcons ? (expanded() ? pixArrowDown2x : pixArrow2x)
+ : (expanded() ? pixArrowDown : pixArrow));
+ }
+
+ // Row type icon
+ static const QPixmap pixSceneNormal = QPixmap(":/images/Objects-Scene-Normal.png");
+ static const QPixmap pixLayerNormal = QPixmap(":/images/Objects-Layer-Normal.png");
+ static const QPixmap pixObjectNormal = QPixmap(":/images/Objects-Model-Normal.png");
+ static const QPixmap pixLightNormal = QPixmap(":/images/Objects-Light-Normal.png");
+ static const QPixmap pixCameraNormal = QPixmap(":/images/Objects-Camera-Normal.png");
+ static const QPixmap pixTextNormal = QPixmap(":/images/Objects-Text-Normal.png");
+ static const QPixmap pixAliasNormal = QPixmap(":/images/Objects-Alias-Normal.png");
+ static const QPixmap pixGroupNormal = QPixmap(":/images/Objects-Group-Normal.png");
+ static const QPixmap pixComponentNormal = QPixmap(":/images/Objects-Component-Normal.png");
+ static const QPixmap pixMaterialNormal = QPixmap(":/images/Objects-Material-Normal.png");
+ static const QPixmap pixPropertyNormal = QPixmap(":/images/Objects-Property-Normal.png");
+ static const QPixmap pixImageNormal = QPixmap(":/images/Objects-Image-Normal.png");
+ static const QPixmap pixBehaviorNormal = QPixmap(":/images/Objects-Behavior-Normal.png");
+ static const QPixmap pixEffectNormal= QPixmap(":/images/Objects-Effect-Normal.png");
+ static const QPixmap pixSceneNormal2x = QPixmap(":/images/Objects-Scene-Normal@2x.png");
+ static const QPixmap pixLayerNormal2x = QPixmap(":/images/Objects-Layer-Normal@2x.png");
+ static const QPixmap pixObjectNormal2x = QPixmap(":/images/Objects-Model-Normal@2x.png");
+ static const QPixmap pixLightNormal2x = QPixmap(":/images/Objects-Light-Normal@2x.png");
+ static const QPixmap pixCameraNormal2x = QPixmap(":/images/Objects-Camera-Normal@2x.png");
+ static const QPixmap pixTextNormal2x = QPixmap(":/images/Objects-Text-Normal@2x.png");
+ static const QPixmap pixAliasNormal2x = QPixmap(":/images/Objects-Alias-Normal@2x.png");
+ static const QPixmap pixGroupNormal2x = QPixmap(":/images/Objects-Group-Normal@2x.png");
+ static const QPixmap pixComponentNormal2x = QPixmap(":/images/Objects-Component-Normal@2x.png");
+ static const QPixmap pixMaterialNormal2x = QPixmap(":/images/Objects-Material-Normal@2x.png");
+ static const QPixmap pixPropertyNormal2x = QPixmap(":/images/Objects-Property-Normal@2x.png");
+ static const QPixmap pixImageNormal2x = QPixmap(":/images/Objects-Image-Normal@2x.png");
+ static const QPixmap pixBehaviorNormal2x = QPixmap(":/images/Objects-Behavior-Normal@2x.png");
+ static const QPixmap pixEffectNormal2x = QPixmap(":/images/Objects-Effect-Normal@2x.png");
+
+ static const QPixmap pixSceneDisabled = QPixmap(":/images/Objects-Scene-Disabled.png");
+ static const QPixmap pixLayerDisabled = QPixmap(":/images/Objects-Layer-Disabled.png");
+ static const QPixmap pixObjectDisabled = QPixmap(":/images/Objects-Model-Disabled.png");
+ static const QPixmap pixLightDisabled = QPixmap(":/images/Objects-Light-Disabled.png");
+ static const QPixmap pixCameraDisabled = QPixmap(":/images/Objects-Camera-Disabled.png");
+ static const QPixmap pixTextDisabled = QPixmap(":/images/Objects-Text-Disabled.png");
+ static const QPixmap pixAliasDisabled = QPixmap(":/images/Objects-Alias-Disabled.png");
+ static const QPixmap pixGroupDisabled = QPixmap(":/images/Objects-Group-Disabled.png");
+ static const QPixmap pixComponentDisabled = QPixmap(":/images/Objects-Component-Disabled.png");
+ static const QPixmap pixMaterialDisabled = QPixmap(":/images/Objects-Material-Disabled.png");
+ static const QPixmap pixPropertyDisabled = QPixmap(":/images/Objects-Property-Disabled.png");
+ static const QPixmap pixImageDisabled = QPixmap(":/images/Objects-Image-Disabled.png");
+ static const QPixmap pixBehaviorDisabled = QPixmap(":/images/Objects-Behavior-Disabled.png");
+ static const QPixmap pixEffectDisabled = QPixmap(":/images/Objects-Effect-Disabled.png");
+ static const QPixmap pixSceneDisabled2x = QPixmap(":/images/Objects-Scene-Disabled@2x.png");
+ static const QPixmap pixLayerDisabled2x = QPixmap(":/images/Objects-Layer-Disabled@2x.png");
+ static const QPixmap pixObjectDisabled2x = QPixmap(":/images/Objects-Model-Disabled@2x.png");
+ static const QPixmap pixLightDisabled2x = QPixmap(":/images/Objects-Light-Disabled@2x.png");
+ static const QPixmap pixCameraDisabled2x = QPixmap(":/images/Objects-Camera-Disabled@2x.png");
+ static const QPixmap pixTextDisabled2x = QPixmap(":/images/Objects-Text-Disabled@2x.png");
+ static const QPixmap pixAliasDisabled2x = QPixmap(":/images/Objects-Alias-Disabled@2x.png");
+ static const QPixmap pixGroupDisabled2x = QPixmap(":/images/Objects-Group-Disabled@2x.png");
+ static const QPixmap pixComponentDisabled2x
+ = QPixmap(":/images/Objects-Component-Disabled@2x.png");
+ static const QPixmap pixMaterialDisabled2x
+ = QPixmap(":/images/Objects-Material-Disabled@2x.png");
+ static const QPixmap pixPropertyDisabled2x
+ = QPixmap(":/images/Objects-Property-Disabled@2x.png");
+ static const QPixmap pixImageDisabled2x = QPixmap(":/images/Objects-Image-Disabled@2x.png");
+ static const QPixmap pixBehaviorDisabled2x
+ = QPixmap(":/images/Objects-Behavior-Disabled@2x.png");
+ static const QPixmap pixEffectDisabled2x = QPixmap(":/images/Objects-Effect-Disabled@2x.png");
+
+ QPixmap pixRowType;
+ if (m_isProperty) {
+ pixRowType = hiResIcons ? (m_locked ? pixPropertyDisabled2x : pixPropertyNormal2x)
+ : (m_locked ? pixPropertyDisabled : pixPropertyNormal);
+ } else {
+ switch (m_objectType) {
+ case OBJTYPE_SCENE:
+ pixRowType = hiResIcons ? (m_locked ? pixSceneDisabled2x : pixSceneNormal2x)
+ : (m_locked ? pixSceneDisabled : pixSceneNormal);
+ break;
+ case OBJTYPE_LAYER:
+ pixRowType = hiResIcons ? (m_locked ? pixLayerDisabled2x : pixLayerNormal2x)
+ : (m_locked ? pixLayerDisabled : pixLayerNormal);
+ break;
+ case OBJTYPE_MODEL:
+ pixRowType = hiResIcons ? (m_locked ? pixObjectDisabled2x : pixObjectNormal2x)
+ : (m_locked ? pixObjectDisabled : pixObjectNormal);
+ break;
+ case OBJTYPE_LIGHT:
+ pixRowType = hiResIcons ? (m_locked ? pixLightDisabled2x : pixLightNormal2x)
+ : (m_locked ? pixLightDisabled : pixLightNormal);
+ break;
+ case OBJTYPE_CAMERA:
+ pixRowType = hiResIcons ? (m_locked ? pixCameraDisabled2x : pixCameraNormal2x)
+ : (m_locked ? pixCameraDisabled : pixCameraNormal);
+ break;
+ case OBJTYPE_TEXT:
+ pixRowType = hiResIcons ? (m_locked ? pixTextDisabled2x : pixTextNormal2x)
+ : (m_locked ? pixTextDisabled : pixTextNormal);
+ break;
+ case OBJTYPE_ALIAS:
+ pixRowType = hiResIcons ? (m_locked ? pixAliasDisabled2x : pixAliasNormal2x)
+ : (m_locked ? pixAliasDisabled : pixAliasNormal);
+ break;
+ case OBJTYPE_GROUP:
+ pixRowType = hiResIcons ? (m_locked ? pixGroupDisabled2x : pixGroupNormal2x)
+ : (m_locked ? pixGroupDisabled : pixGroupNormal);
+ break;
+ case OBJTYPE_COMPONENT:
+ pixRowType = hiResIcons ? (m_locked ? pixComponentDisabled2x : pixComponentNormal2x)
+ : (m_locked ? pixComponentDisabled : pixComponentNormal);
+ break;
+ case OBJTYPE_MATERIAL:
+ case OBJTYPE_CUSTOMMATERIAL:
+ case OBJTYPE_REFERENCEDMATERIAL:
+ pixRowType = hiResIcons ? (m_locked ? pixMaterialDisabled2x : pixMaterialNormal2x)
+ : (m_locked ? pixMaterialDisabled : pixMaterialNormal);
+ break;
+ case OBJTYPE_IMAGE:
+ pixRowType = hiResIcons ? (m_locked ? pixImageDisabled2x : pixImageNormal2x)
+ : (m_locked ? pixImageDisabled : pixImageNormal);
+ break;
+ case OBJTYPE_BEHAVIOR:
+ pixRowType = hiResIcons ? (m_locked ? pixBehaviorDisabled2x : pixBehaviorNormal2x)
+ : (m_locked ? pixBehaviorDisabled : pixBehaviorNormal);
+ break;
+ case OBJTYPE_EFFECT:
+ pixRowType = hiResIcons ? (m_locked ? pixEffectDisabled2x : pixEffectNormal2x)
+ : (m_locked ? pixEffectDisabled : pixEffectNormal);
+ break;
+ default:
+ break;
+ }
+ }
+
+ painter->drawPixmap(m_rectType, pixRowType);
+}
+
+void RowTree::updateVariants(const QStringList &groups)
+{
+ m_variantsGroups = groups;
+ update();
+}
+
+int RowTree::treeWidth() const
+{
+ return m_scene->treeWidth() - m_scene->getScrollbarOffsets().x();
+}
+
+void RowTree::setBinding(ITimelineItemBinding *binding)
+{
+ m_binding = binding;
+
+ // Restore the expansion state of rows
+ m_expandState = m_scene->expandMap().value(instance(), ExpandState::Unknown);
+
+ if (m_expandState == ExpandState::Unknown) {
+ // Everything but scene/component is initially collapsed and hidden
+ if (m_objectType == OBJTYPE_SCENE || m_objectType == OBJTYPE_COMPONENT)
+ m_expandState = ExpandState::Expanded;
+ else
+ m_expandState = ExpandState::HiddenCollapsed;
+ }
+
+ // Make sure all children of visible expanded parents are shown, and vice versa
+ if (parentRow()) {
+ if (parentRow()->expanded()) {
+ if (m_expandState == ExpandState::HiddenCollapsed)
+ m_expandState = ExpandState::Collapsed;
+ else if (m_expandState == ExpandState::HiddenExpanded)
+ m_expandState = ExpandState::Expanded;
+ } else {
+ if (m_expandState == ExpandState::Collapsed)
+ m_expandState = ExpandState::HiddenCollapsed;
+ else if (m_expandState == ExpandState::Expanded)
+ m_expandState = ExpandState::HiddenExpanded;
+ }
+ }
+
+ setRowVisible(m_expandState == ExpandState::Collapsed
+ || m_expandState == ExpandState::Expanded);
+
+ updateFromBinding();
+}
+
+// x value where label should clip
+int RowTree::clipX() const
+{
+ return treeWidth() - TimelineConstants::TREE_ICONS_W - m_variantsGroups.size() * 8 - 2;
+}
+
+ITimelineItemProperty *RowTree::propBinding()
+{
+ return m_PropBinding;
+}
+
+void RowTree::setPropBinding(ITimelineItemProperty *binding)
+{
+ m_PropBinding = binding;
+
+ if (parentRow()->expanded())
+ setRowVisible(true);
+
+ // Update label color
+ m_labelItem.setMaster(m_PropBinding->IsMaster());
+}
+
+void RowTree::setState(State state)
+{
+ m_state = state;
+ m_rowTimeline->m_state = state;
+
+ update();
+ m_rowTimeline->update();
+}
+
+void RowTree::setTimelineRow(RowTimeline *rowTimeline)
+{
+ m_rowTimeline = rowTimeline;
+}
+
+void RowTree::setParentRow(RowTree *parent)
+{
+ m_parentRow = parent;
+}
+
+void RowTree::selectLabel()
+{
+ m_labelItem.setEnabled(true);
+ m_labelItem.setFocus();
+ // Select all text
+ QTextCursor cursor = m_labelItem.textCursor();
+ cursor.select(QTextCursor::Document);
+ m_labelItem.setTextCursor(cursor);
+}
+
+RowTree *RowTree::parentRow() const
+{
+ return m_parentRow;
+}
+
+int RowTree::depth() const
+{
+ return m_depth;
+}
+
+EStudioObjectType RowTree::objectType() const
+{
+ return m_objectType;
+}
+
+QString RowTree::propertyType() const
+{
+ return m_propertyType;
+}
+
+int RowTree::type() const
+{
+ // Enable the use of qgraphicsitem_cast with this item.
+ return TypeRowTree;
+}
+
+int RowTree::index() const
+{
+ // first child in a parent has index 0
+ return m_index;
+}
+
+int RowTree::indexInLayout() const
+{
+ // first child (scene) at index 1, tree header at index 0 (invisible rows are also counted)
+ return m_indexInLayout;
+}
+
+void RowTree::addChild(RowTree *child)
+{
+ int index = getLastChildIndex(child->isProperty()) + 1;
+ addChildAt(child, index);
+}
+
+int RowTree::getLastChildIndex(bool isProperty) const
+{
+ int index = -1;
+ if (isProperty && !m_childProps.empty())
+ index = m_childProps.last()->index();
+ else if (!isProperty && !m_childRows.empty())
+ index = m_childRows.last()->index();
+
+ return index;
+}
+
+void RowTree::updateArrowVisibility()
+{
+ bool oldVisibility = m_arrowVisible;
+ if (m_childRows.empty() && m_childProps.empty()) {
+ m_arrowVisible = false;
+ } else {
+ if (m_childProps.empty()) {
+ m_arrowVisible = false;
+ for (RowTree *row : qAsConst(m_childRows)) {
+ if (!row->m_filtered) {
+ m_arrowVisible = true;
+ break;
+ }
+ }
+ } else {
+ m_arrowVisible = true;
+ }
+ }
+ if (oldVisibility != m_arrowVisible)
+ update();
+}
+
+bool RowTree::isInVariantsFilter() const
+{
+ const QString filterStr = g_StudioApp.m_pMainWnd->getVariantsFilterStr();
+
+ if (m_objectType & ~OBJTYPE_IS_VARIANT || filterStr.isEmpty()
+ || !m_scene->widgetTimeline()->toolbar()->isVariantsFilterOn()) {
+ return true;
+ }
+
+ CDoc *doc = g_StudioApp.GetCore()->GetDoc();
+ const auto propertySystem = doc->GetStudioSystem()->GetPropertySystem();
+ const auto bridge = doc->GetStudioSystem()->GetClientDataModelBridge();
+ auto property = bridge->getVariantsProperty(instance());
+
+ using namespace qt3dsdm;
+ SValue sValue;
+ if (propertySystem->GetInstancePropertyValue(instance(), property, sValue)) {
+ QString propVal = get<TDataStrPtr>(sValue)->toQString();
+ const QStringList filterPairs = filterStr.split(QLatin1Char(','));
+ QHash<QString, bool> matches;
+ for (auto &filterPair : filterPairs) {
+ QString group = filterPair.left(filterPair.indexOf(QLatin1Char(':')) + 1);
+ if (propVal.contains(group)) { // the layer has 1 or more tags from this filter group
+ if (propVal.contains(filterPair))
+ matches[group] = true; // filter tag exists in the property variant group
+ else if (!matches.contains(group))
+ matches[group] = false;
+ }
+ }
+
+ for (auto m : qAsConst(matches)) {
+ if (!m)
+ return false;
+ }
+ }
+
+ return true;
+}
+
+void RowTree::updateFilter()
+{
+ auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ if (bridge->isMaterialContainer(instance()))
+ return;
+
+ bool parentOk = !m_parentRow || m_parentRow->isVisible();
+ bool shyOk = !m_shy || !m_scene->treeHeader()->filterShy();
+ bool visibleOk = m_visible || !m_scene->treeHeader()->filterHidden();
+ bool lockOk = !m_locked || !m_scene->treeHeader()->filterLocked();
+ bool expandOk = !expandHidden();
+ bool variantsOk = isInVariantsFilter();
+
+ m_filtered = !(shyOk && visibleOk && lockOk && variantsOk);
+ const bool visible = parentOk && expandOk && !m_filtered;
+ setVisible(visible);
+ m_rowTimeline->setVisible(visible);
+ for (auto propRow : qAsConst(m_childProps)) {
+ propRow->setVisible(visible);
+ propRow->m_rowTimeline->setVisible(visible);
+ }
+}
+
+int RowTree::getCountDecendentsRecursive() const
+{
+ int num = m_childProps.count();
+
+ for (auto child : qAsConst(m_childRows)) {
+ num++;
+ num += child->getCountDecendentsRecursive();
+ }
+
+ return num;
+}
+
+void RowTree::addChildAt(RowTree *child, int index)
+{
+ // Mahmoud_TODO: improvement: implement moving the child (instead of remove/add) if it is added
+ // under the same parent.
+
+ int maxIndex = getLastChildIndex(child->isProperty()) + 1;
+
+ if (index > maxIndex)
+ index = maxIndex;
+
+ if (child->parentRow() == this && index == child->m_index) // same place
+ return;
+
+ if (child->parentRow())
+ child->parentRow()->removeChild(child);
+
+ child->m_index = index;
+
+ QList<RowTree *> &childRows = child->isProperty() ? m_childProps : m_childRows;
+ int updateIndexInLayout = child->m_indexInLayout;
+ child->m_indexInLayout = m_indexInLayout + index + 1;
+
+ if (!child->isProperty()) {
+ child->m_indexInLayout += m_childProps.count();
+
+ if (m_childRows.size() >= index) {
+ for (int i = 0; i < index; ++i)
+ child->m_indexInLayout += m_childRows.at(i)->getCountDecendentsRecursive();
+ }
+ }
+
+ if (!childRows.contains(child))
+ childRows.insert(index, child);
+
+ child->m_parentRow = this;
+ child->updateDepthRecursive();
+ if (!child->isProperty()) {
+ m_rowTimeline->updateChildrenMinStartXRecursive(this);
+ m_rowTimeline->updateChildrenMaxEndXRecursive(this);
+ }
+
+ // update the layout
+ child->addToLayout(child->m_indexInLayout);
+
+ // update indices
+ updateIndexInLayout = std::min(updateIndexInLayout, child->m_indexInLayout);
+ updateIndices(true, child->m_index + 1, updateIndexInLayout, child->isProperty());
+ updateArrowVisibility();
+}
+
+int RowTree::addToLayout(int indexInLayout)
+{
+ m_scene->layoutTree()->insertItem(indexInLayout, this);
+ m_scene->layoutTimeline()->insertItem(indexInLayout, rowTimeline());
+
+ indexInLayout++;
+
+ for (auto p : qAsConst(m_childProps))
+ indexInLayout = p->addToLayout(indexInLayout);
+
+ for (auto c : qAsConst(m_childRows))
+ indexInLayout = c->addToLayout(indexInLayout);
+
+ return indexInLayout;
+}
+
+RowTree *RowTree::getChildAt(int index) const
+{
+ if (index < 0 || index > m_childRows.count() - 1)
+ return nullptr;
+
+ return m_childRows.at(index);
+}
+
+// this does not destroy the row, just remove it from the layout and parenting hierarchy
+void RowTree::removeChild(RowTree *child)
+{
+ if (m_childProps.contains(child) || m_childRows.contains(child)) { // child exists
+ removeChildFromLayout(child);
+
+ // detach from parent
+ if (child->isProperty())
+ m_childProps.removeAll(child);
+ else
+ m_childRows.removeAll(child);
+
+ child->m_depth = -1;
+ child->m_parentRow = nullptr;
+
+ updateIndices(false, child->m_index, child->m_indexInLayout, child->isProperty());
+ updateArrowVisibility();
+ }
+}
+
+int RowTree::removeChildFromLayout(RowTree *child) const
+{
+ int numRemoved = 0;
+ int deleteIndex = child->m_indexInLayout;
+ for (;;) {
+ RowTree *row_i = static_cast<RowTree *>(m_scene->layoutTree()->itemAt(deleteIndex)
+ ->graphicsItem());
+ if (row_i->depth() <= child->depth() && numRemoved > 0)
+ break;
+
+ m_scene->layoutTree()->removeItem(row_i);
+ m_scene->layoutTimeline()->removeItem(row_i->rowTimeline());
+ numRemoved++;
+
+ if (m_scene->layoutTree()->count() == deleteIndex) // reached end of the list
+ break;
+ }
+
+ return numRemoved;
+}
+
+bool RowTree::draggable() const
+{
+ return !m_locked && !isProperty()
+ && m_objectType & ~(OBJTYPE_IMAGE | OBJTYPE_SCENE | OBJTYPE_IS_MATERIAL);
+}
+
+void RowTree::updateDepthRecursive()
+{
+ if (m_parentRow) {
+ m_depth = m_parentRow->m_depth + 1;
+ updateLabelPosition();
+
+ for (auto p : qAsConst(m_childProps))
+ p->updateDepthRecursive();
+
+ for (auto r : qAsConst(m_childRows))
+ r->updateDepthRecursive();
+ }
+}
+
+// update this parent's children indices after a child row is inserted or removed
+void RowTree::updateIndices(bool isInsertion, int index, int indexInLayout, bool isProperty)
+{
+ // update index
+ if (isProperty && index < m_childProps.count()) {
+ for (int i = index; i < m_childProps.count(); i++)
+ m_childProps.at(i)->m_index += isInsertion ? 1 : -1;
+ } else if (!isProperty && index < m_childRows.count()) {
+ for (int i = index; i < m_childRows.count(); i++)
+ m_childRows.at(i)->m_index += isInsertion ? 1 : -1;
+ }
+
+ // update indexInLayout
+ for (int i = indexInLayout; i < m_scene->layoutTree()->count(); ++i) {
+ RowTree *row_i = static_cast<RowTree *>(m_scene->layoutTree()->itemAt(i)->graphicsItem());
+ row_i->m_indexInLayout = i;
+ }
+}
+
+void RowTree::updateFromBinding()
+{
+ // update view (shy, visible, locked)
+ m_shy = m_binding->GetTimelineItem()->IsShy();
+ m_visible = m_binding->GetTimelineItem()->IsVisible();
+ updateLock(m_binding->GetTimelineItem()->IsLocked());
+ m_visibilityCtrld = m_binding->GetTimelineItem()->IsVisibilityControlled();
+
+ // Update label color
+ Qt3DSDMTimelineItemBinding *itemBinding =
+ static_cast<Qt3DSDMTimelineItemBinding *>(m_binding);
+ m_master = itemBinding->IsMaster();
+ m_labelItem.setMaster(m_master);
+ // Update timeline comments
+ m_rowTimeline->updateCommentItem();
+}
+
+void RowTree::updateLabel()
+{
+ if (m_binding)
+ m_labelItem.setLabel(m_binding->GetTimelineItem()->GetName().toQString());
+}
+
+void RowTree::setRowVisible(bool visible)
+{
+ auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+ if (bridge->isMaterialContainer(instance()))
+ return;
+
+ if (visible) {
+ setMaximumHeight(TimelineConstants::ROW_H);
+ setOpacity(1.0);
+ setVisible(true);
+ m_rowTimeline->setMaximumHeight(TimelineConstants::ROW_H);
+ m_rowTimeline->setOpacity(1.0);
+ m_rowTimeline->setVisible(true);
+ } else {
+ setMaximumHeight(0.0);
+ setOpacity(0.0);
+ setVisible(false);
+ m_rowTimeline->setMaximumHeight(0.0);
+ m_rowTimeline->setOpacity(0.0);
+ m_rowTimeline->setVisible(false);
+ }
+}
+
+bool RowTree::hasPropertyChildren() const
+{
+ return !m_childProps.empty();
+}
+
+void RowTree::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
+{
+ QPointF p = event->pos();
+ if (m_rectType.contains(p.x(), p.y()) && !m_locked)
+ if (m_binding)
+ m_binding->OpenAssociatedEditor();
+}
+
+// handle clicked control and return its type
+TreeControlType RowTree::getClickedControl(const QPointF &scenePos)
+{
+ QPointF p = mapFromScene(scenePos.x(), scenePos.y());
+ if (m_arrowVisible && m_rectArrow.contains(p.x(), p.y())) {
+ updateExpandStatus(m_expandState == ExpandState::Expanded ? ExpandState::Collapsed
+ : ExpandState::Expanded, false);
+ update();
+ return TreeControlType::Arrow;
+ }
+
+ if (hasActionButtons()) {
+ if (m_rectShy.contains(p.x(), p.y())) {
+ toggleShy();
+ return TreeControlType::Shy;
+ } else if (!m_onMasterSlide && m_rectVisible.contains(p.x(), p.y())) {
+ // Prevent toggling hide on master slide
+ toggleVisible();
+ return TreeControlType::Hide;
+ } else if (m_rectLocked.contains(p.x(), p.y())) {
+ toggleLocked();
+ return TreeControlType::Lock;
+ }
+ }
+
+ return TreeControlType::None;
+}
+
+void RowTree::updateExpandStatus(ExpandState state, bool animate, bool forceChildUpdate)
+{
+ const bool changed = m_expandState != state;
+ if (!forceChildUpdate && !changed)
+ return;
+
+ m_expandState = state;
+
+ if (m_scene->widgetTimeline()->isFullReconstructPending())
+ return;
+
+ // Store the expanded state of items so we can restore it on slide change
+ if (changed && m_binding)
+ m_scene->expandMap().insert(instance(), m_expandState);
+
+ if (animate)
+ animateExpand(m_expandState);
+
+ // updateFilter updates the row visibility. It must be called before children are handled
+ // to ensure parent visibility is up to date.
+ if (changed)
+ updateFilter();
+
+ if (!m_childRows.empty()) {
+ for (auto child : qAsConst(m_childRows)) {
+ if (state == ExpandState::Expanded) {
+ if (child->m_expandState == ExpandState::HiddenExpanded)
+ child->updateExpandStatus(ExpandState::Expanded);
+ else if (child->m_expandState == ExpandState::HiddenCollapsed)
+ child->updateExpandStatus(ExpandState::Collapsed);
+ } else {
+ if (child->m_expandState == ExpandState::Expanded)
+ child->updateExpandStatus(ExpandState::HiddenExpanded);
+ else if (child->m_expandState == ExpandState::Collapsed)
+ child->updateExpandStatus(ExpandState::HiddenCollapsed);
+ }
+ }
+ }
+
+ if (!m_childProps.empty()) {
+ for (auto child : qAsConst(m_childProps)) {
+ // Properties can never be collapsed
+ if (state == ExpandState::Expanded)
+ child->updateExpandStatus(ExpandState::Expanded);
+ else
+ child->updateExpandStatus(ExpandState::HiddenExpanded);
+ }
+ }
+}
+
+void RowTree::updateLockRecursive(bool state)
+{
+ updateLock(state);
+ if (!m_childRows.empty()) {
+ for (auto child : qAsConst(m_childRows))
+ child->updateLockRecursive(m_locked);
+ }
+}
+
+void RowTree::updateLock(bool state)
+{
+ m_locked = state;
+
+ m_labelItem.setLocked(m_locked);
+ update();
+ if (!m_childProps.empty()) {
+ for (auto child : qAsConst(m_childProps))
+ child->updateLock(m_locked);
+ }
+ if (m_locked)
+ m_scene->keyframeManager()->deselectRowKeyframes(this);
+}
+
+void RowTree::updateSubpresentations(int updateParentsOnlyVal)
+{
+ if (updateParentsOnlyVal != 0) {
+ int n = m_numDescendantSubpresentations;
+ if (m_hasSubpresentation)
+ n++;
+ if (n > 0) {
+ RowTree *parentRow = m_parentRow;
+ while (parentRow) {
+ parentRow->m_numDescendantSubpresentations += n * updateParentsOnlyVal;
+ parentRow->update();
+ parentRow = parentRow->m_parentRow;
+ }
+ }
+ } else {
+ auto binding = static_cast<Qt3DSDMTimelineItemBinding *>(m_binding);
+ bool hasSubp = binding->hasSubpresentation();
+
+ if (m_hasSubpresentation != hasSubp) {
+ m_hasSubpresentation = hasSubp;
+ int n = hasSubp ? 1 : -1;
+ RowTree *parentRow = m_parentRow;
+ while (parentRow) {
+ parentRow->m_numDescendantSubpresentations += n;
+ parentRow->update();
+ parentRow = parentRow->m_parentRow;
+ }
+ }
+ }
+ update();
+}
+
+void RowTree::updateLabelPosition()
+{
+ int offset = 5 + m_depth * TimelineConstants::ROW_DEPTH_STEP + 30;
+ m_labelItem.setPos(offset, -1);
+}
+
+bool RowTree::expanded() const
+{
+ if (m_isProperty)
+ return false;
+ else
+ return m_expandState == ExpandState::Expanded;
+}
+
+bool RowTree::expandHidden() const
+{
+ return m_expandState == ExpandState::HiddenExpanded
+ || m_expandState == ExpandState::HiddenCollapsed;
+}
+
+bool RowTree::isDecendentOf(RowTree *row) const
+{
+ RowTree *parentRow = m_parentRow;
+
+ while (parentRow) {
+ if (parentRow == row)
+ return true;
+
+ parentRow = parentRow->parentRow();
+ }
+
+ return false;
+}
+
+void RowTree::setDnDHover(bool val)
+{
+ m_dndHover = val;
+ update();
+}
+
+void RowTree::setDnDState(DnDState state, DnDState onlyIfState, bool recursive)
+{
+ if (m_dndState == onlyIfState || onlyIfState == DnDState::Any) {
+ m_dndState = state;
+ update();
+
+ if (recursive) { // used by source rows to highlights all of their descendants
+ for (auto child : qAsConst(m_childProps))
+ child->setDnDState(state, onlyIfState, true);
+
+ for (auto child : qAsConst(m_childRows))
+ child->setDnDState(state, onlyIfState, true);
+ }
+ }
+}
+
+RowTree::DnDState RowTree::getDnDState() const
+{
+ return m_dndState;
+}
+
+void RowTree::setActionStates(ActionStates states)
+{
+ if (states != m_actionStates) {
+ m_actionStates = states;
+ update();
+ }
+}
+
+bool RowTree::isContainer() const
+{
+ return !m_isProperty && m_objectType & OBJTYPE_IS_CONTAINER;
+}
+
+bool RowTree::isProperty() const
+{
+ return m_isProperty;
+}
+
+RowTree *RowTree::getPropertyRow(const QString &type) const
+{
+ for (RowTree *prop : qAsConst(m_childProps)) {
+ if (prop->label() == type)
+ return prop;
+ }
+
+ return nullptr;
+}
+
+
+bool RowTree::isPropertyOrMaterial() const
+{
+ return m_isProperty || m_objectType & (OBJTYPE_IS_MATERIAL | OBJTYPE_IMAGE);
+}
+
+bool RowTree::isComponent() const
+{
+ return m_objectType == OBJTYPE_COMPONENT;
+}
+
+bool RowTree::isComponentRoot() const
+{
+ if (m_objectType == OBJTYPE_COMPONENT && m_binding)
+ return static_cast<Qt3DSDMTimelineItemBinding *>(m_binding)->isRootComponent();
+
+ return false;
+}
+
+bool RowTree::isMaster() const
+{
+ return m_master;
+}
+
+bool RowTree::isDefaultMaterial() const
+{
+ if (m_binding)
+ return static_cast<Qt3DSDMTimelineItemBinding *>(m_binding)->isDefaultMaterial();
+
+ return false;
+}
+
+bool RowTree::empty() const
+{
+ return m_childRows.empty() && m_childProps.empty();
+}
+
+bool RowTree::selected() const
+{
+ return m_state == Selected;
+}
+
+QList<RowTree *> RowTree::childRows() const
+{
+ return m_childRows;
+}
+
+QList<RowTree *> RowTree::childProps() const
+{
+ return m_childProps;
+}
+
+RowTimeline *RowTree::rowTimeline() const
+{
+ return m_rowTimeline;
+}
+
+QString RowTree::label() const
+{
+ return m_label;
+}
+
+void RowTree::toggleShy()
+{
+ if (hasActionButtons()) {
+ m_shy = !m_shy;
+ update();
+ m_binding->GetTimelineItem()->SetShy(m_shy);
+ }
+}
+
+void RowTree::toggleVisible()
+{
+ if (hasActionButtons()) {
+ m_visible = !m_visible;
+ update();
+ m_binding->GetTimelineItem()->SetVisible(m_visible);
+ }
+}
+
+void RowTree::toggleLocked()
+{
+ if (hasActionButtons()) {
+ updateLockRecursive(!m_locked);
+ m_binding->GetTimelineItem()->SetLocked(m_locked);
+ if (m_locked && selected())
+ m_scene->rowManager()->clearSelection();
+ }
+}
+
+bool RowTree::shy() const
+{
+ return m_shy;
+}
+
+bool RowTree::visible() const
+{
+ return m_visible;
+}
+
+bool RowTree::locked() const
+{
+ return m_locked;
+}
+
+// Returns true for items with shy/visible/lock buttons
+bool RowTree::hasActionButtons() const
+{
+ return !m_isProperty && m_indexInLayout != 1
+ && m_objectType & ~(OBJTYPE_SCENE | OBJTYPE_IS_MATERIAL | OBJTYPE_IMAGE);
+}
+
+bool RowTree::hasComponentAncestor() const
+{
+ RowTree *parentRow = m_parentRow;
+ while (parentRow) {
+ if (parentRow->objectType() == OBJTYPE_COMPONENT)
+ return true;
+ parentRow = parentRow->parentRow();
+ }
+ return false;
+}
+
+// Returns true for items with duration bar
+bool RowTree::hasDurationBar() const
+{
+ return hasActionButtons(); // Same at least now
+}
+
+bool RowTree::propertyExpanded() const
+{
+ return m_isPropertyExpanded;
+}
+
+void RowTree::togglePropertyExpanded()
+{
+ setPropertyExpanded(!m_isPropertyExpanded);
+}
+
+void RowTree::setPropertyExpanded(bool expand)
+{
+ m_isPropertyExpanded = expand;
+ if (m_isPropertyExpanded)
+ animateExpand(ExpandState::Expanded);
+ else
+ animateExpand(ExpandState::Collapsed);
+}
+
+void RowTree::showDataInputSelector(const QString &propertyname, const QPoint &pos)
+{
+ auto bridge = g_StudioApp.GetCore()->GetDoc()->GetStudioSystem()->GetClientDataModelBridge();
+
+ // Set the datainput to control property in referenced object if this
+ // is a referenced material.
+ auto refInstance = bridge->getMaterialReference(instance());
+
+ m_scene->handleShowDISelector(propertyname, refInstance.Valid() ? refInstance : instance(),
+ pos);
+}
+