Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1994 lines
64 KiB

//====== Copyright © 1996-2005, Valve Corporation, All rights reserved. =======
//
// Purpose:
//
//=============================================================================
#include "dme_controls/BaseAnimSetControlGroupPanel.h"
#include "vgui_controls/TreeView.h"
#include "vgui_controls/Menu.h"
#include "vgui_controls/Button.h"
#include "vgui_controls/ImageList.h"
#include "vgui_controls/Tooltip.h"
#include "tier1/KeyValues.h"
#include "movieobjects/dmeanimationset.h"
#include "movieobjects/dmegamemodel.h"
#include "movieobjects/dmerig.h"
#include "movieobjects/dmetransformcontrol.h"
#include "dme_controls/BaseAnimSetAttributeSliderPanel.h"
#include "dme_controls/BaseAnimationSetEditor.h"
#include "dme_controls/dmecontrols_utils.h"
#include "vgui/ISystem.h"
#include "vgui/ISurface.h"
#include "vgui/IInput.h"
#include "vgui/IVGui.h"
#include "vgui/ILocalize.h"
// memdbgon must be the last include file in a .cpp file!!!
#include "tier0/memdbgon.h"
using namespace vgui;
enum StateIcons_t
{
STATE_ICON_WORK_CAMERA_PARENT_ACTIVE,
STATE_ICON_WORK_CAMERA_PARENT_HIDDEN,
STATE_ICON_DAG_LOCKED,
STATE_ICON_DAG_LOCKED_PARTIAL,
STATE_ICON_DAG_LOCKED_WORLD,
STATE_ICON_DAG_LOCKED_WORLD_PARTIAL,
STATE_ICON_COUNT
};
enum StateIconSetType_t
{
STATE_ICON_SET_INVALID,
STATE_ICON_SET_GROUP,
STATE_ICON_SET_CONTROL_POSITION,
STATE_ICON_SET_CONTROL_ROTATION,
};
//-----------------------------------------------------------------------------
// A panel containing a set of icons providing information about the state of
// an animation group tree item.
//-----------------------------------------------------------------------------
class CAnimGroupStateIconSet : public Panel
{
DECLARE_CLASS_SIMPLE( CAnimGroupStateIconSet, Panel );
// The sole purpose of the icon button is to allow right clicks
// to be handled and used for context menu creation.
class IconButton : public Button
{
DECLARE_CLASS_SIMPLE( IconButton, Button );
public:
IconButton( CAnimGroupStateIconSet *pIconSet, const char *pName )
: Button( pIconSet, pName, "" )
, m_pIconSet( pIconSet )
{}
void OnMousePressed( MouseCode code )
{
if ( code == MOUSE_RIGHT )
{
m_pIconSet->IconButtonRightClick();
return;
}
BaseClass::OnMousePressed( code );
}
CAnimGroupStateIconSet *const m_pIconSet;
};
public:
CAnimGroupStateIconSet( Panel *pParent, const char *pchName, StateIconSetType_t stateType, CDmeDag *pDag, ImageList &imageList, const int *pImageIndexMap );
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PerformLayout();
virtual bool IsDroppable( CUtlVector< KeyValues * > &msglist );
virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist );
void IconButtonRightClick();
void UpdateState();
private:
MESSAGE_FUNC( OnLockDagButton, "LockDagButton" );
static const CDmeDag *GetDagFromDragElement( CDmElement *pElement );
static const int LOCKED_TOOLTIP_DELAY = 750;
static const int UNLOCKED_TOOLTIP_DELAY = 1500;
ImageList &m_ImageList;
const int *const m_pImageIndexMap;
const int m_StateType;
CDmeDag *m_pDag;
IconButton *m_pLockButton;
};
CAnimGroupStateIconSet::CAnimGroupStateIconSet( Panel *pParent, const char *pchName, StateIconSetType_t itemType, CDmeDag *pDag, ImageList &imageList, const int *pImageIndexMap )
: BaseClass( pParent, "AnimGroupStateIconSet" )
, m_ImageList( imageList )
, m_pImageIndexMap( pImageIndexMap )
, m_StateType( itemType )
, m_pDag( pDag )
, m_pLockButton( NULL )
{
m_pLockButton = new IconButton( this, "LockButton" );
m_pLockButton->SetVisible( true );
m_pLockButton->AddActionSignalTarget( this );
m_pLockButton->SetCommand( new KeyValues( "LockDagButton" ) );
m_pLockButton->SetKeyBoardInputEnabled( false );
vgui::BaseTooltip *pTooltip = m_pLockButton->GetTooltip();
if ( pTooltip )
{
pTooltip->SetTooltipDelay( UNLOCKED_TOOLTIP_DELAY );
pTooltip->SetText( "#LockButtonTip" );
pTooltip->SetTooltipFormatToSingleLine();
}
SetDropEnabled( true );
}
void CAnimGroupStateIconSet::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
Color buttonColor = pScheme->GetColor( "Frame.BgColor", Color( 0, 0, 0, 0 ) );
SetBgColor( buttonColor );
if ( m_pLockButton )
{
m_pLockButton->SetDefaultBorder( pScheme->GetBorder( "DepressedBorder" ) );
}
}
void CAnimGroupStateIconSet::PerformLayout()
{
int nHeight = GetTall();
if ( m_pLockButton )
{
m_pLockButton->SetBounds( 1, 1, nHeight - 1, nHeight - 1 );
}
}
const CDmeDag *CAnimGroupStateIconSet::GetDagFromDragElement( CDmElement *pElement )
{
if ( pElement == NULL )
return NULL;
const CDmeDag *pDag = NULL;
CDmeTransformControl *pTransformControl = CastElement< CDmeTransformControl >( pElement );
if ( pTransformControl )
{
pDag = pTransformControl->GetDag();
}
else
{
pDag = CastElement< CDmeDag >( pElement );
}
return pDag;
}
bool CAnimGroupStateIconSet::IsDroppable( CUtlVector< KeyValues * > &msglist )
{
int nCount = msglist.Count();
if ( nCount != 1 )
return false;
KeyValues *pData = msglist[ 0 ];
CDmElement *pElement = GetElementKeyValue< CDmElement >( pData, "dmeelement" );
const CDmeDag *pParentDag = GetDagFromDragElement( pElement );
if ( pParentDag == NULL )
return false;
// Can't parent a dag to is child
if ( m_pDag->IsAncestorOfDag( pParentDag ) )
return false;
// Can't parent a dag to itself.
if ( m_pDag == pParentDag )
return false;
return true;
}
void CAnimGroupStateIconSet::OnPanelDropped( CUtlVector< KeyValues * >& msglist )
{
int nCount = msglist.Count();
if ( nCount == 1 )
{
KeyValues *pData = msglist[ 0 ];
CDmElement *pElement = GetElementKeyValue< CDmElement >( pData, "dmeelement" );
const CDmElement *pParentDag = GetDagFromDragElement( pElement );
if ( pParentDag )
{
bool bPosition = ( ( m_StateType == STATE_ICON_SET_GROUP ) || ( m_StateType == STATE_ICON_SET_CONTROL_POSITION ) );
bool bRotation = ( ( m_StateType == STATE_ICON_SET_GROUP ) || ( m_StateType == STATE_ICON_SET_CONTROL_ROTATION ) );
KeyValues *pMsgKv = new KeyValues( "SetOverrideParent" );
pMsgKv->SetInt( "targetDag", m_pDag->GetHandle() );
pMsgKv->SetInt( "parentDag", pParentDag->GetHandle() );
pMsgKv->SetBool( "position", bPosition );
pMsgKv->SetBool( "rotation", bRotation );
PostMessage( GetParent(), pMsgKv, 0.0f );
}
}
}
void CAnimGroupStateIconSet::IconButtonRightClick()
{
bool bPosition = ( ( m_StateType == STATE_ICON_SET_GROUP ) || ( m_StateType == STATE_ICON_SET_CONTROL_POSITION ) );
bool bRotation = ( ( m_StateType == STATE_ICON_SET_GROUP ) || ( m_StateType == STATE_ICON_SET_CONTROL_ROTATION ) );
KeyValues *pMsgKv = new KeyValues( "OpenLockContextMenu" );
pMsgKv->SetInt( "targetDag", m_pDag->GetHandle() );
pMsgKv->SetBool( "position", bPosition );
pMsgKv->SetBool( "rotation", bRotation );
PostMessage( GetParent(), pMsgKv, 0.0f );
}
void CAnimGroupStateIconSet::UpdateState()
{
if ( m_pDag == NULL )
return;
m_pLockButton->ClearImages();
vgui::BaseTooltip *pTooltip = m_pLockButton->GetTooltip();
bool bPos = false;
bool bRot = false;
const CDmeDag *pOverrideParent = m_pDag->GetOverrideParent( bPos, bRot, true );
if ( pOverrideParent != NULL )
{
bool bLockedToWorld = ( pOverrideParent->GetParent() == NULL );
int nFullLockedIcon = bLockedToWorld ? STATE_ICON_DAG_LOCKED_WORLD : STATE_ICON_DAG_LOCKED;
int nPartialLockedIcon = bLockedToWorld ? STATE_ICON_DAG_LOCKED_WORLD_PARTIAL : STATE_ICON_DAG_LOCKED_PARTIAL;
// Change the image to the grayed out version if both position and rotation are not overridden
int nImageIndex = 0;
if ( m_StateType == STATE_ICON_SET_GROUP )
{
nImageIndex = ( bPos && bRot ) ? m_pImageIndexMap[ nFullLockedIcon ] : m_pImageIndexMap[ nPartialLockedIcon ];
}
else if ( ( ( m_StateType == STATE_ICON_SET_CONTROL_POSITION ) && bPos ) ||
( ( m_StateType == STATE_ICON_SET_CONTROL_ROTATION ) && bRot ) )
{
nImageIndex = m_pImageIndexMap[ nFullLockedIcon ];
}
// Update the image of on the button
vgui::IImage *pLockImage = m_ImageList.GetImage( nImageIndex );
m_pLockButton->AddImage( pLockImage, 0 );
// Update the tool tip text describing what dag is new parent
if ( pTooltip )
{
if ( bLockedToWorld )
{
pTooltip->SetText( "#LockedToWorld" );
}
else
{
const wchar_t *pLabel = g_pVGuiLocalize->Find( "#LockedTo" );
if ( pLabel )
{
char itemText[ 32 ];
char tipText[ 64 ];
g_pVGuiLocalize->ConvertUnicodeToANSI( pLabel, itemText, sizeof( itemText ) );
V_snprintf( tipText, sizeof( tipText ), "%s %s", itemText, pOverrideParent->GetName() );
pTooltip->SetText( tipText );
}
}
pTooltip->SetTooltipDelay( LOCKED_TOOLTIP_DELAY );
}
}
else
{
pTooltip->SetTooltipDelay( UNLOCKED_TOOLTIP_DELAY );
pTooltip->SetText( "#LockButtonTip" );
}
}
void CAnimGroupStateIconSet::OnLockDagButton()
{
if ( m_pDag )
{
bool bPosition = ( ( m_StateType == STATE_ICON_SET_GROUP ) || ( m_StateType == STATE_ICON_SET_CONTROL_POSITION ) );
bool bRotation = ( ( m_StateType == STATE_ICON_SET_GROUP ) || ( m_StateType == STATE_ICON_SET_CONTROL_ROTATION ) );
KeyValues *pMsgKv = new KeyValues( "ToggleDagLock" );
pMsgKv->SetInt( "targetDag", m_pDag->GetHandle() );
pMsgKv->SetBool( "position", bPosition );
pMsgKv->SetBool( "rotation", bRotation );
PostMessage( GetParent(), pMsgKv, 0.0f );
}
}
//-----------------------------------------------------------------------------
// Shows the tree view of the animation groups
//-----------------------------------------------------------------------------
class CAnimGroupTree : public TreeView
{
DECLARE_CLASS_SIMPLE( CAnimGroupTree, TreeView );
public:
CAnimGroupTree( Panel *parent, const char *panelName, CBaseAnimSetControlGroupPanel *groupPanel, bool bStateInterface );
virtual ~CAnimGroupTree();
virtual bool IsItemDroppable( int itemIndex, bool bInsertBefore, CUtlVector< KeyValues * >& msglist );
virtual void OnItemDropped( int itemIndex, bool bInsertBefore, CUtlVector< KeyValues * >& msglist );
virtual bool CanCurrentlyEditLabel( int nItemIndex ) const;
virtual void OnLabelChanged( int nItemIndex, char const *pOldString, char const *pNewString );
virtual void GetSelectedItemsForDrag( int nPrimaryDragItem, CUtlVector< int >& list );
virtual void GenerateDragDataForItem( int itemIndex, KeyValues *msg );
virtual void GenerateContextMenu( int itemIndex, int x, int y );
virtual void GenerateChildrenOfNode(int itemIndex);
virtual void RemoveItem(int itemIndex, bool bPromoteChildren, bool bRecursivelyRemove = false );
virtual void RemoveAll();
virtual void ApplySchemeSettings( IScheme *pScheme );
virtual void PaintBackground();
virtual void PerformLayout();
virtual void OnTick();
virtual void OnMousePressed( MouseCode code );
// Item add helpers
int AddAnimationSetToTree( CDmeAnimationSet *pAnimSet );
int AddControlGroupToTree( int parentIndex, CDmeControlGroup *pControlGroup, CDmeControlGroup *pParentGroup, CDmeAnimationSet *pAnimSet );
int AddControlToTree( int parentIndex, CDmElement *pControl, CDmeControlGroup *pControlGroup, CDmeAnimationSet *pAnimSet );
void AddTransformComponentsToTree( int parentIndex, CDmeTransformControl *pTransformControl, CDmeControlGroup *pControlGroup, CDmeAnimationSet *pAnimSet, TransformComponent_t nComponentFlags );
CDmElement *GetTreeItemData( int nTreeIndex, AnimTreeItemType_t *pItemType = NULL,
CDmeAnimationSet **ppParentAnimationSet = NULL,
CDmeControlGroup **ppParentControlGroup = NULL ) const;
// Get the component flags associated with the specified item
TransformComponent_t GetItemComponentFlags( int nTreeIndex ) const;
// Get the control group associated with the specified tree item
CDmeControlGroup *GetControlGroupForTreeItem( int nTreeItemIndex ) const;
// Get the state icon set panel associated with the specified tree item
CAnimGroupStateIconSet *GetTreeItemStateIconSet( int nTreeItemIndex );
// Get the dag node associated with the specified tree item
CDmeDag *GetDagForTreeItem( int nTreeItemIndex ) const;
// Find the child of the specified item which has the specified element as its "handle" value
int FindChildItemForElement( int nParentIndex, const CDmElement *pElement, TransformComponent_t nComponentFlags = TRANSFORM_COMPONENT_NONE );
// Find the index of the the tree view item that has the specified element as its "handle" value
int FindItemForElement( const CDmElement *pElement, TransformComponent_t nComponentFlags = TRANSFORM_COMPONENT_NONE );
// Build the tree view items from the root down to the specified animation set
int BuildTreeToAnimationSet( CDmeAnimationSet *pAnimationSet );
// Build the tree view items from the root down to the specified control group
int BuildTreeToGroup( CDmeControlGroup *pGroup, CDmeAnimationSet *pAnimationSet );
// Build the tree view items from the root down to the specified control
void BuildTreeToControl( const CDmElement *pElement, TransformComponent_t nComponentFlags );
// Set the selection state on the specified item
void SetItemSelectionState( int nItemIndex, SelectionState_t selectionState );
// Get the selection state of the specified item
SelectionState_t GetItemSelectionState( int nItemIndex ) const;
// Get a list of the controls which are fully selected and whose parents are not fully selected
void GetSelectionRootItems( CUtlVector< int > &selectedItems ) const;
private:
MESSAGE_FUNC( OnClearWorkCameraParent, "ClearWorkCameraParent" );
MESSAGE_FUNC_INT( OnResetTransformPivot, "OnResetTransformPivot", viewCenter );
MESSAGE_FUNC_PARAMS( OnToggleDagLock, "ToggleDagLock", params );
MESSAGE_FUNC_PARAMS( OnSetOverrideParent, "SetOverrideParent", params );
MESSAGE_FUNC_PARAMS( OnOpenLockContextMenu, "OpenLockContextMenu", params );
void CleanupContextMenu();
virtual void OnContextMenuSelection( int itemIndex );
int AddItemToTree(
AnimTreeItemType_t itemType,
const char *label,
int parentIndex,
const Color& fg,
CDmElement *pElement,
CDmeAnimationSet *pAnimSet,
CDmeControlGroup *pControlGroup,
bool bExpandable,
SelectionState_t selection,
TransformComponent_t nComponentFlags );
Color ModifyColorByGroupState( const Color &baseColor, CDmeControlGroup *pControlGroup );
void AddDmeControlGroup( int nParentItemIndex, CDmeAnimationSet *pAnimationSet, CDmeControlGroup *pGroup );
bool VisibleControlsBelow_R( CDmeControlGroup *pGroup );
static bool CanAddDragIntoGroup( const CDmeControlGroup *pTargetGroup, const CDmElement *pTargetElement, const CDmElement *pDragElement, bool bInsertBefore );
static SelectionState_t GetSelectionStateForFlags( int nBaseFlags, int nSelectionFlags );
vgui::DHANDLE< vgui::Menu > m_hContextMenu;
CBaseAnimSetControlGroupPanel *m_pGroupPanel;
Button *m_pWorkCameraParentButton;
ImageList m_Images;
int m_StateIconIndices[ STATE_ICON_COUNT ];
Color m_RootColor;
Color m_StateColumnColor;
int m_nStateColumnWidth;
bool m_bStateInterface;
};
CAnimGroupTree::CAnimGroupTree( Panel *parent, const char *panelName, CBaseAnimSetControlGroupPanel *groupPanel, bool bStateInterface ) :
BaseClass( parent, panelName ),
m_pGroupPanel( groupPanel ),
m_Images( false ),
m_RootColor( 128, 128, 128, 255 ),
m_StateColumnColor( 0, 0, 0, 0 ),
m_nStateColumnWidth( 0 ),
m_bStateInterface( bStateInterface )
{
if ( m_bStateInterface )
{
m_nStateColumnWidth = 20;
SetTreeIndent( m_nStateColumnWidth - 2 );
}
SetShowRootNode( false );
SetDragEnabledItems( true );
SetAllowLabelEditing( true );
SetEnableInsertDropLocation( true );
m_pWorkCameraParentButton = new Button( this, "workCameraParent", "" );
m_pWorkCameraParentButton->SetVisible( false );
m_pWorkCameraParentButton->AddActionSignalTarget( this );
m_pWorkCameraParentButton->SetCommand( new KeyValues( "ClearWorkCameraParent" ) );
m_pWorkCameraParentButton->SetKeyBoardInputEnabled( false );
m_StateIconIndices[ STATE_ICON_WORK_CAMERA_PARENT_ACTIVE ] = m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_referenceframe_active", false ) );
m_StateIconIndices[ STATE_ICON_WORK_CAMERA_PARENT_HIDDEN ] = m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_referenceframe_active_hidden", false ) );
m_StateIconIndices[ STATE_ICON_DAG_LOCKED ] = m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_dag_locked", false ) );
m_StateIconIndices[ STATE_ICON_DAG_LOCKED_PARTIAL ] = m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_dag_locked_grey", false ) );
m_StateIconIndices[ STATE_ICON_DAG_LOCKED_WORLD ] = m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_dag_locked_world", false ) );
m_StateIconIndices[ STATE_ICON_DAG_LOCKED_WORLD_PARTIAL ] = m_Images.AddImage( scheme()->GetImage( "tools/ifm/icon_dag_locked_world_grey", false ) );
ivgui()->AddTickSignal( GetVPanel(), 0 );
}
CAnimGroupTree::~CAnimGroupTree()
{
CleanupContextMenu();
}
void CAnimGroupTree::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
m_RootColor = pScheme->GetColor( "AnimSet.RootColor", Color( 128, 128, 128, 255 ) );
m_StateColumnColor = pScheme->GetColor( "Frame.BgColor", Color( 0, 0, 0, 0 ) );
SetFont( pScheme->GetFont( "DefaultBold", IsProportional() ) );
}
int CAnimGroupTree::AddAnimationSetToTree( CDmeAnimationSet *pAnimSet )
{
// Don't add the animation set if the root control group is not visible
CDmeControlGroup *pRootGroup = pAnimSet->GetRootControlGroup();
if ( !m_pGroupPanel->m_pController->IsControlGroupVisible( pRootGroup ) )
return -1;
int parentIndex = GetRootItemIndex();
SelectionState_t selection = m_pGroupPanel->m_pController->GetSelectionState( pAnimSet );
Color groupColor = ModifyColorByGroupState( m_RootColor, pRootGroup );
return AddItemToTree( ANIMTREE_ITEM_ANIMSET, pAnimSet->GetName(), parentIndex, groupColor, pAnimSet, pAnimSet, NULL, true, selection, TRANSFORM_COMPONENT_NONE );
}
int CAnimGroupTree::AddControlGroupToTree( int parentIndex, CDmeControlGroup *pControlGroup, CDmeControlGroup *pParentGroup, CDmeAnimationSet *pAnimSet )
{
SelectionState_t selection = m_pGroupPanel->m_pController->GetSelectionState( pControlGroup );
Color groupColor = ModifyColorByGroupState( pControlGroup->GroupColor(), pControlGroup );
return AddItemToTree( ANIMTREE_ITEM_GROUP, pControlGroup->GetName(), parentIndex, groupColor, pControlGroup, pAnimSet, pParentGroup, true, selection, TRANSFORM_COMPONENT_NONE );
}
int CAnimGroupTree::AddControlToTree( int parentIndex, CDmElement *pControl, CDmeControlGroup *pControlGroup, CDmeAnimationSet *pAnimSet )
{
SelectionState_t selection = m_pGroupPanel->m_pController->GetSelectionState( pControl );
bool bTransformControl = pControl->IsA( CDmeTransformControl::GetStaticTypeSymbol() );
TransformComponent_t nComponentFlags = bTransformControl ? TRANSFORM_COMPONENT_ALL : TRANSFORM_COMPONENT_NONE;
Color controlColor = ModifyColorByGroupState( pControlGroup->ControlColor(), pControlGroup );
return AddItemToTree( ANIMTREE_ITEM_CONTROL, pControl->GetName(), parentIndex, controlColor, pControl, pAnimSet, pControlGroup, bTransformControl, selection, nComponentFlags );
}
SelectionState_t CAnimGroupTree::GetSelectionStateForFlags( int nBaseFlags, int nSelectionFlags )
{
int nMaskedFlags = nBaseFlags & nSelectionFlags;
if ( nMaskedFlags == nBaseFlags )
return SEL_ALL;
return ( ( nMaskedFlags > 0 ) ? SEL_SOME : SEL_NONE );
}
void CAnimGroupTree::AddTransformComponentsToTree( int nParentIndex, CDmeTransformControl *pControl, CDmeControlGroup *pControlGroup, CDmeAnimationSet *pAnimSet, TransformComponent_t nParentComponentFlags )
{
int nSelectionFlags = m_pGroupPanel->m_pController->GetSelectionComponentFlags( pControl );
const char *pName = pControl->GetName();
Color color = ModifyColorByGroupState( pControlGroup->ControlColor(), pControlGroup );
if ( nParentComponentFlags == TRANSFORM_COMPONENT_ALL )
{
SelectionState_t posSelection = GetSelectionStateForFlags( TRANSFORM_COMPONENT_POSITION, nSelectionFlags );
SelectionState_t rotSelection = GetSelectionStateForFlags( TRANSFORM_COMPONENT_ROTATION, nSelectionFlags );
AddItemToTree( ANIMTREE_ITEM_COMPONENT, CFmtStr( "%s - pos", pName ), nParentIndex, color, pControl, pAnimSet, pControlGroup, true, posSelection, TRANSFORM_COMPONENT_POSITION );
AddItemToTree( ANIMTREE_ITEM_COMPONENT, CFmtStr( "%s - rot", pName ), nParentIndex, color, pControl, pAnimSet, pControlGroup, true, rotSelection, TRANSFORM_COMPONENT_ROTATION );
}
else if ( nParentComponentFlags == TRANSFORM_COMPONENT_POSITION )
{
AddItemToTree( ANIMTREE_ITEM_COMPONENT, CFmtStr( "%s - pos.x", pName ), nParentIndex, color, pControl, pAnimSet, NULL, false, GetSelectionStateForFlags( TRANSFORM_COMPONENT_POSITION_X, nSelectionFlags ), TRANSFORM_COMPONENT_POSITION_X );
AddItemToTree( ANIMTREE_ITEM_COMPONENT, CFmtStr( "%s - pos.y", pName ), nParentIndex, color, pControl, pAnimSet, NULL, false, GetSelectionStateForFlags( TRANSFORM_COMPONENT_POSITION_Y, nSelectionFlags ), TRANSFORM_COMPONENT_POSITION_Y );
AddItemToTree( ANIMTREE_ITEM_COMPONENT, CFmtStr( "%s - pos.z", pName ), nParentIndex, color, pControl, pAnimSet, NULL, false, GetSelectionStateForFlags( TRANSFORM_COMPONENT_POSITION_Z, nSelectionFlags ), TRANSFORM_COMPONENT_POSITION_Z );
}
else if ( nParentComponentFlags == TRANSFORM_COMPONENT_ROTATION )
{
AddItemToTree( ANIMTREE_ITEM_COMPONENT, CFmtStr( "%s - rot.x", pName ), nParentIndex, color, pControl, pAnimSet, NULL, false, GetSelectionStateForFlags( TRANSFORM_COMPONENT_ROTATION_X, nSelectionFlags ), TRANSFORM_COMPONENT_ROTATION_X );
AddItemToTree( ANIMTREE_ITEM_COMPONENT, CFmtStr( "%s - rot.y", pName ), nParentIndex, color, pControl, pAnimSet, NULL, false, GetSelectionStateForFlags( TRANSFORM_COMPONENT_ROTATION_Y, nSelectionFlags ), TRANSFORM_COMPONENT_ROTATION_Y );
AddItemToTree( ANIMTREE_ITEM_COMPONENT, CFmtStr( "%s - rot.z", pName ), nParentIndex, color, pControl, pAnimSet, NULL, false, GetSelectionStateForFlags( TRANSFORM_COMPONENT_ROTATION_Z, nSelectionFlags ), TRANSFORM_COMPONENT_ROTATION_Z );
}
}
Color CAnimGroupTree::ModifyColorByGroupState( const Color &baseColor, CDmeControlGroup *pControlGroup )
{
Color groupColor = baseColor;
int nAlpha = ( pControlGroup->IsSelectable() && pControlGroup->IsVisible() ) ? 255 : 64;
groupColor.SetColor( groupColor.r(), groupColor.g(), groupColor.b(), nAlpha );
return groupColor;
}
int CAnimGroupTree::AddItemToTree( AnimTreeItemType_t itemType, const char *label, int parentIndex, const Color& fg, CDmElement *pElement, CDmeAnimationSet *pAnimSet, CDmeControlGroup *pControlGroup, bool bExpandable, SelectionState_t selection, TransformComponent_t nComponentFlags )
{
DmElementHandle_t hElement = pElement ? pElement->GetHandle() : DMELEMENT_HANDLE_INVALID;
DmElementHandle_t hAnimSet = pAnimSet ? pAnimSet->GetHandle() : DMELEMENT_HANDLE_INVALID;
DmElementHandle_t hControlGroup = pControlGroup ? pControlGroup->GetHandle() : DMELEMENT_HANDLE_INVALID;
KeyValues *kv = new KeyValues( "item", "text", label );
kv->SetInt( "droppable", 1 );
kv->SetInt( "itemType", ( int )itemType );
kv->SetInt( "handle", ( int )hElement );
kv->SetInt( "animset", ( int )hAnimSet );
kv->SetInt( "controlgroup", ( int )hControlGroup );
kv->SetInt( "selection", ( int )selection );
kv->SetInt( "componentFlags", ( int )nComponentFlags );
kv->SetInt( "Expand", bExpandable ? 1 : 0 );
CDmeTransformControl *pTransformControl = CastElement< CDmeTransformControl >( pElement );
CDmeTransform *pTransform = ( pTransformControl != NULL ) ? pTransformControl->GetTransform() : NULL;
CDmeDag *pDag = ( pTransform != NULL ) ? pTransform->GetDag() : NULL;
if ( m_bStateInterface )
{
StateIconSetType_t stateIconSetType = STATE_ICON_SET_INVALID;
if ( itemType == ANIMTREE_ITEM_COMPONENT )
{
if ( nComponentFlags == TRANSFORM_COMPONENT_POSITION )
{
stateIconSetType = STATE_ICON_SET_CONTROL_POSITION;
}
else if ( nComponentFlags == TRANSFORM_COMPONENT_ROTATION )
{
stateIconSetType = STATE_ICON_SET_CONTROL_ROTATION;
}
}
else if ( itemType == ANIMTREE_ITEM_CONTROL )
{
stateIconSetType = STATE_ICON_SET_GROUP;
}
if ( pDag && ( stateIconSetType != STATE_ICON_SET_INVALID ) )
{
CAnimGroupStateIconSet *pIconSet = new CAnimGroupStateIconSet( this, label, stateIconSetType, pDag, m_Images, m_StateIconIndices );
kv->SetPtr( "stateIconSet", pIconSet );
}
}
int idx = AddItem( kv, parentIndex );
SetItemFgColor( idx, fg );
SetItemSelectionTextColor( idx, fg );
SetSilentMode( true );
if ( selection == SEL_ALL || selection == SEL_SOME )
{
Color color = ( selection == SEL_ALL ) ? Color( 128, 128, 128, 128 ) : Color( 128, 128, 64, 64 );
SetItemSelectionBgColor( idx, color );
SetItemSelectionUnfocusedBgColor( idx, color );
AddSelectedItem( idx, false, false, true );
}
else
{
Color color( 0, 0, 0, 128 );
SetItemSelectionBgColor( idx, color );
SetItemSelectionUnfocusedBgColor( idx, color );
RemoveSelectedItem( idx );
}
if ( ( itemType == ANIMTREE_ITEM_GROUP ) || ( itemType == ANIMTREE_ITEM_ANIMSET ) )
{
SetLabelEditingAllowed( idx, true );
}
SetSilentMode( false );
ExpandItem( idx, false );
kv->deleteThis();
return idx;
}
CDmElement *CAnimGroupTree::GetTreeItemData( int nTreeIndex, AnimTreeItemType_t *pItemType /*= NULL */,
CDmeAnimationSet **ppParentAnimationSet /*= NULL*/,
CDmeControlGroup **ppControlGroup /*= NULL*/ ) const
{
KeyValues *kv = GetItemData( nTreeIndex );
if ( !kv )
return NULL;
if ( pItemType )
{
*pItemType = static_cast< AnimTreeItemType_t >( kv->GetInt( "itemType" ) );
}
if ( ppParentAnimationSet )
{
*ppParentAnimationSet = GetElementKeyValue< CDmeAnimationSet >( kv, "animset" );
}
if ( ppControlGroup )
{
*ppControlGroup = GetElementKeyValue< CDmeControlGroup >( kv, "controlgroup" );
}
return GetElementKeyValue< CDmElement >( kv, "handle" );
}
TransformComponent_t CAnimGroupTree::GetItemComponentFlags( int nTreeIndex ) const
{
KeyValues *kv = GetItemData( nTreeIndex );
return static_cast< TransformComponent_t >( kv->GetInt( "componentFlags" ) );
}
CDmeControlGroup *CAnimGroupTree::GetControlGroupForTreeItem( int nItemIndex ) const
{
CDmElement *pElement = GetTreeItemData( nItemIndex, NULL );
CDmeControlGroup *pControlGroup = NULL;
const CDmeAnimationSet *pAnimationSet = CastElement< CDmeAnimationSet >( pElement );
if ( pAnimationSet )
{
pControlGroup = pAnimationSet->GetRootControlGroup();
}
else
{
pControlGroup = CastElement< CDmeControlGroup >( pElement );
}
return pControlGroup;
}
CAnimGroupStateIconSet *CAnimGroupTree::GetTreeItemStateIconSet( int nTreeIndex )
{
KeyValues *kv = GetItemData( nTreeIndex );
if ( !kv )
return NULL;
return static_cast< CAnimGroupStateIconSet * >( kv->GetPtr( "stateIconSet" ) );
}
CDmeDag *CAnimGroupTree::GetDagForTreeItem( int nTreeItemIndex ) const
{
AnimTreeItemType_t itemType;
CDmElement *pElement = GetTreeItemData( nTreeItemIndex, &itemType );
if ( !pElement && itemType != ANIMTREE_ITEM_GROUP )
return NULL;
CDmeTransformControl *pTransformControl = CastElement< CDmeTransformControl >( pElement );
if ( pTransformControl == NULL )
return NULL;
CDmeTransform *pTransform = pTransformControl->GetTransform();
if ( !pTransform )
return NULL;
return pTransform->GetDag();
}
void CAnimGroupTree::CleanupContextMenu()
{
if ( m_hContextMenu.Get() )
{
delete m_hContextMenu.Get();
m_hContextMenu = NULL;
}
}
bool CAnimGroupTree::CanAddDragIntoGroup( const CDmeControlGroup *pTargetGroup, const CDmElement *pTargetElement, const CDmElement *pDragElement, bool bInsertBefore )
{
const static CUtlSymbolLarge symControls = g_pDataModel->GetSymbol( "controls" );
if ( ( pTargetGroup == NULL ) || ( pDragElement == NULL ) || ( pTargetElement == NULL ) )
return false;
// Cannot drag a group into itself
if ( pDragElement == pTargetGroup )
return false;
CDmeAnimationSet *pTargetGroupAnimSet = pTargetGroup->FindAnimationSet( true );
CDmeAnimationSet *pDragGroupAnimSet = NULL;
const CDmeControlGroup *pDragGroup = CastElement< CDmeControlGroup >( pDragElement );
bool bTargetElenentIsGroup = pTargetElement->IsA( CDmeControlGroup::GetStaticTypeSymbol() );
if ( pDragGroup )
{
// Cannot drag a group into a group that already exists in its sub-tree
if ( pDragGroup->IsAncestorOfGroup( pTargetGroup ) )
return false;
// Cannot insert a group before a control
if ( bInsertBefore && !bTargetElenentIsGroup )
return false;
CDmeControlGroup *pDragGroupParent = pDragGroup->FindParent();
pDragGroupAnimSet = ( pDragGroupParent != NULL ) ? pDragGroupParent->FindAnimationSet( true ) : NULL;
}
else
{
// Cannot insert a control before a group
if ( bInsertBefore && bTargetElenentIsGroup )
return false;
// Find the animation set to which the control belongs
pDragGroupAnimSet = FindReferringElement< CDmeAnimationSet >( pDragElement, symControls );
}
// If a control group is in the sub-tree of an animation set it must remain in the sub-tree of that animation set,
// it is not in the sub-tree of an animation set it must not be added to the sub-tree of any other animation set.
if ( pDragGroupAnimSet != pTargetGroupAnimSet )
return false;
return true;
}
bool CAnimGroupTree::IsItemDroppable( int nItemIndex, bool bInsertBefore, CUtlVector< KeyValues * >& msglist )
{
if ( msglist.Count() == 0 )
return false;
CDmeControlGroup *pParentControlGroup = NULL;
CDmElement *pTargetElement = GetTreeItemData( nItemIndex, NULL, NULL, &pParentControlGroup );
if ( pTargetElement == NULL )
return false;
CDmeControlGroup *pTargetControlGroup = GetControlGroupForTreeItem( nItemIndex );
const CDmeControlGroup *pNewParentGroup = bInsertBefore ? pParentControlGroup : pTargetControlGroup;
// See if there are any messages in the list that will apply to the control group
int nMsgCount = msglist.Count();
for ( int iMsg = 0; iMsg < nMsgCount; ++iMsg )
{
KeyValues *pData = msglist[ iMsg ];
if ( pData == NULL )
continue;
if ( pData->FindKey( "color" ) )
{
if ( pTargetControlGroup != NULL )
return true;
}
const CDmElement *pDragElement = GetElementKeyValue< CDmElement >( pData, "dmeelement" );
if ( CanAddDragIntoGroup( pNewParentGroup, pTargetElement, pDragElement, bInsertBefore ) )
return true;
}
return false;
}
void CAnimGroupTree::OnItemDropped( int nItemIndex, bool bInsertBefore, CUtlVector< KeyValues * >& msglist )
{
if ( !IsItemDroppable( nItemIndex, bInsertBefore, msglist ) )
return;
CDmeControlGroup *pParentControlGroup = NULL;
CDmeAnimationSet *pTargetAnimSet = NULL;
CDmElement *pTargetElement = GetTreeItemData( nItemIndex, NULL, &pTargetAnimSet, &pParentControlGroup );
if ( pTargetElement == NULL )
return;
CDmeControlGroup *pTargetControlGroup = GetControlGroupForTreeItem( nItemIndex );
CDmeControlGroup *pNewParentGroup = bInsertBefore ? pParentControlGroup : pTargetControlGroup;
CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Drop onto control group" );
int nMsgCount = msglist.Count();
for ( int iMsg = 0; iMsg < nMsgCount; ++iMsg )
{
KeyValues *pData = msglist[ iMsg ];
if ( pData == NULL )
continue;
if ( pData->FindKey( "color" ) )
{
Color clr = pData->GetColor( "color" );
SetItemFgColor( nItemIndex, clr );
SetItemSelectionTextColor( nItemIndex, clr );
pTargetControlGroup->SetGroupColor( clr, false );
}
CDmElement *pDragElement = GetElementKeyValue< CDmElement >( pData, "dmeelement" );
if ( CanAddDragIntoGroup( pNewParentGroup, pTargetElement, pDragElement, bInsertBefore ) )
{
CDmeControlGroup *pDragGroup = CastElement< CDmeControlGroup >( pDragElement );
if ( pDragGroup )
{
pNewParentGroup->AddChild( pDragGroup, ( bInsertBefore ? pTargetControlGroup : NULL ) );
}
else
{
pNewParentGroup->AddControl( pDragElement, ( bInsertBefore ? pTargetElement : NULL ) );
}
if ( pTargetAnimSet )
{
CDmeControlGroup *pRootControlGroup = pTargetAnimSet->GetRootControlGroup();
if ( pRootControlGroup )
{
pRootControlGroup->DestroyEmptyChildren();
}
}
}
}
}
void CAnimGroupTree::GetSelectedItemsForDrag( int nPrimaryDragItem, CUtlVector< int >& list )
{
// The item actually being dragged will be the first item in the selection list,
// since ancestors of this primary item may also be in the selection we can't
// drag the whole selection, but we do want to drag the siblings of the item.
// So iterate the selection and add any siblings of the primary selected item.
CUtlVector< int > selectedItems;
GetSelectedItems( selectedItems );
int nNumSelected = selectedItems.Count();
if ( nNumSelected <= 0 )
return;
AnimTreeItemType_t itemType;
CDmeControlGroup *pPrimaryParentGroup = NULL;
GetTreeItemData( nPrimaryDragItem, &itemType, NULL, &pPrimaryParentGroup );
if ( itemType == ANIMTREE_ITEM_COMPONENT )
return;
for ( int i = 0 ; i < nNumSelected; ++i )
{
CDmeControlGroup *pParentGroup = NULL;
GetTreeItemData( selectedItems[ i ], &itemType, NULL, &pParentGroup );
if ( ( pPrimaryParentGroup == pParentGroup ) && ( itemType != ANIMTREE_ITEM_COMPONENT ) )
{
list.AddToTail( selectedItems[ i ] );
}
}
}
void CAnimGroupTree::GenerateDragDataForItem( int nItemIndex, KeyValues *msg )
{
AnimTreeItemType_t itemType;
CDmElement *pElement = GetTreeItemData( nItemIndex, &itemType );
if ( ( pElement ) && ( itemType != ANIMTREE_ITEM_COMPONENT ) )
{
msg->SetInt( "dmeelement", pElement->GetHandle() );
}
}
bool CAnimGroupTree::CanCurrentlyEditLabel( int nItemIndex ) const
{
// The item must still be in the selection
if ( IsItemSelected( nItemIndex ) == false )
return false;
// Parents or children of the item may be selected, but siblings must not be selected.
int nParentIndex = GetItemParent( nItemIndex );
CUtlVector< int > selectedItems;
GetSelectedItems( selectedItems );
int nSelectedItems = selectedItems.Count();
for ( int iItem = 0; iItem < nSelectedItems; ++iItem )
{
int nSelectedItem = selectedItems[ iItem ];
if ( nItemIndex == nSelectedItem )
continue;
if ( GetItemParent( nSelectedItem) == nParentIndex )
return false;
}
return true;
}
void CAnimGroupTree::OnLabelChanged( int nItemIndex, char const *pOldString, char const *pNewString )
{
CUndoScopeGuard undoSg( "Change group label" );
CDmElement *pElement = GetTreeItemData( nItemIndex );
CDmeControlGroup *pControlGroup = CastElement< CDmeControlGroup >( pElement );
CDmeAnimationSet *pAnimationSet = CastElement< CDmeAnimationSet >( pElement );
if ( pControlGroup )
{
CDmeControlGroup *pRootGroup = pControlGroup->FindRootControlGroup();
if ( pRootGroup && pRootGroup->FindChildByName( pNewString, true ) )
{
CUtlVector< DmElementHandle_t > childList;
pRootGroup->GetAllChildren( childList );
int nIndex = GenerateUniqueNameIndex( pNewString, childList, 0 );
CFmtStr newName( "%s%d", pNewString, nIndex );
pControlGroup->SetName( newName.Access() );
// Force the tree display to update to reflect the modified name
m_pGroupPanel->RebuildTree( true );
}
else
{
pControlGroup->SetName( pNewString );
}
}
else if ( pAnimationSet )
{
KeyValues *pMsgKV = new KeyValues( "SetAnimationSetName", "text", pNewString );
SetElementKeyValue( pMsgKV, "animset", pAnimationSet );
PostMessage( m_pGroupPanel->GetEditor(), pMsgKV, 0.0f );
}
}
// override to open a custom context menu on a node being selected and right-clicked
void CAnimGroupTree::GenerateContextMenu( int itemIndex, int x, int y )
{
PostMessage( GetParent(), new KeyValues( "TreeViewOpenContextMenu", "itemID", itemIndex ), 0.0f );
}
//-----------------------------------------------------------------------------
// Purpose: Override the default right click selection behavior so that the
// clicked item becomes the first selected item.
//-----------------------------------------------------------------------------
void CAnimGroupTree::OnContextMenuSelection( int itemIndex )
{
// Select the item, if it was already selected
// this should make it the first selected item.
AddSelectedItem( itemIndex, !IsItemSelected( itemIndex ) );
Assert( GetFirstSelectedItem() == itemIndex );
}
void CAnimGroupTree::OnClearWorkCameraParent()
{
m_pGroupPanel->m_pController->SetWorkCameraParent( NULL );
}
void CAnimGroupTree::OnResetTransformPivot( int viewCenter )
{
PostMessage( m_pGroupPanel->GetEditor(), new KeyValues( "ResetTransformPivot", "viewCenter", viewCenter ), 0.0f );
}
void CAnimGroupTree::OnToggleDagLock( KeyValues *pParams )
{
PostMessage( m_pGroupPanel->GetEditor(), pParams->MakeCopy(), 0.0f );
// Cause perform layout to be run so that the lock icons
// are updated after the message has been processed.
InvalidateLayout();
}
void CAnimGroupTree::OnSetOverrideParent( KeyValues *pParams )
{
PostMessage( m_pGroupPanel->GetEditor(), pParams->MakeCopy(), 0.0f );
InvalidateLayout();
}
void CAnimGroupTree::OnOpenLockContextMenu( KeyValues *pParams )
{
PostMessage( m_pGroupPanel->GetEditor(), pParams->MakeCopy(), 0.0f );
}
void CAnimGroupTree::OnMousePressed( MouseCode code )
{
int mx, my;
input()->GetCursorPos( mx, my );
ScreenToLocal( mx, my );
int idx = FindItemUnderMouse( mx, my );
// The default tree behavior ignores the width of the item when testing against the mouse. For
// the animation set editor only the actual are of the item should be considered under the mouse.
if ( IsItemIDValid( idx ) )
{
int xPos, yPos, width, height;
GetItemBounds( idx, xPos, yPos, width, height );
if ( ( mx < xPos ) || ( my < yPos ) || ( mx > ( xPos + width ) ) || ( my > ( yPos + height) ) )
{
idx = -1;
}
}
bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL));
if ( !ctrl )
{
if ( code == MOUSE_RIGHT )
{
PostMessage( GetParent(), new KeyValues( "TreeViewOpenContextMenu", "itemID", idx ), 0.0f );
}
else
{
BaseClass::OnMousePressed( code );
}
return;
}
if ( !IsItemIDValid( idx ) || ( mx >= ( 20 + m_nStateColumnWidth ) ) )
{
BaseClass::OnMousePressed( code );
return;
}
CDmeTransformControl *pTransformControl = CastElement< CDmeTransformControl >( GetTreeItemData( idx ) );
if ( pTransformControl )
{
CDmeDag *pDag = pTransformControl->GetDag();
m_pGroupPanel->m_pController->SetWorkCameraParent( pDag );
}
}
void CAnimGroupTree::PaintBackground()
{
BaseClass::PaintBackground();
if ( m_bStateInterface )
{
int nHeight = GetTall();
vgui::surface()->DrawSetColor( m_StateColumnColor );
vgui::surface()->DrawFilledRect( 0, 0, m_nStateColumnWidth, nHeight );
}
}
void CAnimGroupTree::PerformLayout()
{
BaseClass::PerformLayout();
for ( int itemID = FirstItem(); itemID != InvalidItemID(); itemID = NextItem( itemID ) )
{
CAnimGroupStateIconSet *pIconSet = GetTreeItemStateIconSet( itemID );
if ( pIconSet == NULL )
continue;
int nPosX, nPosY, nWidth, nHeight;
if ( GetItemBounds( itemID, nPosX, nPosY, nWidth, nHeight ) )
{
pIconSet->UpdateState();
pIconSet->SetBounds( 0, nPosY, nHeight, nHeight );
pIconSet->SetVisible( true );
}
else
{
pIconSet->SetVisible( false );
}
}
}
void CAnimGroupTree::OnTick()
{
BaseClass::OnTick();
int nWorkCameraParentItem = -1;
if ( CDmeDag *pWorkCameraParent = m_pGroupPanel->GetWorkCameraParent() )
{
for ( int itemID = FirstItem(); itemID != InvalidItemID(); itemID = NextItem( itemID ) )
{
CDmeDag *pDag = GetDagForTreeItem( itemID );
if ( pDag == pWorkCameraParent )
{
nWorkCameraParentItem = itemID;
break;
}
}
}
m_pWorkCameraParentButton->SetVisible( nWorkCameraParentItem != -1 );
if ( nWorkCameraParentItem != -1 )
{
int x = 0, y = 0, w = 0, h = 0;
GetItemBounds( nWorkCameraParentItem, x, y, w, h );
m_pWorkCameraParentButton->SetBounds( 1 + m_nStateColumnWidth, y + 1, h - 2, h - 2 );
m_pWorkCameraParentButton->ClearImages();
m_pWorkCameraParentButton->AddImage( m_Images.GetImage( m_StateIconIndices[ STATE_ICON_WORK_CAMERA_PARENT_ACTIVE ] ), 0 );
}
}
void CAnimGroupTree::RemoveItem( int itemIndex, bool bPromoteChildren, bool bRecursivelyRemove )
{
// The tree view implementation uses negative item indices to indicate
// recursion, here we don't care, we just want the current item being removed.
int nActualIndex = ( itemIndex < 0 ) ? -itemIndex : itemIndex;
CAnimGroupStateIconSet *pIconSet = GetTreeItemStateIconSet( nActualIndex );
if ( pIconSet )
{
delete pIconSet;
}
BaseClass::RemoveItem( itemIndex, bPromoteChildren, bRecursivelyRemove );
}
void CAnimGroupTree::RemoveAll()
{
for ( int itemID = FirstItem(); itemID != InvalidItemID(); itemID = NextItem( itemID ) )
{
CAnimGroupStateIconSet *pIconSet = GetTreeItemStateIconSet( itemID );
if ( pIconSet )
{
delete pIconSet;
}
}
BaseClass::RemoveAll();
}
bool CAnimGroupTree::VisibleControlsBelow_R( CDmeControlGroup *pGroup )
{
// If the group is not visible itself then it
// cannot have any visible controls below it.
if ( !m_pGroupPanel->m_pController->IsControlGroupVisible( pGroup ) )
return false;
// The group is visible and has controls itself then it has
// visible controls below it, no need to go farther.
if ( pGroup->Controls().Count() > 0 )
return true;
// If the group did not have any controls itself, check all of its children recursively,
// if any are visible and have controls then there are visible controls below this group.
const CDmaElementArray< CDmeControlGroup > &children = pGroup->Children();
int nNumChildren = children.Count();
for ( int iChild = 0; iChild < nNumChildren; ++iChild )
{
CDmeControlGroup *pChild = children[ iChild ];
if ( pChild == NULL )
continue;
if ( VisibleControlsBelow_R( pChild ) )
return true;
}
return false;
}
void CAnimGroupTree::AddDmeControlGroup( int nParentItemIndex, CDmeAnimationSet *pAnimationSet, CDmeControlGroup *pGroup )
{
Assert( pGroup );
Assert( pAnimationSet );
// Add the sub-groups
const CDmaElementArray< CDmeControlGroup > &subGroups = pGroup->Children();
int nGroups = subGroups.Count();
for ( int iGroup = 0; iGroup < nGroups; ++iGroup )
{
CDmeControlGroup *pSubGroup = subGroups[ iGroup ];
if ( pSubGroup == NULL )
continue;
if ( !VisibleControlsBelow_R( pSubGroup ) )
continue;
AddControlGroupToTree( nParentItemIndex, pSubGroup, pGroup, pAnimationSet );
}
// Add the controls
const CDmaElementArray< CDmElement > &controls = pGroup->Controls();
int nControls = controls.Count();
for ( int iControl = 0; iControl < nControls; ++iControl )
{
CDmElement *pControl = controls[ iControl ];
if ( pControl )
{
AddControlToTree( nParentItemIndex, pControl, pGroup, pAnimationSet );
}
}
}
void CAnimGroupTree::GenerateChildrenOfNode(int itemIndex)
{
BaseClass::GenerateChildrenOfNode( itemIndex );
// Make sure the children are only generated once.
if ( GetNumChildren( itemIndex ) > 0 )
return;
if ( itemIndex == GetRootItemIndex() )
{
CDmeFilmClip *pFilmClip = m_pGroupPanel->m_pController->GetAnimationSetClip();
CAnimSetGroupAnimSetTraversal traversal( pFilmClip );
while ( CDmeAnimationSet *pAnimSet = traversal.Next() )
{
AddAnimationSetToTree( pAnimSet );
}
return;
}
// Get type for item
KeyValues *kv = GetItemData( itemIndex );
if ( kv )
{
AnimTreeItemType_t itemType = static_cast< AnimTreeItemType_t >( kv->GetInt( "itemType" ) );
CDmElement *pElement = GetElementKeyValue< CDmElement >( kv, "handle" );
CDmeAnimationSet *pAnimationSet = CastElement< CDmeAnimationSet >( GetElementKeyValue< CDmElement >( kv, "animset" ) );
CDmeControlGroup *pControlGroup = CastElement< CDmeControlGroup >( GetElementKeyValue< CDmElement >( kv, "controlgroup" ) );
TransformComponent_t nComponentFlags = static_cast< TransformComponent_t >( kv->GetInt( "componentFlags" ) );
switch ( itemType )
{
default:
break;
case ANIMTREE_ITEM_ANIMSET:
// Under anim set we get 1-N groups, expandable if they have children or controls
{
Assert( pAnimationSet );
CDmeControlGroup *pRootGroup = pAnimationSet->GetRootControlGroup();
AddDmeControlGroup( itemIndex, pAnimationSet, pRootGroup );
}
break;
case ANIMTREE_ITEM_GROUP:
// Under group we have subgroups and controls
{
Assert( pAnimationSet );
Assert( pElement );
CDmeControlGroup *pGroup = CastElement< CDmeControlGroup >( pElement );
Assert( pGroup );
AddDmeControlGroup( itemIndex, pAnimationSet, pGroup );
}
break;
case ANIMTREE_ITEM_CONTROL:
case ANIMTREE_ITEM_COMPONENT:
// Under transform controls are the position and rotation components
{
Assert( pControlGroup );
Assert( pAnimationSet );
Assert( pElement );
CDmeTransformControl *pTranformControl = CastElement< CDmeTransformControl >( pElement );
if ( pTranformControl )
{
AddTransformComponentsToTree( itemIndex, pTranformControl, pControlGroup, pAnimationSet, nComponentFlags );
}
}
break;
}
}
}
//-----------------------------------------------------------------------------
// Find the child of the specified item which has the specified element as its
// "handle" value
//-----------------------------------------------------------------------------
int CAnimGroupTree::FindChildItemForElement( int nParentIndex, const CDmElement *pElement, TransformComponent_t nComponentFlags )
{
int nChildren = GetNumChildren( nParentIndex );
for ( int iChild = 0; iChild < nChildren; ++iChild )
{
int nChildIndex = GetChild( nParentIndex, iChild );
KeyValues *pItemData = GetItemData( nChildIndex );
if ( !pItemData )
continue;
CDmElement *pItemElement = GetElementKeyValue< CDmElement >( pItemData, "handle" );
if ( pItemElement == pElement )
{
if ( nComponentFlags == 0 )
return nChildIndex;
TransformComponent_t nItemComponentFlags = static_cast< TransformComponent_t >( pItemData->GetInt( "componentFlags" ) );
if ( nItemComponentFlags == nComponentFlags )
return nChildIndex;
if ( ( nItemComponentFlags & nComponentFlags ) == nComponentFlags )
{
GenerateChildrenOfNode( nChildIndex );
int nSubChildIndex = FindChildItemForElement( nChildIndex, pElement, nComponentFlags );
if ( nSubChildIndex >= 0 )
return nChildIndex;
}
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Find the index of the the tree view item that has the specified element as
// its "handle" value
//-----------------------------------------------------------------------------
int CAnimGroupTree::FindItemForElement( const CDmElement *pElement, TransformComponent_t nComponentFlags )
{
int highest = GetHighestItemID();
for ( int i = 0; i < highest; ++i )
{
if ( !IsItemIDValid( i ) )
continue;
KeyValues *pItemData = GetItemData( i );
if ( !pItemData )
continue;
CDmElement *pItemElement = GetElementKeyValue< CDmElement >( pItemData, "handle" );
if ( pItemElement == pElement )
{
TransformComponent_t nItemComponentFlags = static_cast< TransformComponent_t >( pItemData->GetInt( "componentFlags" ) );
if ( ( nItemComponentFlags == nComponentFlags ) || ( nComponentFlags == 0 ) )
return i;
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Build the tree view items from the root down to the specified animation set
//-----------------------------------------------------------------------------
int CAnimGroupTree::BuildTreeToAnimationSet( CDmeAnimationSet *pAnimationSet )
{
if ( pAnimationSet == NULL )
return -1;
// Check to see if the animation set is already has an item, if so just return that.
int nItemIndex = FindItemForElement( pAnimationSet );
// If the item was not found find or create the item for the animation set group containing
// the animation set and then create all its children, including the animation set being looked for.
if ( nItemIndex < 0 )
{
int nParentItemIndex = GetRootItemIndex();
GenerateChildrenOfNode( nParentItemIndex );
nItemIndex = FindChildItemForElement( nParentItemIndex, pAnimationSet );
}
return nItemIndex;
}
//-----------------------------------------------------------------------------
// Build the tree view items from the root down to the specified control group
//-----------------------------------------------------------------------------
int CAnimGroupTree::BuildTreeToGroup( CDmeControlGroup *pGroup, CDmeAnimationSet *pAnimationSet )
{
if ( ( pGroup == NULL ) || ( pAnimationSet == NULL ) )
return -1;
// Check to see if the group is already has an item, if so just return that.
int nItemIndex = FindItemForElement( pGroup );
if ( nItemIndex < 0 )
{
int nParentItemIndex = -1;
if ( pAnimationSet->GetRootControlGroup() == pGroup )
{
nParentItemIndex = BuildTreeToAnimationSet( pAnimationSet );
return nParentItemIndex;
}
else
{
CDmeControlGroup* pParentGroup = pGroup->FindParent();
nParentItemIndex = BuildTreeToGroup( pParentGroup, pAnimationSet );
}
if ( nParentItemIndex >= 0 )
{
GenerateChildrenOfNode( nParentItemIndex );
nItemIndex = FindChildItemForElement( nParentItemIndex, pGroup );
}
}
return nItemIndex;
}
//-----------------------------------------------------------------------------
// Build the tree view items from the root down to the specified control
//-----------------------------------------------------------------------------
void CAnimGroupTree::BuildTreeToControl( const CDmElement *pControl, TransformComponent_t nComponentFlags )
{
if ( pControl == NULL )
return;
// Find the animation set to which the control belongs
CDmeAnimationSet *pAnimationSet = FindAncestorReferencingElement< CDmeAnimationSet >( pControl );
if ( pAnimationSet == NULL )
return;
// Check to see if the control is already has an item, if so just return that.
int nItemIndex = FindItemForElement( pControl, nComponentFlags );
// If the item for the control was not found, find the group containing the control
// and make sure it has an item in the tree and the generate the children for that
// item which will include the item for the control being looked for.
if ( nItemIndex < 0 )
{
int nParentItemIndex = -1;
CDmeControlGroup *pGroup = CDmeControlGroup::FindGroupContainingControl( pControl );
nParentItemIndex = BuildTreeToGroup( pGroup, pAnimationSet );
if ( nParentItemIndex >= 0 )
{
GenerateChildrenOfNode( nParentItemIndex );
nItemIndex = FindChildItemForElement( nParentItemIndex, pControl, nComponentFlags );
}
}
if ( nItemIndex >= 0 )
{
MakeItemVisible( nItemIndex );
}
}
//-----------------------------------------------------------------------------
// Set the selection state on the specified item
//-----------------------------------------------------------------------------
void CAnimGroupTree::SetItemSelectionState( int nItemIndex, SelectionState_t selectionState )
{
KeyValues *pItemData = GetItemData( nItemIndex );
if ( pItemData )
{
pItemData->SetInt( "selection", ( int )selectionState );
}
}
//-----------------------------------------------------------------------------
// Get the selection state of the specified item
//-----------------------------------------------------------------------------
SelectionState_t CAnimGroupTree::GetItemSelectionState( int nItemIndex ) const
{
KeyValues *pItemData = GetItemData( nItemIndex );
if ( pItemData == NULL)
return SEL_EMPTY;
return ( SelectionState_t )( pItemData->GetInt( "selection" ) );
}
//-----------------------------------------------------------------------------
// Get a list of the controls which are fully selected and whose parents are
// not fully selected
//-----------------------------------------------------------------------------
void CAnimGroupTree::GetSelectionRootItems( CUtlVector< int > &rootSelectedItems ) const
{
CUtlVector< int > selectedItems;
GetSelectedItems( selectedItems );
int nNumSelectedItems = selectedItems.Count();
rootSelectedItems.EnsureCapacity( nNumSelectedItems );
for ( int iItem = 0; iItem < nNumSelectedItems; ++iItem )
{
int nItemIndex = selectedItems[ iItem ];
// Skip any items which are not fully selected
if ( GetItemSelectionState( nItemIndex ) != SEL_ALL )
continue;
// Skip any items whose parent is fully selected
int nParentIndex = GetItemParent( nItemIndex );
if ( GetItemSelectionState( nParentIndex ) == SEL_ALL )
continue;
rootSelectedItems.AddToTail( nItemIndex );
}
}
CBaseAnimSetControlGroupPanel::CBaseAnimSetControlGroupPanel( vgui::Panel *parent, const char *className, CBaseAnimationSetEditor *editor, bool bControlStateInterface ) :
BaseClass( parent, className ),
m_pController( NULL )
{
m_hEditor = editor;
m_pController = editor->GetController();
m_pController->AddControlSelectionChangedListener( this );
m_hGroups = SETUP_PANEL( new CAnimGroupTree( this, "AnimSetGroups", this, bControlStateInterface ) );
m_hGroups->SetAutoResize( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 );
m_hGroups->SetAllowMultipleSelections( true );
m_hGroups->AddItem( new KeyValues( "root" ), -1 ); // add (invisible) root
}
CBaseAnimSetControlGroupPanel::~CBaseAnimSetControlGroupPanel()
{
}
void CBaseAnimSetControlGroupPanel::ApplySchemeSettings( IScheme *pScheme )
{
BaseClass::ApplySchemeSettings( pScheme );
m_FullSelectionColor = pScheme->GetColor( "AnimSet.FullSelectionColor" , Color( 128, 128, 128, 128 ) );
m_PartialSelectionColor = pScheme->GetColor( "AnimSet.PartialSelectionColor" , Color( 128, 128, 64, 64 ) );
m_ContextMenuHighlightColor = pScheme->GetColor( "AnimSet.ContextMenuSelectionColor", Color( 255, 153, 0, 255 ) );
// Normally we shouldn't have to call this but ApplySchemeSettings is called directly
// by the animation set editor, so normal solve traverse isn't performed in that case.
m_hGroups->ApplySchemeSettings( pScheme );
}
void CBaseAnimSetControlGroupPanel::SelectAnimTreeItem( int itemIndex, ESelectionMode selectionMode )
{
AnimTreeItemType_t itemType;
CDmElement *pElement = m_hGroups->GetTreeItemData( itemIndex, &itemType );
if ( !pElement )
return;
switch ( itemType )
{
case ANIMTREE_ITEM_ANIMSET:
if ( CDmeAnimationSet *pAnimSet = CastElement< CDmeAnimationSet >( pElement ) )
{
m_pController->SelectAnimationSet( pAnimSet, selectionMode );
}
break;
case ANIMTREE_ITEM_GROUP:
if ( CDmeControlGroup *pGroup = CastElement< CDmeControlGroup >( pElement ) )
{
m_pController->SelectControlGroup( pGroup, selectionMode );
}
break;
case ANIMTREE_ITEM_CONTROL:
m_pController->SelectControl( pElement, selectionMode );
break;
case ANIMTREE_ITEM_COMPONENT:
TransformComponent_t nComponentFlags = m_hGroups->GetItemComponentFlags( itemIndex );
m_pController->SelectControl( pElement, selectionMode, nComponentFlags );
break;
}
}
void CBaseAnimSetControlGroupPanel::OnControlSelectionChanged()
{
UpdateSelection();
}
void CBaseAnimSetControlGroupPanel::ExpandTreeToControl( const CDmElement *pSelection, TransformComponent_t nComponentFlags )
{
m_hGroups->BuildTreeToControl( pSelection, nComponentFlags );
}
void CBaseAnimSetControlGroupPanel::OnTreeViewStartRangeSelection()
{
m_pController->SetRangeSelectionState( true );
}
void CBaseAnimSetControlGroupPanel::OnTreeViewFinishRangeSelection()
{
m_pController->SetRangeSelectionState( false );
}
void CBaseAnimSetControlGroupPanel::OnTreeViewItemSelected( int itemIndex, int replaceSelection )
{
SelectAnimTreeItem( itemIndex, replaceSelection ? SELECTION_SET : SELECTION_ADD );
}
void CBaseAnimSetControlGroupPanel::OnTreeViewItemDeselected( int itemIndex )
{
SelectAnimTreeItem( itemIndex, SELECTION_REMOVE );
}
void CBaseAnimSetControlGroupPanel::OnTreeViewItemSelectionCleared()
{
m_pController->ClearSelection();
}
void CBaseAnimSetControlGroupPanel::ChangeAnimationSetClip( CDmeFilmClip *pFilmClip )
{
RebuildTree( false );
}
void CBaseAnimSetControlGroupPanel::OnControlsAddedOrRemoved()
{
RebuildTree( true );
}
//-----------------------------------------------------------------------------
// Purpose: Rebuild the tree view from the current control selection hierarchy
//-----------------------------------------------------------------------------
void CBaseAnimSetControlGroupPanel::RebuildTree( bool bRestoreExpansion )
{
if ( bRestoreExpansion )
{
CUtlVector< ElementExpansion_t > expandedNodes( 0, m_hGroups->GetItemCount() );
CollectExpandedItems( expandedNodes, m_hGroups->GetRootItemIndex() );
m_hGroups->SetSilentMode( true );
m_hGroups->RemoveAll();
m_hGroups->SetSilentMode( false );
m_hGroups->AddItem( new KeyValues( "root" ), -1 ); // add (invisible) root
m_hGroups->ExpandItem( m_hGroups->GetRootItemIndex(), true );
ExpandItems( expandedNodes );
UpdateSelection();
}
else
{
m_hGroups->SetSilentMode( true );
m_hGroups->RemoveAll();
m_hGroups->SetSilentMode( false );
m_hGroups->AddItem( new KeyValues( "root" ), -1 ); // add (invisible) root
if ( CDmeFilmClip *pFilmClip = m_pController->GetAnimationSetClip() )
{
CAnimSetGroupAnimSetTraversal traversal( pFilmClip );
while ( CDmeAnimationSet *pAnimSet = traversal.Next() )
{
m_hGroups->AddAnimationSetToTree( pAnimSet );
}
m_hGroups->ExpandItem( m_hGroups->GetRootItemIndex(), true );
}
}
}
// pre-order traversal so that we can ExpandItems linearly
void CBaseAnimSetControlGroupPanel::CollectExpandedItems( CUtlVector< ElementExpansion_t > &expandedNodes, int nItemIndex )
{
if ( nItemIndex == m_hGroups->InvalidItemID() )
return;
AnimTreeItemType_t itemType;
CDmElement *pElement = m_hGroups->GetTreeItemData( nItemIndex, &itemType );
if ( !m_hGroups->IsItemExpanded( nItemIndex ) )
return;
ElementExpansion_t *pExpansionInfo = NULL;
if ( pElement )
{
int nIndex = expandedNodes.AddToTail();
pExpansionInfo = &expandedNodes[ nIndex ];
pExpansionInfo->m_pElement = pElement;
pExpansionInfo->m_ComponentFlags = TRANSFORM_COMPONENT_NONE;
}
int nChildren = m_hGroups->GetNumChildren( nItemIndex );
for ( int i = 0; i < nChildren; ++i )
{
int nChildIndex = m_hGroups->GetChild( nItemIndex, i );
AnimTreeItemType_t childItemType;
m_hGroups->GetTreeItemData( nChildIndex, &childItemType );
if ( childItemType == ANIMTREE_ITEM_COMPONENT )
{
if ( m_hGroups->IsItemExpanded( nChildIndex ) && pExpansionInfo )
{
pExpansionInfo->m_ComponentFlags |= m_hGroups->GetItemComponentFlags( nChildIndex );
}
}
else
{
CollectExpandedItems( expandedNodes, nChildIndex );
}
}
}
// assumes expandedNodes have parents before children (ie expandedNodes is a pre-order traversal)
void CBaseAnimSetControlGroupPanel::ExpandItems( const CUtlVector< ElementExpansion_t > &expandedNodes )
{
int nExpandedNodes = expandedNodes.Count();
for ( int i = 0; i < nExpandedNodes; ++i )
{
CDmElement *pElement = expandedNodes[ i ].m_pElement;
int nItemIndex = m_hGroups->FindItemForElement( pElement );
if ( nItemIndex != m_hGroups->InvalidItemID() )
{
m_hGroups->ExpandItem( nItemIndex, true );
TransformComponent_t expandedComponents = expandedNodes[ i ].m_ComponentFlags;
if ( expandedComponents == TRANSFORM_COMPONENT_NONE )
continue;
int nChildren = m_hGroups->GetNumChildren( nItemIndex );
for ( int i = 0; i < nChildren; ++i )
{
int nChildIndex = m_hGroups->GetChild( nItemIndex, i );
AnimTreeItemType_t childItemType;
m_hGroups->GetTreeItemData( nChildIndex, &childItemType );
TransformComponent_t childComponents = m_hGroups->GetItemComponentFlags( nChildIndex );
if ( ( childItemType == ANIMTREE_ITEM_COMPONENT ) && ( childComponents & expandedComponents ) )
{
m_hGroups->ExpandItem( nChildIndex, true );
}
}
}
}
}
void CBaseAnimSetControlGroupPanel::UpdateSelection()
{
if ( !m_hGroups )
return;
m_hGroups->SetSilentMode( true );
m_hGroups->ClearSelection();
m_hGroups->SetSilentMode( false );
UpdateSelection_R( m_hGroups->GetRootItemIndex() );
}
SelectionState_t CBaseAnimSetControlGroupPanel::UpdateSelection_R( int nParentIndex )
{
if ( nParentIndex == m_hGroups->InvalidItemID() )
return SEL_EMPTY;
SelectionState_t selection = SEL_EMPTY;
int nChildren = m_hGroups->GetNumChildren( nParentIndex );
if ( nChildren > 0 )
{
for ( int i = 0; i < nChildren; ++i )
{
int nChildIndex = m_hGroups->GetChild( nParentIndex, i );
selection += UpdateSelection_R( nChildIndex );
}
}
else
{
// check actual controls
AnimTreeItemType_t itemType;
CDmElement *pElement = m_hGroups->GetTreeItemData( nParentIndex, &itemType );
if ( !pElement )
return SEL_EMPTY;
switch ( itemType )
{
case ANIMTREE_ITEM_ANIMSET:
if ( CDmeAnimationSet *pAnimSet = CastElement< CDmeAnimationSet >( pElement ) )
{
selection = m_pController->GetSelectionState( pAnimSet );
}
break;
case ANIMTREE_ITEM_GROUP:
if ( CDmeControlGroup *pGroup = CastElement< CDmeControlGroup >( pElement ) )
{
selection = m_pController->GetSelectionState( pGroup );
}
break;
case ANIMTREE_ITEM_CONTROL:
selection = m_pController->GetSelectionState( pElement );
break;
case ANIMTREE_ITEM_COMPONENT:
TransformComponent_t nComponentFlags = m_hGroups->GetItemComponentFlags( nParentIndex );
selection = m_pController->GetSelectionState( pElement, nComponentFlags );
break;
}
}
m_hGroups->SetItemSelectionState( nParentIndex, selection );
if ( selection == SEL_SOME || selection == SEL_ALL )
{
Color color = ( selection == SEL_ALL ) ? m_FullSelectionColor : m_PartialSelectionColor;
m_hGroups->SetItemSelectionBgColor( nParentIndex, color );
m_hGroups->SetItemSelectionUnfocusedBgColor( nParentIndex, color );
m_hGroups->SetSilentMode( true );
m_hGroups->AddSelectedItem( nParentIndex, false, false, false );
m_hGroups->SetSilentMode( false );
}
return selection;
}
CDmeDag *CBaseAnimSetControlGroupPanel::GetWorkCameraParent()
{
return m_pController->GetWorkCameraParent();
}
//-----------------------------------------------------------------------------
// Purpose: Handle the request of the tree view to create a context menu
//-----------------------------------------------------------------------------
void CBaseAnimSetControlGroupPanel::OnTreeViewOpenContextMenu( int itemID )
{
if ( itemID >= 0 )
{
m_hGroups->SetItemSelectionUnfocusedBgColor( itemID, m_ContextMenuHighlightColor );
}
KeyValues *pItemData = m_hGroups->GetItemData( itemID );
m_hEditor->OpenTreeViewContextMenu( pItemData );
}
//-----------------------------------------------------------------------------
// Create a new control group containing the selected controls
//-----------------------------------------------------------------------------
void CBaseAnimSetControlGroupPanel::CreateGroupFromSelectedControls()
{
CUtlVector< int > selectedItems;
m_hGroups->GetSelectionRootItems( selectedItems );
int nNumSelectedItems = selectedItems.Count();
if ( nNumSelectedItems < 0 )
return;
CDmeControlGroup *pCommonAncestor = NULL;
for ( int iItem = 0; iItem < nNumSelectedItems; ++iItem )
{
int nItemIndex = selectedItems[ iItem ];
AnimTreeItemType_t itemType;
CDmeControlGroup *pParentControlGroup = NULL;
m_hGroups->GetTreeItemData( nItemIndex, &itemType, NULL, &pParentControlGroup );
// Currently not allowed to group animation sets
if ( itemType == ANIMTREE_ITEM_ANIMSET )
return;
if ( pCommonAncestor )
{
pCommonAncestor = pParentControlGroup->FindCommonAncestor( pCommonAncestor );
}
else
{
pCommonAncestor = pParentControlGroup;
}
// If any of the selected items do not have a common
// ancestor, do not allow the group to be created.
if ( pCommonAncestor == NULL )
return;
}
// If the selected items did not share a common ancestor a new group cannot be created
if ( pCommonAncestor == NULL )
return;
// Generate a name for the new group which is unique among the children of the group it will belong to.
const CDmeControlGroup *pRootGroup = pCommonAncestor->FindRootControlGroup();
if ( pRootGroup == NULL)
return;
CUtlVector< DmElementHandle_t > childList;
pRootGroup->GetAllChildren( childList );
int nIndex = GenerateUniqueNameIndex( "group", childList, 1 );
CFmtStr groupName( "group%d", nIndex );
// Create the new group and make it a child of the common ancestor
CDmeControlGroup *pNewGroup = pCommonAncestor->CreateControlGroup( groupName );
// Add the selected items to the new group
if ( pNewGroup )
{
for ( int iItem = 0; iItem < nNumSelectedItems; ++iItem )
{
int nItemIndex = selectedItems[ iItem ];
AnimTreeItemType_t itemType;
CDmElement *pElement = m_hGroups->GetTreeItemData( nItemIndex, &itemType );
if ( itemType == ANIMTREE_ITEM_GROUP )
{
pNewGroup->AddChild( CastElement< CDmeControlGroup >( pElement ) );
}
else if ( itemType == ANIMTREE_ITEM_CONTROL )
{
pNewGroup->AddControl( pElement );
}
}
// Clean up any empty children which may have been left in the tree.
pCommonAncestor->DestroyEmptyChildren();
// Rebuild the tree so that it includes the new group
RebuildTree( true );
// Find the item corresponding to the new group and set it label editing mode.
int nNewItemIndex = m_hGroups->FindItemForElement( pNewGroup );
m_hGroups->MakeItemVisible( nNewItemIndex );
m_hGroups->StartEditingLabel( nNewItemIndex );
}
}