summaryrefslogtreecommitdiffstats
path: root/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp')
-rw-r--r--src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp1139
1 files changed, 1139 insertions, 0 deletions
diff --git a/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp b/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp
new file mode 100644
index 00000000..7960ed96
--- /dev/null
+++ b/src/Authoring/Studio/Palettes/Timeline/BaseStateRow.cpp
@@ -0,0 +1,1139 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "stdafx.h"
+
+#include "BaseStateRow.h"
+#include "PropertyRow.h"
+#include "BaseTimelineTreeControl.h"
+#include "ToggleControl.h"
+#include "BaseTimebarlessRow.h"
+#include "ColorControl.h"
+#include "StateRowFactory.h"
+#include "TimelineTimelineLayout.h"
+#include "ComponentContextMenu.h"
+#include "ITimelineControl.h"
+#include "ResourceCache.h"
+#include "StudioUtils.h"
+#include "Bindings/ITimelineItemBinding.h"
+#include "Bindings/ITimelineTimebar.h"
+
+const long CBaseStateRow::DEFAULT_TOGGLE_LENGTH = 57;
+
+CBaseStateRow::CBaseStateRow()
+ : m_TimeRatio(0.0f)
+ , m_TreeList(true)// true to align the children in the timeline.
+ , m_TreeControl(nullptr)
+ , m_ColorControl(nullptr)
+ , m_ToggleControl(nullptr)
+ , m_TimebarControl(nullptr)
+ , m_Loaded(false)
+ , m_IsExpanded(false)
+ , m_Highlighted(false)
+ , m_Dirty(false)
+ , m_Selected(false)
+ , m_TimelineItemBinding(nullptr)
+ , m_ActiveStart(0)
+ , m_ActiveEnd(0)
+{
+}
+
+CBaseStateRow::~CBaseStateRow()
+{
+ delete m_TreeControl;
+ delete m_ColorControl;
+ delete m_ToggleControl;
+ delete m_TimebarControl;
+
+ // Go through all the state rows and delete them, this control owns all child controls.
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ (*thePos)->Dispose();
+
+ // Go through all the properties and delete them, this control owns all child controls.
+ TPropertyRowList::iterator thePropertyPos = m_PropertyRows.begin();
+ for (; thePropertyPos != m_PropertyRows.end(); ++thePropertyPos) {
+ CPropertyRow *theRow = (*thePropertyPos);
+ delete theRow;
+ }
+}
+
+void CBaseStateRow::Initialize(ITimelineItemBinding *inTimelineItemBinding)
+{
+ m_Dirty = true;
+ ASSERT(inTimelineItemBinding);
+ m_TimelineItemBinding = inTimelineItemBinding;
+
+ m_TreeControl = new CBaseTimelineTreeControl(this, GetTimelineItem()->IsMaster());
+ m_ColorControl = new CColorControl(this);
+ m_ToggleControl = CreateToggleControl();
+ m_TimebarControl = CreateTimebarRow();
+
+ long theTimebarHeight = CStudioPreferences::GetRowSize();
+ m_TreeControl->SetSize(CPt(500, theTimebarHeight));
+ m_ColorControl->SetAbsoluteSize(CPt(theTimebarHeight, theTimebarHeight));
+ m_ToggleControl->SetAbsoluteSize(CPt(DEFAULT_TOGGLE_LENGTH, theTimebarHeight));
+ m_TimebarControl->SetSize(CPt(800, theTimebarHeight));
+
+ ::CColor theColor = GetTimebarBackgroundColor(GetObjectType());
+ m_TreeControl->SetBackgroundColor(theColor);
+ m_ToggleControl->SetBackgroundColor(theColor);
+ m_TimebarControl->SetBackgroundColor(theColor);
+
+ m_ColorList.AddChild(m_ColorControl);
+ m_TreeList.AddChild(m_TreeControl);
+ m_ToggleList.AddChild(m_ToggleControl);
+ m_TimebarList.AddChild(m_TimebarControl);
+
+ // sk - I think setting controls' names is only useful for debugging.
+ /*Q3DStudio::CString theAssetName( m_Asset->GetName( ) );
+ m_TreeControl->SetName( theAssetName + "TreeControl" );
+ m_TreeList.SetName( theAssetName + "TreeList" );
+ m_ColorControl->SetName( theAssetName + "ColorControl" );
+ m_ColorList.SetName( theAssetName + "ColorList" );
+ m_ToggleControl->SetName( theAssetName + "ToggleControl" );
+ m_ToggleList.SetName( theAssetName + "ToggleList" );
+ m_TimebarControl->SetName( theAssetName + "TimebarControl" );
+ m_TimebarList.SetName( theAssetName + "TimebarList" );*/
+
+ // Bind after all the UI is setup.
+ m_TimelineItemBinding->Bind(this); // see Dispose where it properly unbinds.
+
+ ClearDirty();
+}
+
+//=============================================================================
+/**
+ * Expand this node of the tree control.
+ * This will display all children the fit the filter.
+ */
+void CBaseStateRow::Expand(bool inExpandAll /*= false*/, bool inExpandUp)
+{
+ if (!m_IsExpanded) {
+ m_Filter.SetExpanded(true);
+
+ // Expand/Collapse is done by adding and removing the children, add all the
+ // properties first so they are at the top of the list.
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->Filter(m_Filter, false);
+ }
+ // Add all the State rows after the properties.
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ theRow->Filter(m_Filter, false);
+ }
+
+ m_TreeControl->SetExpanded(true);
+ m_IsExpanded = true;
+ m_ColorControl->UpdateIconStatus();
+ }
+
+ if (inExpandAll) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ (*thePos)->Expand(inExpandAll);
+ }
+
+ if (inExpandUp && m_ParentRow)
+ m_ParentRow->Expand(false, inExpandUp);
+}
+
+//=============================================================================
+/**
+ * Collapse this node of the tree control.
+ * This will hide all children of this control.
+ */
+void CBaseStateRow::Collapse(bool inCollapseAll /* = false */)
+{
+ if (m_IsExpanded) {
+ CFilter theFilter = m_Filter;
+ theFilter.SetExpanded(false);
+
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->Filter(theFilter);
+ }
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ theRow->Filter(theFilter);
+ }
+
+ m_TimelineItemBinding->OnCollapsed();
+
+ m_TreeControl->SetExpanded(false);
+ m_IsExpanded = false;
+ m_ColorControl->UpdateIconStatus();
+ }
+
+ if (inCollapseAll) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ (*thePos)->Collapse(inCollapseAll);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Toggle the expansion state of this control.
+ * This will expand the control if it is closed, or collapse it if it is
+ * open.
+ */
+void CBaseStateRow::ToggleExpansion(CToggleButton *, CButtonControl::EButtonState inButtonState)
+{
+ if (inButtonState == CButtonControl::EBUTTONSTATE_UP)
+ Collapse();
+ else
+ Expand();
+}
+
+//=============================================================================
+/**
+ * Shows or hides rows for all children, based on the filter.
+ * @param inFilter Object specifying the filters currently applied to the timeline.
+ * @param inFilterChildren true if the filter should go recursively to children.
+ */
+void CBaseStateRow::Filter(const CFilter &inFilter, bool inFilterChildren /*= true*/)
+{
+ m_Filter = inFilter;
+
+ // For each child object
+ if (inFilterChildren) {
+ CFilter theChildFilter = inFilter;
+ theChildFilter.SetExpanded(m_IsExpanded);
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ // Apply the filter
+ CStateRow *theRow = (*thePos);
+ theRow->Filter(theChildFilter);
+ }
+ }
+
+ // This flag determines whether or not the controls on this row should be shown, based on the
+ // filter
+ bool theVisibleFlag = PerformFilter(m_Filter);
+
+ m_IsViewable = theVisibleFlag;
+
+ theVisibleFlag &= inFilter.IsExpanded();
+
+ // Show or hide the controls on this row before we iterate through the properties
+ m_ColorList.SetVisible(theVisibleFlag);
+ m_TreeList.SetVisible(theVisibleFlag);
+ m_ToggleList.SetVisible(theVisibleFlag);
+ m_TimebarList.SetVisible(theVisibleFlag);
+
+ if (inFilterChildren) {
+ CFilter theChildFilter = inFilter;
+ theChildFilter.SetExpanded(m_IsExpanded);
+
+ // For each property on this object
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ // Apply the filter
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->Filter(theChildFilter);
+ }
+ }
+
+ m_TreeControl->SetToggleVisible(this->HasVisibleChildren());
+}
+
+//=============================================================================
+/**
+ * Get the color control for this row.
+ * @return the color control for this row.
+ */
+CControl *CBaseStateRow::GetColorControl()
+{
+ return &m_ColorList;
+}
+
+//=============================================================================
+/**
+ * Get the tree control for this row.
+ * @return the tree control for this row.
+ */
+CControl *CBaseStateRow::GetTreeControl()
+{
+ return &m_TreeList;
+}
+
+//=============================================================================
+/**
+ * Get the toggle control for this row.
+ * @return the toggle control for this row.
+ */
+CControl *CBaseStateRow::GetToggleControl()
+{
+ return &m_ToggleList;
+}
+
+//=============================================================================
+/**
+ * Get the timebar control for this row.
+ * @return the timebar control for this row.
+ */
+CControl *CBaseStateRow::GetTimebarControl()
+{
+ return &m_TimebarList;
+}
+
+//=============================================================================
+/**
+ * Remove a row from this control.
+ * @param inState the state of the row to be removed.
+ */
+void CBaseStateRow::RemoveRow(CStateRow *inRow)
+{
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ if (theRow == inRow) {
+ DeleteRow(theRow);
+ m_StateRows.erase(thePos);
+ break;
+ }
+ }
+ m_TreeControl->SetToggleVisible(this->HasVisibleChildren());
+}
+
+//=============================================================================
+/**
+ * Helper function to remove all controls of this property row and dispose of it.
+ */
+void CBaseStateRow::DeletePropertyRow(CPropertyRow *inPropertyRow)
+{
+ if (!inPropertyRow)
+ return;
+
+ m_ColorList.RemoveChild(inPropertyRow->GetColorControl());
+ m_TreeList.RemoveChild(inPropertyRow->GetTreeControl());
+ m_ToggleList.RemoveChild(inPropertyRow->GetToggleControl());
+ m_TimebarList.RemoveChild(inPropertyRow->GetTimebarControl());
+ delete inPropertyRow;
+}
+
+//=============================================================================
+/**
+ * By default, we don't show shy/eye/lock toggles
+ */
+CBlankToggleControl *CBaseStateRow::CreateToggleControl()
+{
+ return new CBlankToggleControl(this);
+}
+
+//=============================================================================
+/**
+ * Get the StateRow that is representing this child timeline item.
+ * @param inTimelineItem child timeline item
+ * @return the StateRow for inState.
+ */
+CStateRow *CBaseStateRow::GetRow(ITimelineItem *inTimelineItem)
+{
+ if (inTimelineItem) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ if ((*thePos)->GetTimelineItem() == inTimelineItem)
+ return (*thePos);
+ }
+ }
+ return nullptr;
+}
+
+//=============================================================================
+/**
+ * Called when a row is to be completely removed from the UI
+ */
+void CBaseStateRow::DeleteRow(CStateRow *inRow)
+{
+ m_ColorList.RemoveChild(inRow->GetColorControl());
+ m_TreeList.RemoveChild(inRow->GetTreeControl());
+ m_ToggleList.RemoveChild(inRow->GetToggleControl());
+ m_TimebarList.RemoveChild(inRow->GetTimebarControl());
+
+ inRow->Dispose();
+}
+
+//=============================================================================
+/**
+ * Call from the child controls that the mouse is over one of the children.
+ * This is used to highlight the entire row on mouse over.
+ */
+void CBaseStateRow::OnMouseOver()
+{
+ if (!m_Highlighted) {
+ try {
+ // TODO: Added the try/catch block to prevent crashing when the instance handle is not
+ // found
+ // this will happen sometimes when delete the object from the timeline
+ // need to really fix this at the root.
+ ::CColor theColor = GetTimebarHighlightBackgroundColor(GetObjectType());
+ m_TreeControl->SetBackgroundColor(theColor);
+ m_ToggleControl->SetBackgroundColor(theColor);
+ m_TimebarControl->SetBackgroundColor(theColor);
+
+ m_Highlighted = true;
+ } catch (...) {
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Call from the child controls that the mouse is no longer over one of the children.
+ * This is used to highlight the entire row on mouse over.
+ */
+void CBaseStateRow::OnMouseOut()
+{
+ if (m_Highlighted) {
+ try {
+ // TODO: Added the try/catch block to prevent crashing when the instance handle is not
+ // found
+ // this will happen sometimes when delete the object from the timeline
+ // need to really fix this at the root.
+ ::CColor theColor = GetTimebarBackgroundColor(GetObjectType());
+ m_TreeControl->SetBackgroundColor(theColor);
+ m_ToggleControl->SetBackgroundColor(theColor);
+ m_TimebarControl->SetBackgroundColor(theColor);
+
+ m_Highlighted = false;
+ } catch (...) {
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Tells this that the Asset data has changed and it needs to be updated.
+ * Someone should call ClearDirty afterwards.
+ */
+void CBaseStateRow::OnDirty()
+{
+ m_Dirty = true;
+}
+
+void CBaseStateRow::ClearDirty()
+{
+ if (m_Dirty) {
+ m_TreeControl->Refresh(m_TimelineItemBinding->GetTimelineItem());
+ m_ToggleControl->Refresh();
+ m_ColorControl->Invalidate();
+ m_TimebarControl->RefreshRowMetaData();
+ m_Dirty = false;
+ }
+}
+
+//=============================================================================
+/**
+ * Recursively load the children of this control, used by derived classes
+ * This will load all the properties and states, and create controls for them.
+ */
+void CBaseStateRow::LoadChildren()
+{
+ if (!m_Loaded) {
+ m_Loaded = true;
+
+ LoadProperties();
+
+ CTimelineItemOrderedIterator theChildIter(m_TimelineItemBinding);
+ // Go through all the children and load them too.
+ for (; !theChildIter.IsDone(); ++theChildIter)
+ CreateChildRow(*theChildIter, nullptr);
+
+ GetTopControl()->OnLayoutChanged();
+ }
+}
+
+//=============================================================================
+/**
+ * Add a row that represents this child timeline item
+ * @param inNextItem indicates row to follow behind the row for inTimeLineItem, nullptr to append inRow
+ * to the end of the current list.
+ */
+void CBaseStateRow::AddChildRow(ITimelineItemBinding *inTimeLineItem,
+ ITimelineItemBinding *inNextItem)
+{
+ if (!inTimeLineItem)
+ return;
+
+ // only add if loaded, else it will get added twice.
+ if (m_Loaded) {
+ CStateRow *theStateRow = CreateChildRow(
+ inTimeLineItem, inNextItem ? GetRow(inNextItem->GetTimelineItem()) : nullptr);
+ if (theStateRow)
+ theStateRow->LoadChildren();
+ }
+ Expand(false, true);
+
+ CBaseStateRow *theRow = GetRow(inTimeLineItem->GetTimelineItem());
+ if (theRow) {
+ CControl *theTreeControl = theRow->GetTreeControl();
+ if (theTreeControl)
+ theTreeControl->EnsureVisible();
+ }
+}
+
+void CBaseStateRow::RemoveChildRow(ITimelineItemBinding *inTimelineItem)
+{
+ CStateRow *theChildRow = GetRow(inTimelineItem->GetTimelineItem());
+ inTimelineItem->SetParent(nullptr);
+ if (theChildRow) {
+ RemoveRow(theChildRow);
+ // preserving legacy behavior.
+ GetTopControl()->HideTimelineMoveableTooltip();
+ }
+}
+
+//=============================================================================
+/**
+ * Removes all child rows from this row. Called prior to a load. The load call is responsible for
+ * updating the UI.
+ */
+void CBaseStateRow::RemoveAllChildren()
+{
+ RemoveAllProperties();
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ DeleteRow(*thePos);
+
+ m_StateRows.clear();
+}
+
+//=============================================================================
+/**
+ * Remove all the properties from this object. Called prior to a load. The load call is responsible
+ * for updating the UI.
+ */
+void CBaseStateRow::RemoveAllProperties()
+{
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos)
+ DeletePropertyRow(*thePropPos);
+
+ m_PropertyRows.clear();
+}
+
+//=============================================================================
+/**
+ * Set this row to selected
+ */
+void CBaseStateRow::Select(SBaseStateRowSelectionKeyState inState,
+ bool inCheckKeySelection /*= true */)
+{
+ bool alreadySelected = m_Selected;
+ m_TimelineItemBinding->SetSelected(inState.IsControlDown());
+ if (inCheckKeySelection) {
+ if (inState.IsShiftDown())
+ m_TimebarControl->SelectAllKeys();
+ else if (!alreadySelected)
+ m_TimelineItemBinding->ClearKeySelection();
+ }
+}
+
+//=============================================================================
+/**
+ * Change the selection state of the row.
+ */
+void CBaseStateRow::OnSelected(bool inSelection)
+{
+ if (inSelection == m_Selected)
+ return;
+
+ m_Selected = inSelection;
+ if (inSelection) {
+ if (m_ParentRow)
+ m_ParentRow->Expand(false, true);
+
+ m_TreeControl->EnsureVisible();
+
+ m_TreeControl->OnSelect();
+ m_ToggleControl->OnSelect();
+ m_ColorControl->OnSelect();
+ m_TimebarControl->OnSelect();
+ } else {
+ m_TreeControl->OnDeselect();
+ m_ToggleControl->OnDeselect();
+ m_ColorControl->OnDeselect();
+ m_TimebarControl->OnDeselect();
+ }
+}
+
+//=============================================================================
+/**
+ * Call to add a property row as a child of this control.
+ * @param inRow the row to be added.
+ */
+void CBaseStateRow::AddPropertyRow(CPropertyRow *inRow, CTimelineRow *inNextRow /*= nullptr */)
+{
+ m_PropertyRows.push_back(inRow);
+ InitializePropertyRow(inRow, inNextRow);
+ // For snapping timebars/keyframes
+ inRow->SetSnappingListProvider(GetSnappingListProvider());
+
+ m_TimebarControl->SetDirty(true);
+}
+
+//=============================================================================
+/**
+ * Remove the property row.
+ */
+void CBaseStateRow::RemovePropertyRow(const CPropertyRow *inRow)
+{
+ if (!inRow)
+ return;
+
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *theRow = *thePropPos;
+ if (theRow == inRow) {
+ DeletePropertyRow(theRow);
+ m_PropertyRows.erase(thePropPos);
+
+ // Update flippy
+ OnChildVisibilityChanged();
+ break;
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Helper function to initialize a new property row
+ * @param inRow the row to be added.
+ * @param inNextRow if specified, row that should be after inRow after
+ * insertion.
+ */
+void CBaseStateRow::InitializePropertyRow(CPropertyRow *inRow, CTimelineRow *inNextRow /*= nullptr */)
+{
+ CFilter theFilter = m_Filter;
+ theFilter.SetExpanded(m_IsExpanded);
+
+ if (!inNextRow) { // not provided, this property row would be inserted before the first
+ // non-property row.
+ CTimelineItemOrderedIterator theIterator(m_TimelineItemBinding);
+ if (!theIterator.IsDone())
+ inNextRow = GetRow(theIterator.GetCurrent()->GetTimelineItem());
+ }
+ AddRowToUILists(inRow, inNextRow, theFilter);
+}
+
+void CBaseStateRow::AddRowToUILists(CTimelineRow *inRow, CTimelineRow *inNextRow, CFilter &inFilter)
+{
+ // Default the insert locations to the end of the list.
+ CControl *theNextColorControl = nullptr;
+ CControl *theNextTreeControl = nullptr;
+ CControl *theNextToggleControl = nullptr;
+ CControl *theNextTimebarControl = nullptr;
+ if (inNextRow) {
+ theNextColorControl = inNextRow->GetColorControl();
+ theNextTreeControl = inNextRow->GetTreeControl();
+ theNextToggleControl = inNextRow->GetToggleControl();
+ theNextTimebarControl = inNextRow->GetTimebarControl();
+ }
+ inRow->SetIndent(m_Indent + CTimelineRow::TREE_INDENT);
+ inRow->SetParent(this);
+ inRow->Filter(inFilter);
+ inRow->SetTimeRatio(m_TimeRatio);
+
+ CControl *theColorControl = inRow->GetColorControl();
+ CControl *theTreeControl = inRow->GetTreeControl();
+ CControl *theToggleControl = inRow->GetToggleControl();
+ CControl *theTimebarControl = inRow->GetTimebarControl();
+
+ // If not expanded then hide the controls.
+ if (!m_IsExpanded) {
+ theColorControl->SetVisible(false);
+ theTreeControl->SetVisible(false);
+ theToggleControl->SetVisible(false);
+ theTimebarControl->SetVisible(false);
+ }
+
+ // Add the controls to the lists in the prioritized order
+ m_ColorList.AddChild(theColorControl, theNextColorControl);
+ m_TreeList.AddChild(theTreeControl, theNextTreeControl);
+ m_ToggleList.AddChild(theToggleControl, theNextToggleControl);
+ m_TimebarList.AddChild(theTimebarControl, theNextTimebarControl);
+
+ m_TreeControl->SetToggleVisible(this->HasVisibleChildren());
+}
+
+CStateRow *CBaseStateRow::CreateChildRow(ITimelineItemBinding *inChildBinding, CStateRow *inNextRow)
+{
+ CStateRow *theRow =
+ CStateRowFactory::CreateStateRow(inChildBinding, this, GetSnappingListProvider());
+ if (theRow) { // add by appending to the list
+ AddStateRow(theRow, inNextRow);
+ }
+ inChildBinding->SetParent(m_TimelineItemBinding);
+ return theRow;
+}
+
+long CBaseStateRow::GetNumNonPropertyRows() const
+{
+ return static_cast<long>(m_StateRows.size());
+}
+
+CBaseStateRow *CBaseStateRow::GetNonPropertyRow(long inIndex) const
+{
+ return m_StateRows.at(inIndex);
+}
+
+long CBaseStateRow::GetNumPropertyRows() const
+{
+ return static_cast<long>(m_PropertyRows.size());
+}
+CPropertyRow *CBaseStateRow::GetPropertyRow(long inIndex) const
+{
+ return m_PropertyRows.at(inIndex);
+}
+
+//=============================================================================
+/**
+ * Call to add a state row as a child of this control.
+ * @param inRow the row to be added.
+ * @param inNextRow row to follow behind the row that would be added, nullptr to append inRow to the
+ * end of the current list.
+ */
+void CBaseStateRow::AddStateRow(CStateRow *inRow, CStateRow *inNextRow)
+{
+ if (inNextRow != nullptr) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ while (thePos != m_StateRows.end()) {
+ if ((*thePos) == inNextRow) {
+ m_StateRows.insert(thePos, inRow);
+ thePos = m_StateRows.end();
+ } else
+ ++thePos;
+ }
+ } else {
+ m_StateRows.push_back(inRow);
+ }
+
+ AddRowToUILists(inRow, inNextRow, m_Filter);
+}
+
+//=============================================================================
+/**
+ * Checks to see if there are any visible children of this row.
+ * This is used for figuring out whether the expand button should be displayed
+ * or not.
+ */
+bool CBaseStateRow::HasVisibleChildren()
+{
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ // Apply the filter
+ if ((*thePos)->IsViewable())
+ return true;
+ }
+
+ // For each property on this object
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ // Apply the filter
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow && thePropRow->IsViewable())
+ return true;
+ }
+ return false;
+}
+
+//=============================================================================
+/**
+ * Set the amount of time that is represented by a pixel.
+ * This modifies the length of this control.
+ * @param inTimePerPixel the time per pixel.
+ */
+void CBaseStateRow::SetTimeRatio(double inTimeRatio)
+{
+ m_TimeRatio = inTimeRatio;
+ m_TimebarControl->SetTimeRatio(inTimeRatio);
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ (*thePos)->SetTimeRatio(inTimeRatio);
+ }
+
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->SetTimeRatio(inTimeRatio);
+ }
+}
+
+//=============================================================================
+/**
+ * Called when a child becomes visible/invisible.
+ */
+void CBaseStateRow::OnChildVisibilityChanged()
+{
+ m_TreeControl->SetToggleVisible(this->HasVisibleChildren());
+}
+
+//=============================================================================
+/**
+ * Called when the mouse is double clicked.
+ * @param inPoint location of the mouse at time of event
+ * @param inFlags modifier key states at time of event
+ */
+void CBaseStateRow::OnMouseDoubleClick(CPt, Qt::KeyboardModifiers inFlags)
+{
+ // Do nothing by default. Let subclasses define what to do.
+ Q_UNUSED(inFlags);
+}
+
+//=============================================================================
+/**
+ * Show context menu for this row
+ */
+void CBaseStateRow::OnMouseRDown(CPt inPoint, Qt::KeyboardModifiers inFlags)
+{
+ Q_UNUSED(inFlags);
+
+ Select(SBaseStateRowSelectionKeyState()); // ensure this is selected, but doesn't affect any key
+ // selections, because this can be triggered from a
+ // key being selected
+ CComponentContextMenu theMenu(m_TreeControl, m_TimelineItemBinding);
+ m_TreeControl->DoPopup(&theMenu, inPoint);
+}
+
+//=============================================================================
+/**
+ * Selects keys in a given rect
+ * @param inRect the rect to use for selection
+ */
+void CBaseStateRow::SelectKeysInRect(CRct inRect, bool inModifierKeyDown,
+ bool inGlobalCommitSelectionFlag)
+{
+ CRct theOffsetRect = inRect;
+ theOffsetRect.Offset(-m_TimebarList.GetPosition());
+
+ // Commits the keyframe selection by setting the keyframes' previous state to its current state,
+ // when the user releases the mouse button.
+ // This will help the keyframes to retain their original states even though they are
+ // not in the mouse select region.
+ if (inGlobalCommitSelectionFlag) {
+ m_TimebarControl->CommitSelections();
+
+ // iterates through every property row and commits the selection states of properties
+ // keyframes
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow && thePropRow->IsViewable())
+ thePropRow->CommitSelections();
+ }
+ }
+
+ if (m_IsExpanded) {
+ // Iterates each property row and select the keys that are in the rectangle
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow && thePropRow->IsViewable())
+ thePropRow->SelectKeysInRect(theOffsetRect, inModifierKeyDown);
+ }
+
+ // Recurse the each state row (or master row) and selects the property keyframes in them
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ (*thePos)->SelectKeysInRect(theOffsetRect, inModifierKeyDown,
+ inGlobalCommitSelectionFlag);
+
+ } else {
+ // Selects all the master key frames in the rect
+ m_TimebarControl->SelectKeysInRect(theOffsetRect, inModifierKeyDown);
+ }
+}
+
+//=============================================================================
+/**
+ * Deletes all the keys for the asset that was chosen by the user
+ * @param inBatch the batch used to batch all the deletes together
+ */
+void CBaseStateRow::DeleteAllKeys()
+{
+ // Iterate through all the property rows and delete all their keys
+ TPropertyRowList::iterator thePropPos = m_PropertyRows.begin();
+ for (; thePropPos != m_PropertyRows.end(); ++thePropPos) {
+ CPropertyRow *thePropRow = (*thePropPos);
+ if (thePropRow)
+ thePropRow->DeleteAllKeys();
+ }
+}
+
+//=============================================================================
+/**
+ * Add snapping points to inSnappingList.
+ * This will add the snapping points for any visible objects to inSnappingList.
+ * @param inSnappingList the list to add the snapping points to.
+ */
+void CBaseStateRow::PopulateSnappingList(CSnapper *inSnappingList)
+{
+ inSnappingList->PushOffset(-m_TimebarList.GetPosition().y);
+ m_TimebarControl->PopulateSnappingList(inSnappingList);
+
+ if (IsExpanded()) {
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ (*thePos)->PopulateSnappingList(inSnappingList);
+ }
+ }
+}
+
+//=============================================================================
+/**
+ * Sets all the child control enable states
+ * @param inEnabled the state to set the controls to
+ */
+void CBaseStateRow::SetEnabled(bool inEnabled)
+{
+ m_TreeControl->SetEnabled(inEnabled);
+ m_ToggleControl->SetEnabled(inEnabled);
+ m_ColorControl->SetEnabled(inEnabled);
+ m_TimebarControl->SetEnabled(inEnabled);
+}
+
+//=============================================================================
+/**
+ * Begin dragging.
+ * sk - potential spot for refactoring the Drag&Drop implementation.
+ * Right now, each IDragable is implicitly assumed to be a asset implementation. See
+ * *DropSource.cpp: each IDragable is dynamically cast to its implementation.
+ */
+void CBaseStateRow::DoStartDrag(CControlWindowListener *inWndListener)
+{
+ m_TimelineItemBinding->DoStartDrag(inWndListener);
+}
+
+void CBaseStateRow::AcceptDropAfter(bool inAccept)
+{
+ m_TreeControl->AcceptDropAfter(inAccept);
+}
+
+void CBaseStateRow::AcceptDropBefore(bool inAccept)
+{
+ m_TreeControl->AcceptDropBefore(inAccept);
+}
+
+//=============================================================================
+/**
+ * Pass through to the binding to set up the target aset for a drag&drop action on this
+ *control.
+ */
+void CBaseStateRow::SetDropTarget(CDropTarget *inDropTarget)
+{
+ m_TimelineItemBinding->SetDropTarget(inDropTarget);
+}
+
+void CBaseStateRow::SetTimelineLatestTime(long inTime)
+{
+ long theLength = ::TimeToPos(inTime, m_TimeRatio) + CTimelineTimelineLayout::END_BUFFER_SIZE;
+ m_TimebarControl->SetAbsoluteSize(CPt(theLength, m_TimebarControl->GetSize().y));
+
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos)
+ (*thePos)->SetTimelineLatestTime(inTime);
+}
+
+//=============================================================================
+/**
+ * Determines whether or not a row is expanded. A row can be expanded even if
+ * it is not visible.
+ * @return true if the row is currently expanded, otherwise false
+ */
+bool CBaseStateRow::IsExpanded()
+{
+ return m_IsExpanded;
+}
+
+//=============================================================================
+/**
+ * Determines whether or not a row is loaded. The rows are delayed loaded, i.e.
+ * it will be loaded when it's visible for the first time. Before it's loaded, any
+ * updates to the structure, say, adding dynamic properties does not need to update the
+ * timeline.
+ * @return true if the row is currently loaded, otherwise false
+ */
+bool CBaseStateRow::IsLoaded()
+{
+ return m_Loaded;
+}
+
+long CBaseStateRow::GetStartTime()
+{
+ ITimelineTimebar *theTimebar = m_TimelineItemBinding->GetTimelineItem()->GetTimebar();
+ if (theTimebar)
+ return theTimebar->GetStartTime();
+ return 0;
+}
+
+long CBaseStateRow::GetEndTime()
+{
+ ITimelineTimebar *theTimebar = m_TimelineItemBinding->GetTimelineItem()->GetTimebar();
+ if (theTimebar)
+ return theTimebar->GetEndTime();
+ return 0;
+}
+
+long CBaseStateRow::GetActiveStart()
+{
+ return m_ActiveStart;
+}
+long CBaseStateRow::GetActiveEnd()
+{
+ return m_ActiveEnd;
+}
+
+//=============================================================================
+/**
+ * Get the start time of this row, which is accumulative of all its descendants.
+ */
+long CBaseStateRow::GetEarliestStartTime()
+{
+ long theEarliestStartTime = 0;
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ long theStartTime = theRow->GetEarliestStartTime();
+ if (theStartTime < theEarliestStartTime)
+ theEarliestStartTime = theStartTime;
+ }
+ return theEarliestStartTime;
+}
+
+//=============================================================================
+/**
+ * Get the end time of this row, which is accumulative of all its descendants.
+ */
+long CBaseStateRow::GetLatestEndTime()
+{
+ long theLatestTime = 0;
+ TStateRowList::iterator thePos = m_StateRows.begin();
+ for (; thePos != m_StateRows.end(); ++thePos) {
+ CStateRow *theRow = (*thePos);
+ long theEndTime = theRow->GetLatestEndTime();
+ if (theEndTime > theLatestTime)
+ theLatestTime = theEndTime;
+ }
+ return theLatestTime;
+}
+
+//=============================================================================
+/**
+ * Lame switch to get the normal state object specific icon.
+ * @return the icon to be used in the 'normal' state.
+ */
+QPixmap CBaseStateRow::GetIcon()
+{
+ return CResourceCache::GetInstance()->GetBitmap(
+ CStudioObjectTypes::GetNormalIconName(GetObjectType()));
+}
+
+//=============================================================================
+/**
+ * Lame switch to get the disabled state object specific icon.
+ * @return the icon to be used in the disabled state.
+ */
+QPixmap CBaseStateRow::GetDisabledIcon()
+{
+ return CResourceCache::GetInstance()->GetBitmap(
+ CStudioObjectTypes::GetDisabledIconName(GetObjectType()));
+}
+
+//=============================================================================
+/**
+ * @return the studio type of the object represented by this row
+ */
+EStudioObjectType CBaseStateRow::GetObjectType() const
+{
+ return GetTimelineItem()->GetObjectType();
+}
+
+ITimelineItemBinding *CBaseStateRow::GetTimelineItemBinding() const
+{
+ return m_TimelineItemBinding;
+}
+
+ITimelineItem *CBaseStateRow::GetTimelineItem() const
+{
+ return m_TimelineItemBinding->GetTimelineItem();
+}
+
+//=============================================================================
+/**
+ * When this row is no longer useful, clean up.
+ */
+void CBaseStateRow::Dispose()
+{
+ // Disconnection point
+ if (m_TimelineItemBinding)
+ m_TimelineItemBinding->Release();
+
+ CTimelineRow::Dispose();
+}
+
+void CBaseStateRow::UpdateActionStatus()
+{
+ m_ColorControl->UpdateIconStatus();
+}
+
+//=============================================================================
+/**
+ * Restores the focus state of this row.
+ */
+void CBaseStateRow::SetFocus()
+{
+ CControl *theParent = m_TreeControl->GetParent();
+ if (theParent)
+ theParent->GrabFocus(m_TreeControl);
+}
+
+CBaseTimebarlessRow *CBaseStateRow::GetTimebar() const
+{
+ return m_TimebarControl;
+}
+
+void CBaseStateRow::SetNameReadOnly(bool inReadOnly)
+{
+ m_TreeControl->SetNameReadOnly(inReadOnly);
+}