//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // //============================================================================= #include "dme_controls/BaseAnimSetControlGroupPanel.h" #include "vgui_controls/TreeView.h" #include "vgui_controls/Menu.h" #include "tier1/KeyValues.h" #include "movieobjects/dmeanimationset.h" #include "dme_controls/BaseAnimSetAttributeSliderPanel.h" #include "dme_controls/BaseAnimationSetEditor.h" #include "dme_controls/dmecontrols_utils.h" // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" using namespace vgui; //----------------------------------------------------------------------------- // 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 ); virtual ~CAnimGroupTree(); virtual bool IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ); virtual void OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ); virtual void GenerateContextMenu( int itemIndex, int x, int y ); private: MESSAGE_FUNC( OnImportAnimation, "ImportAnimation" ); void CleanupContextMenu(); vgui::DHANDLE< vgui::Menu > m_hContextMenu; CBaseAnimSetControlGroupPanel *m_pGroupPanel; }; CAnimGroupTree::CAnimGroupTree( Panel *parent, const char *panelName, CBaseAnimSetControlGroupPanel *groupPanel ) : BaseClass( parent, panelName ), m_pGroupPanel( groupPanel ) { } CAnimGroupTree::~CAnimGroupTree() { CleanupContextMenu(); } void CAnimGroupTree::CleanupContextMenu() { if ( m_hContextMenu.Get() ) { delete m_hContextMenu.Get(); m_hContextMenu = NULL; } } bool CAnimGroupTree::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ) { if ( msglist.Count() != 1 ) return false; KeyValues *data = msglist[ 0 ]; if ( !data ) return false; if ( !data->FindKey( "color" ) ) return false; KeyValues *itemData = GetItemData( itemIndex ); if ( !itemData->FindKey( "handle" ) ) return false; DmElementHandle_t handle = (DmElementHandle_t)itemData->GetInt( "handle" ); if ( handle == DMELEMENT_HANDLE_INVALID ) return false; return true; } void CAnimGroupTree::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ) { if ( !IsItemDroppable( itemIndex, msglist ) ) return; KeyValues *data = msglist[ 0 ]; if ( !data ) return; KeyValues *itemData = GetItemData( itemIndex ); CDmElement *group = GetElementKeyValue< CDmElement >( itemData, "handle" ); Assert( m_pGroupPanel ); Color clr = data->GetColor( "color" ); SetItemFgColor( itemIndex, clr ); SetItemSelectionTextColor( itemIndex, clr ); if ( group ) { CAppUndoScopeGuard guard( NOTIFY_SETDIRTYFLAG, "Change Group Color" ); group->SetValue< Color >( "treeColor", clr ); } } void CAnimGroupTree::OnImportAnimation() { PostMessage( m_pGroupPanel->m_hEditor, new KeyValues( "ImportAnimation", "visibleOnly", "1" ), 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 ) { CleanupContextMenu(); m_hContextMenu = new Menu( this, "ActionMenu" ); m_hContextMenu->AddMenuItem( "#ImportAnimation", new KeyValues( "ImportAnimation" ), this ); Menu::PlaceContextMenu( this, m_hContextMenu.Get() ); } CBaseAnimSetControlGroupPanel::CBaseAnimSetControlGroupPanel( vgui::Panel *parent, const char *className, CBaseAnimationSetEditor *editor ) : BaseClass( parent, className ), m_bStartItemWasSelected( false ), m_SliderNames( 0, 0, true ) { m_hEditor = editor; m_hGroups = new CAnimGroupTree( this, "AnimSetGroups", this ); m_hGroups->SetMultipleItemDragEnabled( true ); m_hGroups->SetAutoResize ( Panel::PIN_TOPLEFT, Panel::AUTORESIZE_DOWNANDRIGHT, 0, 0, 0, 0 ); m_hGroups->SetAllowMultipleSelections( true ); } CBaseAnimSetControlGroupPanel::~CBaseAnimSetControlGroupPanel() { } static int AddItemToTree( TreeView *tv, const char *label, int parentIndex, const Color& fg, int groupNumber, int handle ) { Color bgColor( 128, 128, 128, 128 ); KeyValues *kv = new KeyValues( "item", "text", label ); kv->SetInt( "groupNumber", groupNumber ); kv->SetInt( "droppable", 1 ); kv->SetInt( "handle", handle ); int idx = tv->AddItem( kv, parentIndex ); tv->SetItemFgColor( idx, fg ); tv->SetItemSelectionTextColor( idx, fg ); tv->SetItemSelectionBgColor( idx, bgColor ); tv->SetItemSelectionUnfocusedBgColor( idx, bgColor ); tv->RemoveSelectedItem( idx ); tv->ExpandItem( idx, false ); kv->deleteThis(); return idx; } void CBaseAnimSetControlGroupPanel::ApplySchemeSettings( IScheme *pScheme ) { BaseClass::ApplySchemeSettings( pScheme ); m_hGroups->SetFont( pScheme->GetFont( "DefaultBold", IsProportional() ) ); } void CBaseAnimSetControlGroupPanel::OnTreeViewItemSelectionCleared() { // We check the entire group manually OnTreeViewItemSelected( -1 ); } void CBaseAnimSetControlGroupPanel::OnTreeViewItemDeselected( int itemIndex ) { OnTreeViewItemSelected( -1 ); } void CBaseAnimSetControlGroupPanel::OnTreeViewItemSelected( int itemIndex ) { if ( !m_AnimSet.Get() ) return; // Build the list of selected groups, and notify the attribute slider panel CUtlVector< int > selection; m_hGroups->GetSelectedItems( selection ); const CDmaElementArray<> &groups = m_AnimSet->GetSelectionGroups(); int groupCount = groups.Count(); int i; int rootIndex = m_hGroups->GetRootItemIndex(); bool selectionHasRoot = false; for ( i = 0 ; i < selection.Count(); ++i ) { if ( selection[ i ] == rootIndex ) { selectionHasRoot = true; break; } } m_SliderNames.RemoveAll(); if ( selectionHasRoot ) { for ( i = 0; i < groups.Count(); ++i ) { CDmElement *element = groups[ i ]; if ( !element ) continue; const CDmrStringArray array( element, "selectedControls" ); if ( array.IsValid() ) { for ( int j = 0 ; j < array.Count(); ++j ) { const char *sliderName = array[ j ]; if ( sliderName && *sliderName ) { m_SliderNames.AddString( sliderName ); } } } } } else { for ( i = 0 ; i < selection.Count(); ++i ) { if ( selection[ i ] == rootIndex ) continue; KeyValues *kv = m_hGroups->GetItemData( selection[ i ] ); if ( !kv ) continue; int groupNumber = kv->GetInt( "groupNumber" ); if ( groupNumber < 0 || groupNumber >= groupCount ) { const char *sliderName = kv->GetString( "text" ); if ( sliderName && *sliderName ) { m_SliderNames.AddString( sliderName ); } continue; } CDmElement *element = groups[ groupNumber ]; if ( !element ) continue; const CDmrStringArray array( element, "selectedControls" ); if ( array.IsValid() ) { for ( int j = 0 ; j < array.Count(); ++j ) { const char *sliderName = array[ j ]; if ( sliderName && *sliderName ) { m_SliderNames.AddString( sliderName ); } } } } } // now notify the attribute slider panel CBaseAnimSetAttributeSliderPanel *attSliders = m_hEditor->GetAttributeSlider(); if ( attSliders ) { attSliders->SetVisibleControlsForSelectionGroup( m_SliderNames ); } } void CBaseAnimSetControlGroupPanel::ChangeAnimationSet( CDmeAnimationSet *newAnimSet ) { bool changed = m_AnimSet.Get() != newAnimSet ? true : false; m_AnimSet = newAnimSet; if ( !m_AnimSet.Get() ) { m_hGroups->RemoveAll(); m_hSelectableIndices.RemoveAll(); m_GroupList.RemoveAll(); return; } // Compare groups bool bRebuildGroups = false; const CDmaElementArray< CDmElement > &groups = m_AnimSet->GetSelectionGroups(); int c = groups.Count(); if ( c != m_GroupList.Count() ) { bRebuildGroups = true; } else { for ( int i = 0; i < c; ++i ) { CDmElement *group = groups[ i ]; if ( group == m_GroupList[ i ].Get() ) { continue; } bRebuildGroups = true; break; } } if ( bRebuildGroups ) { m_hGroups->SetFont( scheme()->GetIScheme( GetScheme() )->GetFont( "DefaultBold", IsProportional() ) ); // Build a tree of every open item in the tree view OpenItemTree_t openItems; int nRootIndex = m_hGroups->GetRootItemIndex(); if ( nRootIndex != -1 ) { BuildOpenItemList( openItems, openItems.InvalidIndex(), nRootIndex ); } m_hGroups->RemoveAll(); m_hSelectableIndices.RemoveAll(); m_GroupList.RemoveAll(); // Create root int rootIndex = AddItemToTree( m_hGroups, "root", -1, Color( 128, 128, 128, 255 ), -1, (int)DMELEMENT_HANDLE_INVALID ); Color defaultColor( 0, 128, 255, 255 ); CAppUndoScopeGuard *guard = NULL; for ( int i = 0; i < c; ++i ) { CDmElement *group = groups[ i ]; if ( !group->HasAttribute( "treeColor" ) ) { if ( !guard ) { guard = new CAppUndoScopeGuard( NOTIFY_SETDIRTYFLAG, "Set Default Colors" ); } group->SetValue< Color >( "treeColor", defaultColor ); } int groupIndex = AddItemToTree( m_hGroups, group->GetName(), rootIndex, group->GetValue< Color >( "treeColor" ), i, (int)group->GetHandle() ); const CDmrStringArray array( group, "selectedControls" ); if ( array.IsValid() ) { for ( int j = 0 ; j < array.Count(); ++j ) { AddItemToTree( m_hGroups, array[ j ], groupIndex, Color( 200, 200, 200, 255 ), -1, (int)DMELEMENT_HANDLE_INVALID ); } } m_hSelectableIndices.AddToTail( groupIndex ); m_GroupList.AddToTail( group->GetHandle() ); } if ( ( nRootIndex >= 0 ) && ( rootIndex >= 0 ) && !changed ) { // Iterate through all previously open items and expand them if they exist if ( openItems.Root() != openItems.InvalidIndex() ) { ExpandOpenItems( openItems, openItems.Root(), rootIndex, true ); } } else { m_hGroups->ExpandItem( rootIndex, true ); } if ( guard ) { delete guard; } } if ( changed ) { for ( int i = 0; i < m_hSelectableIndices.Count(); ++i ) { m_hGroups->AddSelectedItem( m_hSelectableIndices[ i ], false, // don't clear selection true, // put focus on tree false ); // don't expand tree to make all of these visible... } } } //----------------------------------------------------------------------------- // Expands all items in the open item tree if they exist //----------------------------------------------------------------------------- void CBaseAnimSetControlGroupPanel::ExpandOpenItems( OpenItemTree_t &tree, int nOpenTreeIndex, int nItemIndex, bool makeVisible ) { int i = tree.FirstChild( nOpenTreeIndex ); if ( nOpenTreeIndex != tree.InvalidIndex() ) { TreeInfo_t& info = tree[ nOpenTreeIndex ]; if ( info.m_nFlags & EP_EXPANDED ) { // Expand the item m_hGroups->ExpandItem( nItemIndex , true ); } if ( info.m_nFlags & EP_SELECTED ) { m_hGroups->AddSelectedItem( nItemIndex, false, false ); if ( makeVisible ) { m_hGroups->MakeItemVisible( nItemIndex ); } } } while ( i != tree.InvalidIndex() ) { TreeInfo_t& info = tree[ i ]; // Look for a match int nChildIndex = FindTreeItem( nItemIndex, info.m_Item ); if ( nChildIndex != -1 ) { ExpandOpenItems( tree, i, nChildIndex, makeVisible ); } else { if ( info.m_nFlags & EP_SELECTED ) { // Look for preserved item nChildIndex = FindTreeItem( nItemIndex, info.m_Item ); if ( nChildIndex != -1 ) { m_hGroups->AddSelectedItem( nChildIndex, false, false ); if ( makeVisible ) { m_hGroups->MakeItemVisible( nChildIndex ); } } } } i = tree.NextSibling( i ); } } void CBaseAnimSetControlGroupPanel::FillInDataForItem( TreeItem_t &item, int nItemIndex ) { KeyValues *data = m_hGroups->GetItemData( nItemIndex ); if ( !data ) return; item.m_pAttributeName = data->GetString( "text" ); } //----------------------------------------------------------------------------- // Builds a list of open items //----------------------------------------------------------------------------- void CBaseAnimSetControlGroupPanel::BuildOpenItemList( OpenItemTree_t &tree, int nParent, int nItemIndex ) { KeyValues *data = m_hGroups->GetItemData( nItemIndex ); if ( !data ) return; bool expanded = m_hGroups->IsItemExpanded( nItemIndex ); bool selected = m_hGroups->IsItemSelected( nItemIndex ); int flags = 0; if ( expanded ) { flags |= EP_EXPANDED; } if ( selected ) { flags |= EP_SELECTED; } int nChild = tree.InsertChildAfter( nParent, tree.InvalidIndex() ); TreeInfo_t &info = tree[nChild]; FillInDataForItem( info.m_Item, nItemIndex ); info.m_nFlags = flags; // Deal with children int nCount = m_hGroups->GetNumChildren( nItemIndex ); for ( int i = 0; i < nCount; ++i ) { int nChildIndex = m_hGroups->GetChild( nItemIndex, i ); BuildOpenItemList( tree, nChild, nChildIndex ); } } //----------------------------------------------------------------------------- // Finds the tree index of a child matching the particular element + attribute //----------------------------------------------------------------------------- int CBaseAnimSetControlGroupPanel::FindTreeItem( int nParentIndex, const TreeItem_t &info ) { // Look for a match int nCount = m_hGroups->GetNumChildren( nParentIndex ); for ( int i = nCount; --i >= 0; ) { int nChildIndex = m_hGroups->GetChild( nParentIndex, i ); KeyValues *data = m_hGroups->GetItemData( nChildIndex ); Assert( data ); const char *pAttributeName = data->GetString( "text" ); if ( !Q_stricmp( pAttributeName, info.m_pAttributeName ) ) { return nChildIndex; } } return -1; }