From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/vgui2/vgui_controls/TreeView.cpp | 5708 +++++++++++++++---------------- 1 file changed, 2854 insertions(+), 2854 deletions(-) (limited to 'mp/src/vgui2/vgui_controls/TreeView.cpp') diff --git a/mp/src/vgui2/vgui_controls/TreeView.cpp b/mp/src/vgui2/vgui_controls/TreeView.cpp index 066ebd50..b7ad4d3b 100644 --- a/mp/src/vgui2/vgui_controls/TreeView.cpp +++ b/mp/src/vgui2/vgui_controls/TreeView.cpp @@ -1,2854 +1,2854 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#define PROTECTED_THINGS_DISABLE - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "tier1/utlstring.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -#ifndef max -#define max(a,b) (((a) > (b)) ? (a) : (b)) -#endif - -using namespace vgui; -enum -{ - WINDOW_BORDER_WIDTH=2 // the width of the window's border -}; - -#define TREE_INDENT_AMOUNT 20 - -namespace vgui -{ - -//----------------------------------------------------------------------------- -// Purpose: Displays an editable text field for the text control -//----------------------------------------------------------------------------- -class TreeNodeText : public TextEntry -{ - DECLARE_CLASS_SIMPLE( TreeNodeText, TextEntry ); - -public: - TreeNodeText(Panel *parent, const char *panelName, TreeView *tree) : BaseClass(parent, panelName), m_pTree( tree ) - { - m_bEditingInPlace = false; - m_bLabelEditingAllowed = false; - SetDragEnabled( false ); - SetDropEnabled( false ); - AddActionSignalTarget( this ); - m_bArmForEditing = false; - m_bWaitingForRelease = false; - m_lArmingTime = 0L; - SetAllowKeyBindingChainToParent( true ); - } - - MESSAGE_FUNC( OnTextChanged, "TextChanged" ) - { - GetParent()->InvalidateLayout(); - } - - bool IsKeyRebound( KeyCode code, int modifiers ) - { - // If in editing mode, don't try and chain keypresses - if ( m_bEditingInPlace ) - { - return false; - } - - return BaseClass::IsKeyRebound( code, modifiers ); - } - - virtual void PaintBackground() - { - BaseClass::PaintBackground(); - - if ( !m_bLabelEditingAllowed ) - return; - - if ( !m_bEditingInPlace ) - return; - - int w, h; - GetSize( w, h ); - surface()->DrawSetColor( GetFgColor() ); - surface()->DrawOutlinedRect( 0, 0, w, h ); - } - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - TextEntry::ApplySchemeSettings(pScheme); - SetBorder(NULL); - SetCursor(dc_arrow); - } - - virtual void OnKeyCodeTyped(KeyCode code) - { - if ( m_bEditingInPlace ) - { - if ( code == KEY_ENTER ) - { - FinishEditingInPlace(); - } - else if ( code == KEY_ESCAPE ) - { - FinishEditingInPlace( true ); - } - else - { - BaseClass::OnKeyCodeTyped( code ); - } - return; - } - else if ( code == KEY_ENTER && IsLabelEditingAllowed() ) - { - EnterEditingInPlace(); - } - else - { - // let parent deal with it (don't chain back to TextEntry) - CallParentFunction(new KeyValues("KeyCodeTyped", "code", code)); - } - } - -#define CLICK_TO_EDIT_DELAY_MSEC 500 - - virtual void OnTick() - { - BaseClass::OnTick(); - if ( m_bArmForEditing ) - { - long msecSinceArming = system()->GetTimeMillis() - m_lArmingTime; - - if ( msecSinceArming > CLICK_TO_EDIT_DELAY_MSEC ) - { - m_bArmForEditing = false; - m_bWaitingForRelease = false; - ivgui()->RemoveTickSignal( GetVPanel() ); - EnterEditingInPlace(); - } - } - } - - virtual void OnMouseReleased( MouseCode code ) - { - if ( m_bEditingInPlace ) - { - BaseClass::OnMouseReleased( code ); - return; - } - else - { - if ( m_bWaitingForRelease && !IsBeingDragged() ) - { - m_bArmForEditing = true; - m_bWaitingForRelease = false; - m_lArmingTime = system()->GetTimeMillis(); - ivgui()->AddTickSignal( GetVPanel() ); - } - else - { - m_bWaitingForRelease = false; - } - } - - // let parent deal with it - CallParentFunction(new KeyValues("MouseReleased", "code", code)); - } - - virtual void OnCursorMoved( int x, int y ) - { - // let parent deal with it - CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); - } - - virtual void OnMousePressed(MouseCode code) - { - if ( m_bEditingInPlace ) - { - BaseClass::OnMousePressed( code ); - return; - } - else - { - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - - // make sure there is only one item selected - // before "WaitingForRelease" which leads to label editing. - CUtlVector< int > list; - m_pTree->GetSelectedItems( list ); - bool bIsOnlyOneItemSelected = ( list.Count() == 1 ); - - if ( !shift && - !ctrl && - !m_bArmForEditing && - IsLabelEditingAllowed() && - bIsOnlyOneItemSelected && - IsTextFullySelected() && - !IsBeingDragged() ) - { - m_bWaitingForRelease = true; - } - } - - // let parent deal with it - CallParentFunction(new KeyValues("MousePressed", "code", code)); - } - - void SetLabelEditingAllowed( bool state ) - { - m_bLabelEditingAllowed = state; - } - - bool IsLabelEditingAllowed() - { - return m_bLabelEditingAllowed; - } - - virtual void OnMouseDoublePressed(MouseCode code) - { - // Once we are editing, double pressing shouldn't chain up - if ( m_bEditingInPlace ) - { - BaseClass::OnMouseDoublePressed( code ); - return; - } - - if ( m_bArmForEditing ) - { - m_bArmForEditing = false; - m_bWaitingForRelease = false; - ivgui()->RemoveTickSignal( GetVPanel() ); - } - - CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); - } - - void EnterEditingInPlace() - { - if ( m_bEditingInPlace ) - return; - - m_bEditingInPlace = true; - char buf[ 1024 ]; - GetText( buf, sizeof( buf ) ); - m_OriginalText = buf; - SetCursor(dc_ibeam); - SetEditable( true ); - SelectNone(); - GotoTextEnd(); - RequestFocus(); - SelectAllText(false); - m_pTree->SetLabelBeingEdited( true ); - } - - void FinishEditingInPlace( bool revert = false ) - { - if ( !m_bEditingInPlace ) - return; - - m_pTree->SetLabelBeingEdited( false ); - SetEditable( false ); - SetCursor(dc_arrow); - m_bEditingInPlace = false; - char buf[ 1024 ]; - GetText( buf, sizeof( buf ) ); - - // Not actually changed... - if ( !Q_strcmp( buf, m_OriginalText.Get() ) ) - return; - - if ( revert ) - { - SetText( m_OriginalText.Get() ); - GetParent()->InvalidateLayout(); - } - else - { - KeyValues *kv = new KeyValues( "LabelChanged", "original", m_OriginalText.Get(), "changed", buf ); - PostActionSignal( kv ); - } - } - - virtual void OnKillFocus() - { - BaseClass::OnKillFocus(); - - FinishEditingInPlace(); - } - - virtual void OnMouseWheeled(int delta) - { - if ( m_bEditingInPlace ) - { - BaseClass::OnMouseWheeled( delta ); - return; - } - - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); - } - // editable - cursor normal, and ability to edit text - - bool IsBeingEdited() const - { - return m_bEditingInPlace; - } - -private: - - bool m_bEditingInPlace; - CUtlString m_OriginalText; - bool m_bLabelEditingAllowed; - - bool m_bArmForEditing; - bool m_bWaitingForRelease; - long m_lArmingTime; - TreeView *m_pTree; -}; - -//----------------------------------------------------------------------------- -// Purpose: icon for the tree node (folder icon, file icon, etc.) -//----------------------------------------------------------------------------- -class TreeNodeImage : public ImagePanel -{ -public: - TreeNodeImage(Panel *parent, const char *name) : ImagePanel(parent, name) - { - SetBlockDragChaining( true ); - } - - //!! this could possibly be changed to just disallow mouse input on the image panel - virtual void OnMousePressed(MouseCode code) - { - // let parent deal with it - CallParentFunction(new KeyValues("MousePressed", "code", code)); - } - - virtual void OnMouseDoublePressed(MouseCode code) - { - // let parent deal with it - CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); - } - - virtual void OnMouseWheeled(int delta) - { - // let parent deal with it - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); - } - - virtual void OnCursorMoved( int x, int y ) - { - // let parent deal with it - CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); - } -}; - -//----------------------------------------------------------------------------- -// Purpose: Scrollable area of the tree control, holds the tree itself only -//----------------------------------------------------------------------------- -class TreeViewSubPanel : public Panel -{ -public: - TreeViewSubPanel(Panel *parent) : Panel(parent) {} - - virtual void ApplySchemeSettings(IScheme *pScheme) - { - Panel::ApplySchemeSettings(pScheme); - - SetBorder(NULL); - } - - virtual void OnMouseWheeled(int delta) - { - // let parent deal with it - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); - } - virtual void OnMousePressed(MouseCode code) - { - // let parent deal with it - CallParentFunction(new KeyValues("MousePressed", "code", code)); - } - virtual void OnMouseDoublePressed(MouseCode code) - { - // let parent deal with it - CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); - } - - virtual void OnCursorMoved( int x, int y ) - { - // let parent deal with it - CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); - } -}; - -//----------------------------------------------------------------------------- -// Purpose: A single entry in the tree -//----------------------------------------------------------------------------- -class TreeNode : public Panel -{ - DECLARE_CLASS_SIMPLE( TreeNode, Panel ); - -public: - TreeNode(Panel *parent, TreeView *pTreeView); - ~TreeNode(); - void SetText(const char *pszText); - void SetFont(HFont font); - void SetKeyValues(KeyValues *data); - bool IsSelected(); - // currently unused, could be re-used if necessary -// bool IsInFocus(); - virtual void PaintBackground(); - virtual void PerformLayout(); - TreeNode *GetParentNode(); - int GetChildrenCount(); - void ClearChildren(); - int ComputeInsertionPosition( TreeNode *pChild ); - int FindChild( TreeNode *pChild ); - void AddChild(TreeNode *pChild); - void SetNodeExpanded(bool bExpanded); - bool IsExpanded(); - int CountVisibleNodes(); - void CalculateVisibleMaxWidth(); - void OnChildWidthChange(); - int GetMaxChildrenWidth(); - int GetVisibleMaxWidth(); - int GetDepth(); - bool HasParent(TreeNode *pTreeNode); - bool IsBeingDisplayed(); - virtual void SetVisible(bool state); - virtual void Paint(); - virtual void ApplySchemeSettings(IScheme *pScheme); - virtual void SetBgColor( Color color ); - virtual void SetFgColor( Color color ); - virtual void OnSetFocus(); - void SelectPrevChild(TreeNode *pCurrentChild); - void SelectNextChild(TreeNode *pCurrentChild); - - int GetPrevChildItemIndex( TreeNode *pCurrentChild ); - int GetNextChildItemIndex( TreeNode *pCurrentChild ); - - virtual void ClosePreviousParents( TreeNode *pPreviousParent ); - virtual void StepInto( bool bClosePrevious=true ); - virtual void StepOut( bool bClosePrevious=true ); - virtual void StepOver( bool bClosePrevious=true ); - virtual void OnKeyCodeTyped(KeyCode code); - virtual void OnMouseWheeled(int delta); - virtual void OnMousePressed( MouseCode code); - virtual void OnMouseReleased( MouseCode code); - virtual void OnCursorMoved( int x, int y ); - virtual bool IsDragEnabled() const; - void PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y); - - // counts items above this item including itself - int CountVisibleIndex(); - - virtual void OnCreateDragData( KeyValues *msg ); - // For handling multiple selections... - virtual void OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ); - virtual void OnMouseDoublePressed( MouseCode code ); - TreeNode *FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my ); - MESSAGE_FUNC_PARAMS( OnLabelChanged, "LabelChanged", data ); - void EditLabel(); - void SetLabelEditingAllowed( bool state ); - bool IsLabelEditingAllowed() const; - - virtual bool IsDroppable( CUtlVector< KeyValues * >& msglist ); - virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist ); - virtual HCursor GetDropCursor( CUtlVector< KeyValues * >& msglist ); - virtual bool GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ); - - void FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex ); - - void RemoveChildren(); - - void SetSelectionTextColor( const Color& clr ); - void SetSelectionBgColor( const Color& clr ); - void SetSelectionUnfocusedBgColor( const Color& clr ); -public: - int m_ItemIndex; - int m_ParentIndex; - KeyValues *m_pData; - CUtlVector m_Children; - bool m_bExpand; - -private: - - void FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex ); - - int m_iNodeWidth; - int m_iMaxVisibleWidth; - - TreeNodeText *m_pText; - TextImage *m_pExpandImage; - TreeNodeImage *m_pImagePanel; - - bool m_bExpandableWithoutChildren; - - TreeView *m_pTreeView; - int m_nClickedItem; - bool m_bClickedSelected; -}; - - -TreeNode::TreeNode(Panel *parent, TreeView *pTreeView) : - BaseClass(parent, "TreeNode" ), - m_nClickedItem( 0 ), - m_bClickedSelected( false ) -{ - m_pData = NULL; - m_pTreeView = pTreeView; - m_ItemIndex = -1; - m_iNodeWidth = 0; - m_iMaxVisibleWidth = 0; - - m_pExpandImage = new TextImage("+"); - m_pExpandImage->SetPos(3, 1); - - m_pImagePanel = new TreeNodeImage(this, "TreeImage"); - m_pImagePanel->SetPos(TREE_INDENT_AMOUNT, 3); - - m_pText = new TreeNodeText(this, "TreeNodeText",pTreeView); - m_pText->SetMultiline(false); - m_pText->SetEditable(false); - m_pText->SetPos(TREE_INDENT_AMOUNT*2, 0); - m_pText->AddActionSignalTarget( this ); - - m_bExpand = false; - m_bExpandableWithoutChildren = false; -} - -TreeNode::~TreeNode() -{ - delete m_pExpandImage; - if ( m_pData ) - { - m_pData->deleteThis(); - } -} - -void TreeNode::SetText(const char *pszText) -{ - m_pText->SetText(pszText); - InvalidateLayout(); -} - -void TreeNode::SetLabelEditingAllowed( bool state ) -{ - Assert( m_pTreeView->IsLabelEditingAllowed() ); - m_pText->SetLabelEditingAllowed( state ); -} - -bool TreeNode::IsLabelEditingAllowed() const -{ - return m_pText->IsLabelEditingAllowed(); -} - -bool TreeNode::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ) -{ - return m_pTreeView->GetItemDropContextMenu( m_ItemIndex, menu, msglist ); -} - -bool TreeNode::IsDroppable( CUtlVector< KeyValues * >& msglist ) -{ - return m_pTreeView->IsItemDroppable( m_ItemIndex, msglist ); -} - -void TreeNode::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) -{ - m_pTreeView->OnItemDropped( m_ItemIndex, msglist ); -} - -HCursor TreeNode::GetDropCursor( CUtlVector< KeyValues * >& msglist ) -{ - return m_pTreeView->GetItemDropCursor( m_ItemIndex, msglist ); -} - - -void TreeNode::OnCreateDragData( KeyValues *msg ) -{ - // make sure the dragged item appears selected, - // on the off chance it appears deselected by a cntl mousedown - m_pTreeView->AddSelectedItem( m_ItemIndex, false ); - - m_pTreeView->GenerateDragDataForItem( m_ItemIndex, msg ); -} - -// For handling multiple selections... -void TreeNode::OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ) -{ - CUtlVector< int > list; - m_pTreeView->GetSelectedItems( list ); - int c = list.Count(); - // walk this in reverse order so that panels are in order of selection - // even though GetSelectedItems returns items in reverse selection order - for ( int i = c - 1; i >= 0; --i ) - { - int itemIndex = list[ i ]; - // Skip self - if ( itemIndex == m_ItemIndex ) - continue; - - dragabbles.AddToTail( ( Panel * )m_pTreeView->GetItem( itemIndex ) ); - } -} - -void TreeNode::OnLabelChanged( KeyValues *data ) -{ - char const *oldString = data->GetString( "original" ); - char const *newString = data->GetString( "changed" ); - if ( m_pTreeView->IsLabelEditingAllowed() ) - { - m_pTreeView->OnLabelChanged( m_ItemIndex, oldString, newString ); - } -} - -void TreeNode::EditLabel() -{ - if ( m_pText->IsLabelEditingAllowed() && - !m_pText->IsBeingEdited() ) - { - m_pText->EnterEditingInPlace(); - } -} - -void TreeNode::SetFont(HFont font) -{ - Assert( font ); - if ( !font ) - return; - - m_pText->SetFont(font); - m_pExpandImage->SetFont(font); - InvalidateLayout(); - int i; - for (i=0;iSetFont(font); - } -} - -void TreeNode::SetKeyValues(KeyValues *data) -{ - if ( m_pData != data ) - { - if (m_pData) - { - m_pData->deleteThis(); - } - - m_pData = data->MakeCopy(); - } - - // set text - m_pText->SetText(data->GetString("Text", "")); - m_bExpandableWithoutChildren = data->GetInt("Expand"); - InvalidateLayout(); -} - -bool TreeNode::IsSelected() -{ - return m_pTreeView->IsItemSelected( m_ItemIndex ); -} - -void TreeNode::PaintBackground() -{ - if ( !m_pText->IsBeingEdited() ) - { - // setup panel drawing - if ( IsSelected() ) - { - m_pText->SelectAllText(false); - } - else - { - m_pText->SelectNoText(); - } - } - - BaseClass::PaintBackground(); -} - - -// currently unused, could be re-used if necessary -/* -bool TreeNode::IsInFocus() -{ - // check if our parent or one of it's children has focus - VPANEL focus = input()->GetFocus(); - return (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))); -} -*/ - -void TreeNode::PerformLayout() -{ - BaseClass::PerformLayout(); - - int width = 0; - if (m_pData->GetInt("SelectedImage", 0) == 0 && - m_pData->GetInt("Image", 0) == 0) - { - width = TREE_INDENT_AMOUNT; - } - else - { - width = TREE_INDENT_AMOUNT * 2; - } - - m_pText->SetPos(width, 0); - - int contentWide, contentTall; - m_pText->SetToFullWidth(); - m_pText->GetSize(contentWide, contentTall); - contentWide += 10; - m_pText->SetSize( contentWide, m_pTreeView->GetRowHeight() ); - width += contentWide; - SetSize(width, m_pTreeView->GetRowHeight()); - - m_iNodeWidth = width; - CalculateVisibleMaxWidth(); -} - -TreeNode *TreeNode::GetParentNode() -{ - if (m_pTreeView->m_NodeList.IsValidIndex(m_ParentIndex)) - { - return m_pTreeView->m_NodeList[m_ParentIndex]; - } - return NULL; -} - -int TreeNode::GetChildrenCount() -{ - return m_Children.Count(); -} - -int TreeNode::ComputeInsertionPosition( TreeNode *pChild ) -{ - if ( !m_pTreeView->m_pSortFunc ) - { - return GetChildrenCount() - 1; - } - - int start = 0, end = GetChildrenCount() - 1; - while (start <= end) - { - int mid = (start + end) >> 1; - if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) ) - { - start = mid + 1; - } - else if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[mid]->m_pData ) ) - { - end = mid - 1; - } - else - { - return mid; - } - } - return end; -} - -int TreeNode::FindChild( TreeNode *pChild ) -{ - if ( !m_pTreeView->m_pSortFunc ) - { - AssertMsg( 0, "This code has never been tested. Is it correct?" ); - for ( int i = 0; i < GetChildrenCount(); ++i ) - { - if ( m_Children[i] == pChild ) - return i; - } - return -1; - } - - // Find the first entry <= to the child - int start = 0, end = GetChildrenCount() - 1; - while (start <= end) - { - int mid = (start + end) >> 1; - - if ( m_Children[mid] == pChild ) - return mid; - - if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) ) - { - start = mid + 1; - } - else - { - end = mid - 1; - } - } - - int nMax = GetChildrenCount(); - while( end < nMax ) - { - // Stop when we reach a child that has a different value - if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[end]->m_pData ) ) - return -1; - - if ( m_Children[end] == pChild ) - return end; - - ++end; - } - - return -1; -} - -void TreeNode::AddChild(TreeNode *pChild) -{ - int i = ComputeInsertionPosition( pChild ); - m_Children.InsertAfter( i, pChild ); -} - -void TreeNode::SetNodeExpanded(bool bExpanded) -{ - m_bExpand = bExpanded; - - if (m_bExpand) - { - // see if we have any child nodes - if (GetChildrenCount() < 1) - { - // we need to get our children from the control - m_pTreeView->GenerateChildrenOfNode(m_ItemIndex); - - // if we still don't have any children, then hide the expand button - if (GetChildrenCount() < 1) - { - m_bExpand = false; - m_bExpandableWithoutChildren = false; - m_pTreeView->InvalidateLayout(); - return; - } - } - - m_pExpandImage->SetText("-"); - } - else - { - m_pExpandImage->SetText("+"); - - if ( m_bExpandableWithoutChildren && GetChildrenCount() > 0 ) - { - m_pTreeView->RemoveChildrenOfNode( m_ItemIndex ); - } - - // check if we've closed down on one of our children, if so, we get the focus - int selectedItem = m_pTreeView->GetFirstSelectedItem(); - if (selectedItem != -1 && m_pTreeView->m_NodeList[selectedItem]->HasParent(this)) - { - m_pTreeView->AddSelectedItem( m_ItemIndex, true ); - } - } - CalculateVisibleMaxWidth(); - m_pTreeView->InvalidateLayout(); -} - -bool TreeNode::IsExpanded() -{ - return m_bExpand; -} - -int TreeNode::CountVisibleNodes() -{ - int count = 1; // count myself - if (m_bExpand) - { - int i; - for (i=0;iCountVisibleNodes(); - } - } - return count; -} - -void TreeNode::CalculateVisibleMaxWidth() -{ - int width; - if (m_bExpand) - { - int childMaxWidth = GetMaxChildrenWidth(); - childMaxWidth += TREE_INDENT_AMOUNT; - - width = max(childMaxWidth, m_iNodeWidth); - } - else - { - width = m_iNodeWidth; - } - if (width != m_iMaxVisibleWidth) - { - m_iMaxVisibleWidth = width; - if (GetParentNode()) - { - GetParentNode()->OnChildWidthChange(); - } - else - { - m_pTreeView->InvalidateLayout(); - } - } -} - -void TreeNode::OnChildWidthChange() -{ - CalculateVisibleMaxWidth(); -} - -int TreeNode::GetMaxChildrenWidth() -{ - int maxWidth = 0; - int i; - for (i=0;iGetVisibleMaxWidth(); - if (childWidth > maxWidth) - { - maxWidth = childWidth; - } - } - return maxWidth; -} - -int TreeNode::GetVisibleMaxWidth() -{ - return m_iMaxVisibleWidth; -} - -int TreeNode::GetDepth() -{ - int depth = 0; - TreeNode *pParent = GetParentNode(); - while (pParent) - { - depth++; - pParent = pParent->GetParentNode(); - } - return depth; -} - -bool TreeNode::HasParent(TreeNode *pTreeNode) -{ - TreeNode *pParent = GetParentNode(); - while (pParent) - { - if (pParent == pTreeNode) - return true; - pParent = pParent->GetParentNode(); - } - return false; -} - -bool TreeNode::IsBeingDisplayed() -{ - TreeNode *pParent = GetParentNode(); - while (pParent) - { - // our parents aren't showing us - if (!pParent->m_bExpand) - return false; - - pParent = pParent->GetParentNode(); - } - return true; -} - -void TreeNode::SetVisible(bool state) -{ - BaseClass::SetVisible(state); - - bool bChildrenVisible = state && m_bExpand; - int i; - for (i=0;iSetVisible(bChildrenVisible); - } -} - -void TreeNode::Paint() -{ - if (GetChildrenCount() > 0 || m_bExpandableWithoutChildren) - { - m_pExpandImage->Paint(); - } - - // set image - int imageIndex = 0; - if (IsSelected()) - { - imageIndex = m_pData->GetInt("SelectedImage", 0); - } - else - { - imageIndex = m_pData->GetInt("Image", 0); - } - - if (imageIndex) - { - IImage *pImage = m_pTreeView->GetImage(imageIndex); - if (pImage) - { - m_pImagePanel->SetImage(pImage); - } - m_pImagePanel->Paint(); - } - - m_pText->Paint(); -} - -void TreeNode::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetBorder( NULL ); - SetFgColor( m_pTreeView->GetFgColor() ); - SetBgColor( m_pTreeView->GetBgColor() ); - SetFont( m_pTreeView->GetFont() ); -} - -void TreeNode::SetSelectionTextColor( const Color& clr ) -{ - if ( m_pText ) - { - m_pText->SetSelectionTextColor( clr ); - } -} - -void TreeNode::SetSelectionBgColor( const Color& clr ) -{ - if ( m_pText ) - { - m_pText->SetSelectionBgColor( clr ); - } -} - -void TreeNode::SetSelectionUnfocusedBgColor( const Color& clr ) -{ - if ( m_pText ) - { - m_pText->SetSelectionUnfocusedBgColor( clr ); - } -} - -void TreeNode::SetBgColor( Color color ) -{ - BaseClass::SetBgColor( color ); - if ( m_pText ) - { - m_pText->SetBgColor( color ); - } - -} - -void TreeNode::SetFgColor( Color color ) -{ - BaseClass::SetFgColor( color ); - if ( m_pText ) - { - m_pText->SetFgColor( color ); - } -} - -void TreeNode::OnSetFocus() -{ - m_pText->RequestFocus(); -} - -int TreeNode::GetPrevChildItemIndex( TreeNode *pCurrentChild ) -{ - int i; - for (i=0;im_ItemIndex; - } - } - return -1; -} - -int TreeNode::GetNextChildItemIndex( TreeNode *pCurrentChild ) -{ - int i; - for (i=0;i= GetChildrenCount() - 1 ) - return -1; - - TreeNode *pChild = m_Children[i+1]; - return pChild->m_ItemIndex; - } - } - return -1; -} - -void TreeNode::SelectPrevChild(TreeNode *pCurrentChild) -{ - int i; - for (i=0;iAddSelectedItem( m_ItemIndex, true ); - } - else - { - // see if we need to find a grandchild of the previous sibling - TreeNode *pChild = m_Children[i-1]; - - // if this child is expanded with children, then we have to find the last child - while (pChild->m_bExpand && pChild->GetChildrenCount()>0) - { - // find the last child - pChild = pChild->m_Children[pChild->GetChildrenCount()-1]; - } - m_pTreeView->AddSelectedItem( pChild->m_ItemIndex, true ); - } -} - -void TreeNode::SelectNextChild(TreeNode *pCurrentChild) -{ - int i; - for (i=0;iSelectNextChild(this); - } - } - else - { - m_pTreeView->AddSelectedItem( m_Children[i+1]->m_ItemIndex, true ); - } -} - -void TreeNode::ClosePreviousParents( TreeNode *pPreviousParent ) -{ - // close up all the open nodes we've just stepped out of. - CUtlVector< int > selected; - m_pTreeView->GetSelectedItems( selected ); - if ( selected.Count() == 0 ) - { - Assert( 0 ); - return; - } - - // Most recently clicked item - TreeNode *selectedItem = m_pTreeView->GetItem( selected[ 0 ] ); - TreeNode *pNewParent = selectedItem->GetParentNode(); - if ( pPreviousParent && pNewParent ) - { - while ( pPreviousParent->m_ItemIndex > pNewParent->m_ItemIndex ) - { - pPreviousParent->SetNodeExpanded(false); - pPreviousParent = pPreviousParent->GetParentNode(); - } - } -} - -void TreeNode::StepInto( bool bClosePrevious ) -{ - if ( !m_bExpand ) - { - SetNodeExpanded(true); - } - - if ( ( GetChildrenCount() > 0 ) && m_bExpand ) - { - m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); - } - else if ( GetParentNode() ) - { - TreeNode *pParent = GetParentNode(); - pParent->SelectNextChild(this); - - if ( bClosePrevious ) - { - ClosePreviousParents( pParent ); - } - } -} - -void TreeNode::StepOut( bool bClosePrevious ) -{ - TreeNode *pParent = GetParentNode(); - if ( pParent ) - { - m_pTreeView->AddSelectedItem( pParent->m_ItemIndex, true ); - if ( pParent->GetParentNode() ) - { - pParent->GetParentNode()->SelectNextChild(pParent); - } - if ( bClosePrevious ) - { - ClosePreviousParents( pParent ); - } - else - { - pParent->SetNodeExpanded(true); - } - } -} - -void TreeNode::StepOver( bool bClosePrevious ) -{ - TreeNode *pParent = GetParentNode(); - if ( pParent ) - { - GetParentNode()->SelectNextChild(this); - if ( bClosePrevious ) - { - ClosePreviousParents( pParent ); - } - } -} - -void TreeNode::OnKeyCodeTyped(KeyCode code) -{ - switch (code) - { - case KEY_LEFT: - { - if (m_bExpand && GetChildrenCount() > 0) - { - SetNodeExpanded(false); - } - else - { - if (GetParentNode()) - { - m_pTreeView->AddSelectedItem( GetParentNode()->m_ItemIndex, true ); - } - } - break; - } - case KEY_RIGHT: - { - if (!m_bExpand) - { - SetNodeExpanded(true); - } - else if (GetChildrenCount() > 0) - { - m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); - } - break; - } - case KEY_UP: - { - if (GetParentNode()) - { - GetParentNode()->SelectPrevChild(this); - } - break; - } - case KEY_DOWN: - { - if (GetChildrenCount() > 0 && m_bExpand) - { - m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); - } - else if (GetParentNode()) - { - GetParentNode()->SelectNextChild(this); - } - break; - } - case KEY_SPACE: - { - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); - if ( shift ) - { - StepOut( !ctrl ); - } - else if ( alt ) - { - StepOver( !ctrl ); - } - else - { - StepInto( !ctrl ); - } - break; - } - case KEY_I: - { - StepInto(); - break; - } - case KEY_U: - { - StepOut(); - break; - } - case KEY_O: - { - StepOver(); - break; - } - case KEY_ESCAPE: - { - if ( m_pTreeView->GetSelectedItemCount() > 0 ) - { - m_pTreeView->ClearSelection(); - } - else - { - BaseClass::OnKeyCodeTyped(code); - } - } - break; - case KEY_A: - { - bool ctrldown = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ); - if ( ctrldown ) - { - m_pTreeView->SelectAll(); - } - else - { - BaseClass::OnKeyCodeTyped(code); - } - } - break; - default: - BaseClass::OnKeyCodeTyped(code); - return; - } -} - -void TreeNode::OnMouseWheeled(int delta) -{ - CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); -} - -void TreeNode::OnMouseDoublePressed( MouseCode code ) -{ - int x, y; - input()->GetCursorPos(x, y); - - if (code == MOUSE_LEFT) - { - ScreenToLocal(x, y); - if (x > TREE_INDENT_AMOUNT) - { - SetNodeExpanded(!m_bExpand); - } - } -} - -bool TreeNode::IsDragEnabled() const -{ - int x, y; - input()->GetCursorPos(x, y); - ((TreeNode *)this)->ScreenToLocal(x, y); - if ( x < TREE_INDENT_AMOUNT ) - return false; - - return BaseClass::IsDragEnabled(); -} - -void TreeNode::OnMouseReleased(MouseCode code) -{ - BaseClass::OnMouseReleased( code ); - - if ( input()->GetMouseCapture() == GetVPanel() ) - { - input()->SetMouseCapture( NULL ); - return; - } - int x, y; - input()->GetCursorPos(x, y); - ScreenToLocal(x, y); - - if ( x < TREE_INDENT_AMOUNT ) - return; - - bool ctrldown = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool shiftdown = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - - if ( !ctrldown && !shiftdown && ( code == MOUSE_LEFT ) ) - { - m_pTreeView->AddSelectedItem( m_ItemIndex, true ); - } -} - -void TreeNode::OnCursorMoved( int x, int y ) -{ - if ( input()->GetMouseCapture() != GetVPanel() ) - return; - - LocalToScreen( x, y ); - m_pTreeView->ScreenToLocal( x, y ); - int newItem = m_pTreeView->FindItemUnderMouse( x, y ); - if ( newItem == -1 ) - { - // Fixme: Figure out best item - return; - } - - int startItem = m_nClickedItem; - int endItem = newItem; - if ( startItem > endItem ) - { - int temp = startItem; - startItem = endItem; - endItem = temp; - } - - CUtlVector< TreeNode * > list; - m_pTreeView->m_pRootNode->FindNodesInRange( list, startItem, endItem ); - - int c = list.Count(); - for ( int i = 0; i < c; ++i ) - { - TreeNode *item = list[ i ]; - if ( m_bClickedSelected ) - { - m_pTreeView->AddSelectedItem( item->m_ItemIndex, false ); - } - else - { - m_pTreeView->RemoveSelectedItem( item->m_ItemIndex ); - } - } -} - -void TreeNode::OnMousePressed( MouseCode code) -{ - BaseClass::OnMousePressed( code ); - - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - int x, y; - input()->GetCursorPos(x, y); - - bool bExpandTree = m_pTreeView->m_bLeftClickExpandsTree; - - if ( code == MOUSE_LEFT ) - { - ScreenToLocal(x, y); - if ( x < TREE_INDENT_AMOUNT ) - { - if ( bExpandTree ) - { - SetNodeExpanded(!m_bExpand); - } - // m_pTreeView->SetSelectedItem(m_ItemIndex); // explorer doesn't actually select item when it expands an item - // purposely commented out in case we want to change the behavior - } - else - { - m_nClickedItem = m_ItemIndex; - if ( m_pTreeView->IsMultipleItemDragEnabled() ) - { - input()->SetMouseCapture( GetVPanel() ); - } - - if ( shift ) - { - m_pTreeView->RangeSelectItems( m_ItemIndex ); - } - else - { - if ( !IsSelected() || ctrl ) - { - if ( IsSelected() && ctrl ) - { - m_pTreeView->RemoveSelectedItem( m_ItemIndex ); - } - else - { - m_pTreeView->AddSelectedItem( m_ItemIndex, !ctrl ); - } - } - else if ( IsSelected() && m_pTreeView->IsMultipleItemDragEnabled() ) - { - m_pTreeView->AddSelectedItem( m_ItemIndex, !shift ); - } - } - - m_bClickedSelected = m_pTreeView->IsItemSelected( m_ItemIndex ); - } - } - else if (code == MOUSE_RIGHT) - { - // context menu selection - // If the item was selected, leave selected items alone, otherwise make it the only selected item - if ( !m_pTreeView->IsItemSelected( m_ItemIndex ) ) - { - m_pTreeView->AddSelectedItem( m_ItemIndex, true ); - } - - // ask parent to context menu - m_pTreeView->GenerateContextMenu(m_ItemIndex, x, y); - } -} - -void TreeNode::RemoveChildren() -{ - int c = m_Children.Count(); - for ( int i = c - 1 ; i >= 0 ; --i ) - { - m_pTreeView->RemoveItem( m_Children[ i ]->m_ItemIndex, false, true ); - } - m_Children.RemoveAll(); -} - -void TreeNode::FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex ) -{ - list.RemoveAll(); - bool finished = false; - bool foundstart = false; - FindNodesInRange_R( list, finished, foundstart, startIndex, endIndex ); -} - -void TreeNode::FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex ) -{ - if ( finished ) - return; - if ( foundStart == true ) - { - list.AddToTail( this ); - - if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex ) - { - finished = true; - return; - } - } - else if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex ) - { - foundStart = true; - list.AddToTail( this ); - if ( startIndex == endIndex ) - { - finished = true; - return; - } - } - - if ( !m_bExpand ) - return; - - - int i; - int c = GetChildrenCount(); - for (i=0;iFindNodesInRange_R( list, finished, foundStart, startIndex, endIndex ); - } -} - -void TreeNode::PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y) -{ - // position ourselves - if (nStart == 0) - { - BaseClass::SetVisible(true); - SetPos(x, y); - y += m_pTreeView->GetRowHeight(); // m_nRowHeight - nCount--; - } - else // still looking for first element - { - nStart--; - BaseClass::SetVisible(false); - } - - x += TREE_INDENT_AMOUNT; - int i; - for (i=0;i 0 && m_bExpand) - { - m_Children[i]->PositionAndSetVisibleNodes(nStart, nCount, x, y); - } - else - { - m_Children[i]->SetVisible(false); // this will make all grand children hidden as well - } - } -} - -TreeNode *TreeNode::FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my ) -{ - // position ourselves - if (nStart == 0) - { - int posx, posy; - GetPos(posx, posy); - if ( my >= posy && my < posy + m_pTreeView->GetRowHeight() ) - { - return this; - } - y += m_pTreeView->GetRowHeight(); - nCount--; - } - else // still looking for first element - { - nStart--; - } - - x += TREE_INDENT_AMOUNT; - int i; - for (i=0;i 0 && m_bExpand) - { - TreeNode *child = m_Children[i]->FindItemUnderMouse(nStart, nCount, x, y, mx, my); - if ( child != NULL ) - { - return child; - } - } - } - - return NULL; -} - -// counts items above this item including itself -int TreeNode::CountVisibleIndex() -{ - int nCount = 1; // myself - if (GetParentNode()) - { - int i; - for (i=0;iGetChildrenCount();i++) - { - if (GetParentNode()->m_Children[i] == this) - break; - - nCount += GetParentNode()->m_Children[i]->CountVisibleNodes(); - } - return nCount + GetParentNode()->CountVisibleIndex(); - } - else - return nCount; -} - - -}; // namespace vgui - -DECLARE_BUILD_FACTORY( TreeView ); - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -TreeView::TreeView(Panel *parent, const char *panelName) : Panel(parent, panelName) -{ - m_bScrollbarExternal[ 0 ] = m_bScrollbarExternal[ 1 ] = false; - m_nRowHeight = 20; - m_pRootNode = NULL; - m_pImageList = NULL; - m_pSortFunc = NULL; - m_Font = 0; - - m_pSubPanel = new TreeViewSubPanel(this); - m_pSubPanel->SetVisible(true); - m_pSubPanel->SetPos(0,0); - - m_pHorzScrollBar = new ScrollBar(this, "HorizScrollBar", false); - m_pHorzScrollBar->AddActionSignalTarget(this); - m_pHorzScrollBar->SetVisible(false); - - m_pVertScrollBar = new ScrollBar(this, "VertScrollBar", true); - m_pVertScrollBar->SetVisible(false); - m_pVertScrollBar->AddActionSignalTarget(this); - - m_bAllowLabelEditing = false; - m_bDragEnabledItems = false; - m_bDeleteImageListWhenDone = false; - m_bLabelBeingEdited = false; - m_bMultipleItemDragging = false; - m_bLeftClickExpandsTree = true; - m_bAllowMultipleSelections = false; - m_nMostRecentlySelectedItem = -1; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -TreeView::~TreeView() -{ - CleanUpImageList(); -} - - -//----------------------------------------------------------------------------- -// Clean up the image list -//----------------------------------------------------------------------------- -void TreeView::CleanUpImageList( ) -{ - if ( m_pImageList ) - { - if ( m_bDeleteImageListWhenDone ) - { - delete m_pImageList; - } - m_pImageList = NULL; - } -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::SetSortFunc(TreeViewSortFunc_t pSortFunc) -{ - m_pSortFunc = pSortFunc; -} - -HFont TreeView::GetFont() -{ - return m_Font; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::SetFont(HFont font) -{ - Assert( font ); - if ( !font ) - return; - - m_Font = font; - m_nRowHeight = surface()->GetFontTall(font) + 2; - - if (m_pRootNode) - { - m_pRootNode->SetFont(font); - } - InvalidateLayout(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetRowHeight() -{ - return m_nRowHeight; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetVisibleMaxWidth() -{ - if (m_pRootNode) - { - return m_pRootNode->GetVisibleMaxWidth(); - } - else - { - return 0; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::AddItem(KeyValues *data, int parentItemIndex) -{ - Assert(parentItemIndex == -1 || m_NodeList.IsValidIndex(parentItemIndex)); - - TreeNode *pTreeNode = new TreeNode(m_pSubPanel, this); - pTreeNode->SetDragEnabled( m_bDragEnabledItems ); - pTreeNode->m_ItemIndex = m_NodeList.AddToTail(pTreeNode); - pTreeNode->SetKeyValues(data); - - if ( m_Font != 0 ) - { - pTreeNode->SetFont( m_Font ); - } - pTreeNode->SetBgColor( GetBgColor() ); - - if ( data->GetInt( "droppable", 0 ) != 0 ) - { - float flContextDelay = data->GetFloat( "drophoverdelay" ); - if ( flContextDelay ) - { - pTreeNode->SetDropEnabled( true, flContextDelay ); - } - else - { - pTreeNode->SetDropEnabled( true ); - } - } - - // there can be only one root - if (parentItemIndex == -1) - { - Assert(m_pRootNode == NULL); - m_pRootNode = pTreeNode; - pTreeNode->m_ParentIndex = -1; - } - else - { - pTreeNode->m_ParentIndex = parentItemIndex; - - // add to parent list - pTreeNode->GetParentNode()->AddChild(pTreeNode); - } - - SETUP_PANEL( pTreeNode ); - - return pTreeNode->m_ItemIndex; -} - - -int TreeView::GetRootItemIndex() -{ - if ( m_pRootNode ) - return m_pRootNode->m_ItemIndex; - else - return -1; -} - - -int TreeView::GetNumChildren( int itemIndex ) -{ - if ( itemIndex == -1 ) - return 0; - - return m_NodeList[itemIndex]->m_Children.Count(); -} - - -int TreeView::GetChild( int iParentItemIndex, int iChild ) -{ - return m_NodeList[iParentItemIndex]->m_Children[iChild]->m_ItemIndex; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : itemIndex - -// Output : TreeNode -//----------------------------------------------------------------------------- -TreeNode *TreeView::GetItem( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - { - Assert( 0 ); - return NULL; - } - - return m_NodeList[ itemIndex ]; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetItemCount(void) -{ - return m_NodeList.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -KeyValues* TreeView::GetItemData(int itemIndex) -{ - if (!m_NodeList.IsValidIndex(itemIndex)) - return NULL; - else - return m_NodeList[itemIndex]->m_pData; -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::RemoveItem(int itemIndex, bool bPromoteChildren, bool bFullDelete ) -{ - // HACK: there's a bug with RemoveItem where panels are lingering. This gets around it temporarily. - - // FIXME: Negative item indices is a bogus interface method! - // because what if you want to recursively remove everything under node 0? - // Use the bFullDelete parameter instead. - if ( itemIndex < 0 ) - { - itemIndex = -itemIndex; - bFullDelete = true; - } - - if (!m_NodeList.IsValidIndex(itemIndex)) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - TreeNode *pParent = pNode->GetParentNode(); - - // are we promoting the children - if (bPromoteChildren && pParent) - { - int i; - for (i=0;iGetChildrenCount();i++) - { - TreeNode *pChild = pNode->m_Children[i]; - pChild->m_ParentIndex = pParent->m_ItemIndex; - } - } - else - { - // delete our children - if ( bFullDelete ) - { - while ( pNode->GetChildrenCount() ) - RemoveItem( -pNode->m_Children[0]->m_ItemIndex, false ); - } - else - { - int i; - for (i=0;iGetChildrenCount();i++) - { - TreeNode *pDeleteChild = pNode->m_Children[i]; - RemoveItem(pDeleteChild->m_ItemIndex, false); - } - } - } - - // remove from our parent's children list - if (pParent) - { - pParent->m_Children.FindAndRemove(pNode); - } - - // finally get rid of ourselves from the main list - m_NodeList.Remove(itemIndex); - - if ( bFullDelete ) - delete pNode; - else - pNode->MarkForDeletion(); - - // Make sure we don't leave ourselves with an invalid selected item. - m_SelectedItems.FindAndRemove( pNode ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::RemoveAll() -{ - int i; - for (i=0;iMarkForDeletion(); - } - m_NodeList.RemoveAll(); - m_pRootNode = NULL; - ClearSelection(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool TreeView::ModifyItem(int itemIndex, KeyValues *data) -{ - if (!m_NodeList.IsValidIndex(itemIndex)) - return false; - - TreeNode *pNode = m_NodeList[itemIndex]; - TreeNode *pParent = pNode->GetParentNode(); - bool bReSort = ( m_pSortFunc && pParent ); - int nChildIndex = -1; - if ( bReSort ) - { - nChildIndex = pParent->FindChild( pNode ); - } - - pNode->SetKeyValues(data); - - // Changing the data can cause it to re-sort - if ( bReSort ) - { - int nChildren = pParent->GetChildrenCount(); - bool bLeftBad = (nChildIndex > 0) && m_pSortFunc( pNode->m_pData, pParent->m_Children[nChildIndex-1]->m_pData ); - bool bRightBad = (nChildIndex < nChildren - 1) && m_pSortFunc( pParent->m_Children[nChildIndex+1]->m_pData, pNode->m_pData ); - if ( bLeftBad || bRightBad ) - { - pParent->m_Children.Remove( nChildIndex ); - pParent->AddChild( pNode ); - } - } - - InvalidateLayout(); - - return true; -} - -//----------------------------------------------------------------------------- -// Purpose: set the selection colors of an element in the tree view -//----------------------------------------------------------------------------- - -void TreeView::SetItemSelectionTextColor( int itemIndex, const Color& clr ) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetSelectionTextColor( clr ); -} - -void TreeView::SetItemSelectionBgColor( int itemIndex, const Color& clr ) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetSelectionBgColor( clr ); -} - -void TreeView::SetItemSelectionUnfocusedBgColor( int itemIndex, const Color& clr ) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetSelectionUnfocusedBgColor( clr ); -} - -//----------------------------------------------------------------------------- -// Purpose: set the fg color of an element in the tree view -//----------------------------------------------------------------------------- -void TreeView::SetItemFgColor(int itemIndex, const Color& color) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetFgColor( color ); -} - -//----------------------------------------------------------------------------- -// Purpose: set the bg color of an element in the tree view -//----------------------------------------------------------------------------- -void TreeView::SetItemBgColor(int itemIndex, const Color& color) -{ - Assert( m_NodeList.IsValidIndex(itemIndex) ); - if ( !m_NodeList.IsValidIndex(itemIndex) ) - return; - - TreeNode *pNode = m_NodeList[itemIndex]; - pNode->SetBgColor( color ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetItemParent(int itemIndex) -{ - return m_NodeList[itemIndex]->m_ParentIndex; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) -{ - CleanUpImageList(); - m_pImageList = imageList; - m_bDeleteImageListWhenDone = deleteImageListWhenDone; -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -IImage *TreeView::GetImage(int index) -{ - return m_pImageList->GetImage(index); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::GetSelectedItems( CUtlVector< int >& list ) -{ - list.RemoveAll(); - - int c = m_SelectedItems.Count(); - for ( int i = 0 ; i < c; ++i ) - { - list.AddToTail( m_SelectedItems[ i ]->m_ItemIndex ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::GetSelectedItemData( CUtlVector< KeyValues * >& list ) -{ - list.RemoveAll(); - - int c = m_SelectedItems.Count(); - for ( int i = 0 ; i < c; ++i ) - { - list.AddToTail( m_SelectedItems[ i ]->m_pData ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -bool TreeView::IsItemIDValid(int itemIndex) -{ - return m_NodeList.IsValidIndex(itemIndex); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -int TreeView::GetHighestItemID() -{ - return m_NodeList.MaxElementIndex(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::ExpandItem(int itemIndex, bool bExpand) -{ - if (!m_NodeList.IsValidIndex(itemIndex)) - return; - - m_NodeList[itemIndex]->SetNodeExpanded(bExpand); - InvalidateLayout(); -} - -bool TreeView::IsItemExpanded( int itemIndex ) -{ - if (!m_NodeList.IsValidIndex(itemIndex)) - return false; - - return m_NodeList[itemIndex]->IsExpanded(); -} - - -//----------------------------------------------------------------------------- -// Purpose: Scrolls the list according to the mouse wheel movement -//----------------------------------------------------------------------------- -void TreeView::OnMouseWheeled(int delta) -{ - if ( !m_pVertScrollBar->IsVisible() ) - { - return; - } - int val = m_pVertScrollBar->GetValue(); - val -= (delta * 3); - m_pVertScrollBar->SetValue(val); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::OnSizeChanged(int wide, int tall) -{ - BaseClass::OnSizeChanged(wide, tall); - InvalidateLayout(); - Repaint(); -} - -void TreeView::GetScrollBarSize( bool vertical, int& w, int& h ) -{ - int idx = vertical ? 0 : 1; - - if ( m_bScrollbarExternal[ idx ] ) - { - w = h = 0; - return; - } - - if ( vertical ) - { - m_pVertScrollBar->GetSize( w, h ); - } - else - { - m_pHorzScrollBar->GetSize( w, h ); - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::PerformLayout() -{ - int wide, tall; - GetSize( wide, tall ); - - if ( !m_pRootNode ) - { - m_pSubPanel->SetSize( wide, tall ); - return; - } - - int sbhw, sbhh; - GetScrollBarSize( false, sbhw, sbhh ); - int sbvw, sbvh; - GetScrollBarSize( true, sbvw, sbvh ); - - bool vbarNeeded = false; - bool hbarNeeded = false; - - // okay we have to check if we need either scroll bars, since if we need one - // it might make it necessary to have the other one - int nodesVisible = tall / m_nRowHeight; - - // count the number of visible items - int visibleItemCount = m_pRootNode->CountVisibleNodes(); - int maxWidth = m_pRootNode->GetVisibleMaxWidth() + 10; // 10 pixel buffer - - vbarNeeded = visibleItemCount > nodesVisible; - - if (!vbarNeeded) - { - if (maxWidth > wide) - { - hbarNeeded = true; - - // recalculate if vbar is needed now - // double check that we really don't need it - nodesVisible = (tall - sbhh) / m_nRowHeight; - vbarNeeded = visibleItemCount > nodesVisible; - } - } - else - { - // we've got the vertical bar here, so shrink the width - hbarNeeded = maxWidth > (wide - (sbvw+2)); - - if (hbarNeeded) - { - nodesVisible = (tall - sbhh) / m_nRowHeight; - } - } - - int subPanelWidth = wide; - int subPanelHeight = tall; - - int vbarPos = 0; - if (vbarNeeded) - { - subPanelWidth -= (sbvw + 2); - int barSize = tall; - if (hbarNeeded) - { - barSize -= sbhh; - } - - //!! need to make it recalculate scroll positions - m_pVertScrollBar->SetVisible(true); - m_pVertScrollBar->SetEnabled(false); - m_pVertScrollBar->SetRangeWindow( nodesVisible ); - m_pVertScrollBar->SetRange( 0, visibleItemCount); - m_pVertScrollBar->SetButtonPressedScrollValue( 1 ); - - if ( !m_bScrollbarExternal[ 0 ] ) - { - m_pVertScrollBar->SetPos(wide - (sbvw + WINDOW_BORDER_WIDTH), 0); - m_pVertScrollBar->SetSize(sbvw, barSize - 2); - } - - // need to figure out - vbarPos = m_pVertScrollBar->GetValue(); - } - else - { - m_pVertScrollBar->SetVisible(false); - m_pVertScrollBar->SetValue( 0 ); - } - - int hbarPos = 0; - if (hbarNeeded) - { - subPanelHeight -= (sbhh + 2); - int barSize = wide; - if (vbarNeeded) - { - barSize -= sbvw; - } - m_pHorzScrollBar->SetVisible(true); - m_pHorzScrollBar->SetEnabled(false); - m_pHorzScrollBar->SetRangeWindow( barSize ); - m_pHorzScrollBar->SetRange( 0, maxWidth); - m_pHorzScrollBar->SetButtonPressedScrollValue( 10 ); - - if ( !m_bScrollbarExternal[ 1 ] ) - { - m_pHorzScrollBar->SetPos(0, tall - (sbhh + WINDOW_BORDER_WIDTH)); - m_pHorzScrollBar->SetSize(barSize - 2, sbhh); - } - - hbarPos = m_pHorzScrollBar->GetValue(); - } - else - { - m_pHorzScrollBar->SetVisible(false); - m_pHorzScrollBar->SetValue( 0 ); - } - - m_pSubPanel->SetSize(subPanelWidth, subPanelHeight); - - int y = 0; - m_pRootNode->PositionAndSetVisibleNodes(vbarPos, visibleItemCount, -hbarPos, y); - - Repaint(); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::MakeItemVisible(int itemIndex) -{ - // first make sure that all parents are expanded - TreeNode *pNode = m_NodeList[itemIndex]; - TreeNode *pParent = pNode->GetParentNode(); - while (pParent) - { - if (!pParent->m_bExpand) - { - pParent->SetNodeExpanded(true); - } - pParent = pParent->GetParentNode(); - } - - // recalculate scroll bar due to possible exapnsion - PerformLayout(); - - if (!m_pVertScrollBar->IsVisible()) - return; - - int visibleIndex = pNode->CountVisibleIndex()-1; - int range = m_pVertScrollBar->GetRangeWindow(); - int vbarPos = m_pVertScrollBar->GetValue(); - - // do we need to scroll up or down? - if (visibleIndex < vbarPos) - { - m_pVertScrollBar->SetValue(visibleIndex); - } - else if (visibleIndex+1 > vbarPos+range) - { - m_pVertScrollBar->SetValue(visibleIndex+1-range); - } - InvalidateLayout(); -} - -void TreeView::GetVBarInfo( int &top, int &nItemsVisible, bool& hbarVisible ) -{ - int wide, tall; - GetSize( wide, tall ); - nItemsVisible = tall / m_nRowHeight; - - if ( m_pVertScrollBar->IsVisible() ) - { - top = m_pVertScrollBar->GetValue(); - } - else - { - top = 0; - } - hbarVisible = m_pHorzScrollBar->IsVisible(); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::ApplySchemeSettings(IScheme *pScheme) -{ - BaseClass::ApplySchemeSettings(pScheme); - - SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); - SetBgColor(GetSchemeColor("TreeView.BgColor", GetSchemeColor("WindowDisabledBgColor", pScheme), pScheme)); - SetFont( pScheme->GetFont( "Default", IsProportional() ) ); - m_pSubPanel->SetBgColor( GetBgColor() ); -} - - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::SetBgColor( Color color ) -{ - BaseClass::SetBgColor( color ); - m_pSubPanel->SetBgColor( color ); -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::OnSliderMoved( int position ) -{ - InvalidateLayout(); - Repaint(); -} - -void TreeView::GenerateDragDataForItem( int itemIndex, KeyValues *msg ) -{ - // Implemented by subclassed TreeView -} - -void TreeView::SetDragEnabledItems( bool state ) -{ - m_bDragEnabledItems = state; -} - -void TreeView::OnLabelChanged( int itemIndex, char const *oldString, char const *newString ) -{ -} - -bool TreeView::IsLabelEditingAllowed() const -{ - return m_bAllowLabelEditing; -} - -void TreeView::SetLabelBeingEdited( bool state ) -{ - m_bLabelBeingEdited = state; -} - -bool TreeView::IsLabelBeingEdited() const -{ - return m_bLabelBeingEdited; -} - -void TreeView::SetAllowLabelEditing( bool state ) -{ - m_bAllowLabelEditing = state; -} - -void TreeView::EnableExpandTreeOnLeftClick( bool bEnable ) -{ - m_bLeftClickExpandsTree = bEnable; -} - -int TreeView::FindItemUnderMouse( int mx, int my ) -{ - mx = clamp( mx, 0, GetWide() - 1 ); - my = clamp( my, 0, GetTall() - 1 ); - if ( mx >= TREE_INDENT_AMOUNT ) - { - // Find what's under this position - // need to figure out - int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0; - int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0; - int count = m_pRootNode->CountVisibleNodes(); - - int y = 0; - TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my ); - if ( item ) - { - return item->m_ItemIndex; - } - } - - return -1; -} - -void TreeView::OnMousePressed( MouseCode code ) -{ - bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); - bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); - - // Try to map mouse position to a row - if ( code == MOUSE_LEFT && m_pRootNode ) - { - int mx, my; - input()->GetCursorPos( mx, my ); - ScreenToLocal( mx, my ); - if ( mx >= TREE_INDENT_AMOUNT ) - { - // Find what's under this position - // need to figure out - int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0; - int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0; - int count = m_pRootNode->CountVisibleNodes(); - - int y = 0; - TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my ); - if ( item ) - { - if ( !item->IsSelected() ) - { - AddSelectedItem( item->m_ItemIndex, !ctrl && !shift ); - } - return; - } - else - { - ClearSelection(); - } - } - } - - BaseClass::OnMousePressed( code ); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : state - -//----------------------------------------------------------------------------- -void TreeView::SetAllowMultipleSelections( bool state ) -{ - m_bAllowMultipleSelections = state; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool TreeView::IsMultipleSelectionAllowed() const -{ - return m_bAllowMultipleSelections; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : int -//----------------------------------------------------------------------------- -int TreeView::GetSelectedItemCount() const -{ - return m_SelectedItems.Count(); -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -//----------------------------------------------------------------------------- -void TreeView::ClearSelection() -{ - m_SelectedItems.RemoveAll(); - m_nMostRecentlySelectedItem = -1; - PostActionSignal( new KeyValues( "TreeViewItemSelectionCleared" ) ); -} - -void TreeView::RangeSelectItems( int endItem ) -{ - int startItem = m_nMostRecentlySelectedItem; - ClearSelection(); - m_nMostRecentlySelectedItem = startItem; - - if ( !m_NodeList.IsValidIndex( startItem ) ) - { - AddSelectedItem( endItem, false ); - return; - } - - Assert( m_NodeList.IsValidIndex( endItem ) ); - - if ( !m_pRootNode ) - { - return; - } - - CUtlVector< TreeNode * > list; - m_pRootNode->FindNodesInRange( list, startItem, endItem ); - - int c = list.Count(); - for ( int i = 0; i < c; ++i ) - { - TreeNode *item = list[ i ]; - AddSelectedItem( item->m_ItemIndex, false ); - } -} - -void TreeView::FindNodesInRange( int startItem, int endItem, CUtlVector< int >& itemIndices ) -{ - CUtlVector< TreeNode * > nodes; - m_pRootNode->FindNodesInRange( nodes, startItem, endItem ); - - int c = nodes.Count(); - for ( int i = 0; i < c; ++i ) - { - TreeNode *item = nodes[ i ]; - itemIndices.AddToTail( item->m_ItemIndex ); - } -} - -void TreeView::RemoveSelectedItem( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - TreeNode *sel = m_NodeList[ itemIndex ]; - Assert( sel ); - int slot = m_SelectedItems.Find( sel ); - if ( slot != m_SelectedItems.InvalidIndex() ) - { - m_SelectedItems.Remove( slot ); - PostActionSignal( new KeyValues( "TreeViewItemDeselected", "itemIndex", itemIndex ) ); - - m_nMostRecentlySelectedItem = itemIndex; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -//----------------------------------------------------------------------------- -void TreeView::AddSelectedItem( int itemIndex, bool clearCurrentSelection, bool requestFocus /* = true */, bool bMakeItemVisible /*= true*/ ) -{ - if ( clearCurrentSelection ) - { - ClearSelection(); - } - - // Assume it's bogus - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - TreeNode *sel = m_NodeList[ itemIndex ]; - Assert( sel ); - if ( requestFocus ) - { - sel->RequestFocus(); - } - - // Item 0 is most recently selected!!! - int slot = m_SelectedItems.Find( sel ); - if ( slot == m_SelectedItems.InvalidIndex() ) - { - m_SelectedItems.AddToHead( sel ); - } - else if ( slot != 0 ) - { - m_SelectedItems.Remove( slot ); - m_SelectedItems.AddToHead( sel ); - } - - if ( bMakeItemVisible ) - { - MakeItemVisible( itemIndex ); - } - - PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", itemIndex ) ); - InvalidateLayout(); - - if ( clearCurrentSelection ) - { - m_nMostRecentlySelectedItem = itemIndex; - } -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : - -// Output : int -//----------------------------------------------------------------------------- -int TreeView::GetFirstSelectedItem() const -{ - if ( m_SelectedItems.Count() <= 0 ) - return -1; - return m_SelectedItems[ 0 ]->m_ItemIndex; -} - -//----------------------------------------------------------------------------- -// Purpose: -// Input : itemIndex - -// Output : Returns true on success, false on failure. -//----------------------------------------------------------------------------- -bool TreeView::IsItemSelected( int itemIndex ) -{ - // Assume it's bogus - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return false; - - TreeNode *sel = m_NodeList[ itemIndex ]; - return m_SelectedItems.Find( sel ) != m_SelectedItems.InvalidIndex(); -} - -void TreeView::SetLabelEditingAllowed( int itemIndex, bool state ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - TreeNode *sel = m_NodeList[ itemIndex ]; - sel->SetLabelEditingAllowed( state ); -} - -void TreeView::StartEditingLabel( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - Assert( IsLabelEditingAllowed() ); - - TreeNode *sel = m_NodeList[ itemIndex ]; - Assert( sel->IsLabelEditingAllowed() ); - if ( !sel->IsLabelEditingAllowed() ) - return; - - sel->EditLabel(); -} - -int TreeView::GetPrevChildItemIndex( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return -1; - TreeNode *sel = m_NodeList[ itemIndex ]; - TreeNode *parent = sel->GetParentNode(); - if ( !parent ) - return -1; - - return parent->GetPrevChildItemIndex( sel ); -} - -int TreeView::GetNextChildItemIndex( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return -1; - TreeNode *sel = m_NodeList[ itemIndex ]; - TreeNode *parent = sel->GetParentNode(); - if ( !parent ) - return -1; - - return parent->GetNextChildItemIndex( sel ); -} - -bool TreeView::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ) -{ - // Derived classes should implement - return false; -} - -void TreeView::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ) -{ -} - -bool TreeView::GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist ) -{ - return false; -} - -HCursor TreeView::GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist ) -{ - return dc_arrow; -} - -void TreeView::RemoveChildrenOfNode( int itemIndex ) -{ - if ( !m_NodeList.IsValidIndex( itemIndex ) ) - return; - - TreeNode *node = m_NodeList[ itemIndex ]; - node->RemoveChildren(); -} - -ScrollBar *TreeView::SetScrollBarExternal( bool vertical, Panel *newParent ) -{ - if ( vertical ) - { - m_bScrollbarExternal[ 0 ] = true; - m_pVertScrollBar->SetParent( newParent ); - return m_pVertScrollBar; - } - m_bScrollbarExternal[ 1 ] = true; - m_pHorzScrollBar->SetParent( newParent ); - return m_pHorzScrollBar; -} - -// if this is set, then clicking on one row and dragging will select a run or items, etc. -void TreeView::SetMultipleItemDragEnabled( bool state ) -{ - m_bMultipleItemDragging = state; -} - -bool TreeView::IsMultipleItemDragEnabled() const -{ - return m_bMultipleItemDragging; -} - -void TreeView::SelectAll() -{ - m_SelectedItems.RemoveAll(); - FOR_EACH_LL( m_NodeList, i ) - { - m_SelectedItems.AddToTail( m_NodeList[ i ] ); - } - - PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", GetRootItemIndex() ) ); - InvalidateLayout(); -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#define PROTECTED_THINGS_DISABLE + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "tier1/utlstring.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +#ifndef max +#define max(a,b) (((a) > (b)) ? (a) : (b)) +#endif + +using namespace vgui; +enum +{ + WINDOW_BORDER_WIDTH=2 // the width of the window's border +}; + +#define TREE_INDENT_AMOUNT 20 + +namespace vgui +{ + +//----------------------------------------------------------------------------- +// Purpose: Displays an editable text field for the text control +//----------------------------------------------------------------------------- +class TreeNodeText : public TextEntry +{ + DECLARE_CLASS_SIMPLE( TreeNodeText, TextEntry ); + +public: + TreeNodeText(Panel *parent, const char *panelName, TreeView *tree) : BaseClass(parent, panelName), m_pTree( tree ) + { + m_bEditingInPlace = false; + m_bLabelEditingAllowed = false; + SetDragEnabled( false ); + SetDropEnabled( false ); + AddActionSignalTarget( this ); + m_bArmForEditing = false; + m_bWaitingForRelease = false; + m_lArmingTime = 0L; + SetAllowKeyBindingChainToParent( true ); + } + + MESSAGE_FUNC( OnTextChanged, "TextChanged" ) + { + GetParent()->InvalidateLayout(); + } + + bool IsKeyRebound( KeyCode code, int modifiers ) + { + // If in editing mode, don't try and chain keypresses + if ( m_bEditingInPlace ) + { + return false; + } + + return BaseClass::IsKeyRebound( code, modifiers ); + } + + virtual void PaintBackground() + { + BaseClass::PaintBackground(); + + if ( !m_bLabelEditingAllowed ) + return; + + if ( !m_bEditingInPlace ) + return; + + int w, h; + GetSize( w, h ); + surface()->DrawSetColor( GetFgColor() ); + surface()->DrawOutlinedRect( 0, 0, w, h ); + } + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + TextEntry::ApplySchemeSettings(pScheme); + SetBorder(NULL); + SetCursor(dc_arrow); + } + + virtual void OnKeyCodeTyped(KeyCode code) + { + if ( m_bEditingInPlace ) + { + if ( code == KEY_ENTER ) + { + FinishEditingInPlace(); + } + else if ( code == KEY_ESCAPE ) + { + FinishEditingInPlace( true ); + } + else + { + BaseClass::OnKeyCodeTyped( code ); + } + return; + } + else if ( code == KEY_ENTER && IsLabelEditingAllowed() ) + { + EnterEditingInPlace(); + } + else + { + // let parent deal with it (don't chain back to TextEntry) + CallParentFunction(new KeyValues("KeyCodeTyped", "code", code)); + } + } + +#define CLICK_TO_EDIT_DELAY_MSEC 500 + + virtual void OnTick() + { + BaseClass::OnTick(); + if ( m_bArmForEditing ) + { + long msecSinceArming = system()->GetTimeMillis() - m_lArmingTime; + + if ( msecSinceArming > CLICK_TO_EDIT_DELAY_MSEC ) + { + m_bArmForEditing = false; + m_bWaitingForRelease = false; + ivgui()->RemoveTickSignal( GetVPanel() ); + EnterEditingInPlace(); + } + } + } + + virtual void OnMouseReleased( MouseCode code ) + { + if ( m_bEditingInPlace ) + { + BaseClass::OnMouseReleased( code ); + return; + } + else + { + if ( m_bWaitingForRelease && !IsBeingDragged() ) + { + m_bArmForEditing = true; + m_bWaitingForRelease = false; + m_lArmingTime = system()->GetTimeMillis(); + ivgui()->AddTickSignal( GetVPanel() ); + } + else + { + m_bWaitingForRelease = false; + } + } + + // let parent deal with it + CallParentFunction(new KeyValues("MouseReleased", "code", code)); + } + + virtual void OnCursorMoved( int x, int y ) + { + // let parent deal with it + CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); + } + + virtual void OnMousePressed(MouseCode code) + { + if ( m_bEditingInPlace ) + { + BaseClass::OnMousePressed( code ); + return; + } + else + { + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + + // make sure there is only one item selected + // before "WaitingForRelease" which leads to label editing. + CUtlVector< int > list; + m_pTree->GetSelectedItems( list ); + bool bIsOnlyOneItemSelected = ( list.Count() == 1 ); + + if ( !shift && + !ctrl && + !m_bArmForEditing && + IsLabelEditingAllowed() && + bIsOnlyOneItemSelected && + IsTextFullySelected() && + !IsBeingDragged() ) + { + m_bWaitingForRelease = true; + } + } + + // let parent deal with it + CallParentFunction(new KeyValues("MousePressed", "code", code)); + } + + void SetLabelEditingAllowed( bool state ) + { + m_bLabelEditingAllowed = state; + } + + bool IsLabelEditingAllowed() + { + return m_bLabelEditingAllowed; + } + + virtual void OnMouseDoublePressed(MouseCode code) + { + // Once we are editing, double pressing shouldn't chain up + if ( m_bEditingInPlace ) + { + BaseClass::OnMouseDoublePressed( code ); + return; + } + + if ( m_bArmForEditing ) + { + m_bArmForEditing = false; + m_bWaitingForRelease = false; + ivgui()->RemoveTickSignal( GetVPanel() ); + } + + CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); + } + + void EnterEditingInPlace() + { + if ( m_bEditingInPlace ) + return; + + m_bEditingInPlace = true; + char buf[ 1024 ]; + GetText( buf, sizeof( buf ) ); + m_OriginalText = buf; + SetCursor(dc_ibeam); + SetEditable( true ); + SelectNone(); + GotoTextEnd(); + RequestFocus(); + SelectAllText(false); + m_pTree->SetLabelBeingEdited( true ); + } + + void FinishEditingInPlace( bool revert = false ) + { + if ( !m_bEditingInPlace ) + return; + + m_pTree->SetLabelBeingEdited( false ); + SetEditable( false ); + SetCursor(dc_arrow); + m_bEditingInPlace = false; + char buf[ 1024 ]; + GetText( buf, sizeof( buf ) ); + + // Not actually changed... + if ( !Q_strcmp( buf, m_OriginalText.Get() ) ) + return; + + if ( revert ) + { + SetText( m_OriginalText.Get() ); + GetParent()->InvalidateLayout(); + } + else + { + KeyValues *kv = new KeyValues( "LabelChanged", "original", m_OriginalText.Get(), "changed", buf ); + PostActionSignal( kv ); + } + } + + virtual void OnKillFocus() + { + BaseClass::OnKillFocus(); + + FinishEditingInPlace(); + } + + virtual void OnMouseWheeled(int delta) + { + if ( m_bEditingInPlace ) + { + BaseClass::OnMouseWheeled( delta ); + return; + } + + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); + } + // editable - cursor normal, and ability to edit text + + bool IsBeingEdited() const + { + return m_bEditingInPlace; + } + +private: + + bool m_bEditingInPlace; + CUtlString m_OriginalText; + bool m_bLabelEditingAllowed; + + bool m_bArmForEditing; + bool m_bWaitingForRelease; + long m_lArmingTime; + TreeView *m_pTree; +}; + +//----------------------------------------------------------------------------- +// Purpose: icon for the tree node (folder icon, file icon, etc.) +//----------------------------------------------------------------------------- +class TreeNodeImage : public ImagePanel +{ +public: + TreeNodeImage(Panel *parent, const char *name) : ImagePanel(parent, name) + { + SetBlockDragChaining( true ); + } + + //!! this could possibly be changed to just disallow mouse input on the image panel + virtual void OnMousePressed(MouseCode code) + { + // let parent deal with it + CallParentFunction(new KeyValues("MousePressed", "code", code)); + } + + virtual void OnMouseDoublePressed(MouseCode code) + { + // let parent deal with it + CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); + } + + virtual void OnMouseWheeled(int delta) + { + // let parent deal with it + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); + } + + virtual void OnCursorMoved( int x, int y ) + { + // let parent deal with it + CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: Scrollable area of the tree control, holds the tree itself only +//----------------------------------------------------------------------------- +class TreeViewSubPanel : public Panel +{ +public: + TreeViewSubPanel(Panel *parent) : Panel(parent) {} + + virtual void ApplySchemeSettings(IScheme *pScheme) + { + Panel::ApplySchemeSettings(pScheme); + + SetBorder(NULL); + } + + virtual void OnMouseWheeled(int delta) + { + // let parent deal with it + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); + } + virtual void OnMousePressed(MouseCode code) + { + // let parent deal with it + CallParentFunction(new KeyValues("MousePressed", "code", code)); + } + virtual void OnMouseDoublePressed(MouseCode code) + { + // let parent deal with it + CallParentFunction(new KeyValues("MouseDoublePressed", "code", code)); + } + + virtual void OnCursorMoved( int x, int y ) + { + // let parent deal with it + CallParentFunction(new KeyValues("OnCursorMoved", "x", x, "y", y)); + } +}; + +//----------------------------------------------------------------------------- +// Purpose: A single entry in the tree +//----------------------------------------------------------------------------- +class TreeNode : public Panel +{ + DECLARE_CLASS_SIMPLE( TreeNode, Panel ); + +public: + TreeNode(Panel *parent, TreeView *pTreeView); + ~TreeNode(); + void SetText(const char *pszText); + void SetFont(HFont font); + void SetKeyValues(KeyValues *data); + bool IsSelected(); + // currently unused, could be re-used if necessary +// bool IsInFocus(); + virtual void PaintBackground(); + virtual void PerformLayout(); + TreeNode *GetParentNode(); + int GetChildrenCount(); + void ClearChildren(); + int ComputeInsertionPosition( TreeNode *pChild ); + int FindChild( TreeNode *pChild ); + void AddChild(TreeNode *pChild); + void SetNodeExpanded(bool bExpanded); + bool IsExpanded(); + int CountVisibleNodes(); + void CalculateVisibleMaxWidth(); + void OnChildWidthChange(); + int GetMaxChildrenWidth(); + int GetVisibleMaxWidth(); + int GetDepth(); + bool HasParent(TreeNode *pTreeNode); + bool IsBeingDisplayed(); + virtual void SetVisible(bool state); + virtual void Paint(); + virtual void ApplySchemeSettings(IScheme *pScheme); + virtual void SetBgColor( Color color ); + virtual void SetFgColor( Color color ); + virtual void OnSetFocus(); + void SelectPrevChild(TreeNode *pCurrentChild); + void SelectNextChild(TreeNode *pCurrentChild); + + int GetPrevChildItemIndex( TreeNode *pCurrentChild ); + int GetNextChildItemIndex( TreeNode *pCurrentChild ); + + virtual void ClosePreviousParents( TreeNode *pPreviousParent ); + virtual void StepInto( bool bClosePrevious=true ); + virtual void StepOut( bool bClosePrevious=true ); + virtual void StepOver( bool bClosePrevious=true ); + virtual void OnKeyCodeTyped(KeyCode code); + virtual void OnMouseWheeled(int delta); + virtual void OnMousePressed( MouseCode code); + virtual void OnMouseReleased( MouseCode code); + virtual void OnCursorMoved( int x, int y ); + virtual bool IsDragEnabled() const; + void PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y); + + // counts items above this item including itself + int CountVisibleIndex(); + + virtual void OnCreateDragData( KeyValues *msg ); + // For handling multiple selections... + virtual void OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ); + virtual void OnMouseDoublePressed( MouseCode code ); + TreeNode *FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my ); + MESSAGE_FUNC_PARAMS( OnLabelChanged, "LabelChanged", data ); + void EditLabel(); + void SetLabelEditingAllowed( bool state ); + bool IsLabelEditingAllowed() const; + + virtual bool IsDroppable( CUtlVector< KeyValues * >& msglist ); + virtual void OnPanelDropped( CUtlVector< KeyValues * >& msglist ); + virtual HCursor GetDropCursor( CUtlVector< KeyValues * >& msglist ); + virtual bool GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ); + + void FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex ); + + void RemoveChildren(); + + void SetSelectionTextColor( const Color& clr ); + void SetSelectionBgColor( const Color& clr ); + void SetSelectionUnfocusedBgColor( const Color& clr ); +public: + int m_ItemIndex; + int m_ParentIndex; + KeyValues *m_pData; + CUtlVector m_Children; + bool m_bExpand; + +private: + + void FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex ); + + int m_iNodeWidth; + int m_iMaxVisibleWidth; + + TreeNodeText *m_pText; + TextImage *m_pExpandImage; + TreeNodeImage *m_pImagePanel; + + bool m_bExpandableWithoutChildren; + + TreeView *m_pTreeView; + int m_nClickedItem; + bool m_bClickedSelected; +}; + + +TreeNode::TreeNode(Panel *parent, TreeView *pTreeView) : + BaseClass(parent, "TreeNode" ), + m_nClickedItem( 0 ), + m_bClickedSelected( false ) +{ + m_pData = NULL; + m_pTreeView = pTreeView; + m_ItemIndex = -1; + m_iNodeWidth = 0; + m_iMaxVisibleWidth = 0; + + m_pExpandImage = new TextImage("+"); + m_pExpandImage->SetPos(3, 1); + + m_pImagePanel = new TreeNodeImage(this, "TreeImage"); + m_pImagePanel->SetPos(TREE_INDENT_AMOUNT, 3); + + m_pText = new TreeNodeText(this, "TreeNodeText",pTreeView); + m_pText->SetMultiline(false); + m_pText->SetEditable(false); + m_pText->SetPos(TREE_INDENT_AMOUNT*2, 0); + m_pText->AddActionSignalTarget( this ); + + m_bExpand = false; + m_bExpandableWithoutChildren = false; +} + +TreeNode::~TreeNode() +{ + delete m_pExpandImage; + if ( m_pData ) + { + m_pData->deleteThis(); + } +} + +void TreeNode::SetText(const char *pszText) +{ + m_pText->SetText(pszText); + InvalidateLayout(); +} + +void TreeNode::SetLabelEditingAllowed( bool state ) +{ + Assert( m_pTreeView->IsLabelEditingAllowed() ); + m_pText->SetLabelEditingAllowed( state ); +} + +bool TreeNode::IsLabelEditingAllowed() const +{ + return m_pText->IsLabelEditingAllowed(); +} + +bool TreeNode::GetDropContextMenu( Menu *menu, CUtlVector< KeyValues * >& msglist ) +{ + return m_pTreeView->GetItemDropContextMenu( m_ItemIndex, menu, msglist ); +} + +bool TreeNode::IsDroppable( CUtlVector< KeyValues * >& msglist ) +{ + return m_pTreeView->IsItemDroppable( m_ItemIndex, msglist ); +} + +void TreeNode::OnPanelDropped( CUtlVector< KeyValues * >& msglist ) +{ + m_pTreeView->OnItemDropped( m_ItemIndex, msglist ); +} + +HCursor TreeNode::GetDropCursor( CUtlVector< KeyValues * >& msglist ) +{ + return m_pTreeView->GetItemDropCursor( m_ItemIndex, msglist ); +} + + +void TreeNode::OnCreateDragData( KeyValues *msg ) +{ + // make sure the dragged item appears selected, + // on the off chance it appears deselected by a cntl mousedown + m_pTreeView->AddSelectedItem( m_ItemIndex, false ); + + m_pTreeView->GenerateDragDataForItem( m_ItemIndex, msg ); +} + +// For handling multiple selections... +void TreeNode::OnGetAdditionalDragPanels( CUtlVector< Panel * >& dragabbles ) +{ + CUtlVector< int > list; + m_pTreeView->GetSelectedItems( list ); + int c = list.Count(); + // walk this in reverse order so that panels are in order of selection + // even though GetSelectedItems returns items in reverse selection order + for ( int i = c - 1; i >= 0; --i ) + { + int itemIndex = list[ i ]; + // Skip self + if ( itemIndex == m_ItemIndex ) + continue; + + dragabbles.AddToTail( ( Panel * )m_pTreeView->GetItem( itemIndex ) ); + } +} + +void TreeNode::OnLabelChanged( KeyValues *data ) +{ + char const *oldString = data->GetString( "original" ); + char const *newString = data->GetString( "changed" ); + if ( m_pTreeView->IsLabelEditingAllowed() ) + { + m_pTreeView->OnLabelChanged( m_ItemIndex, oldString, newString ); + } +} + +void TreeNode::EditLabel() +{ + if ( m_pText->IsLabelEditingAllowed() && + !m_pText->IsBeingEdited() ) + { + m_pText->EnterEditingInPlace(); + } +} + +void TreeNode::SetFont(HFont font) +{ + Assert( font ); + if ( !font ) + return; + + m_pText->SetFont(font); + m_pExpandImage->SetFont(font); + InvalidateLayout(); + int i; + for (i=0;iSetFont(font); + } +} + +void TreeNode::SetKeyValues(KeyValues *data) +{ + if ( m_pData != data ) + { + if (m_pData) + { + m_pData->deleteThis(); + } + + m_pData = data->MakeCopy(); + } + + // set text + m_pText->SetText(data->GetString("Text", "")); + m_bExpandableWithoutChildren = data->GetInt("Expand"); + InvalidateLayout(); +} + +bool TreeNode::IsSelected() +{ + return m_pTreeView->IsItemSelected( m_ItemIndex ); +} + +void TreeNode::PaintBackground() +{ + if ( !m_pText->IsBeingEdited() ) + { + // setup panel drawing + if ( IsSelected() ) + { + m_pText->SelectAllText(false); + } + else + { + m_pText->SelectNoText(); + } + } + + BaseClass::PaintBackground(); +} + + +// currently unused, could be re-used if necessary +/* +bool TreeNode::IsInFocus() +{ + // check if our parent or one of it's children has focus + VPANEL focus = input()->GetFocus(); + return (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))); +} +*/ + +void TreeNode::PerformLayout() +{ + BaseClass::PerformLayout(); + + int width = 0; + if (m_pData->GetInt("SelectedImage", 0) == 0 && + m_pData->GetInt("Image", 0) == 0) + { + width = TREE_INDENT_AMOUNT; + } + else + { + width = TREE_INDENT_AMOUNT * 2; + } + + m_pText->SetPos(width, 0); + + int contentWide, contentTall; + m_pText->SetToFullWidth(); + m_pText->GetSize(contentWide, contentTall); + contentWide += 10; + m_pText->SetSize( contentWide, m_pTreeView->GetRowHeight() ); + width += contentWide; + SetSize(width, m_pTreeView->GetRowHeight()); + + m_iNodeWidth = width; + CalculateVisibleMaxWidth(); +} + +TreeNode *TreeNode::GetParentNode() +{ + if (m_pTreeView->m_NodeList.IsValidIndex(m_ParentIndex)) + { + return m_pTreeView->m_NodeList[m_ParentIndex]; + } + return NULL; +} + +int TreeNode::GetChildrenCount() +{ + return m_Children.Count(); +} + +int TreeNode::ComputeInsertionPosition( TreeNode *pChild ) +{ + if ( !m_pTreeView->m_pSortFunc ) + { + return GetChildrenCount() - 1; + } + + int start = 0, end = GetChildrenCount() - 1; + while (start <= end) + { + int mid = (start + end) >> 1; + if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) ) + { + start = mid + 1; + } + else if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[mid]->m_pData ) ) + { + end = mid - 1; + } + else + { + return mid; + } + } + return end; +} + +int TreeNode::FindChild( TreeNode *pChild ) +{ + if ( !m_pTreeView->m_pSortFunc ) + { + AssertMsg( 0, "This code has never been tested. Is it correct?" ); + for ( int i = 0; i < GetChildrenCount(); ++i ) + { + if ( m_Children[i] == pChild ) + return i; + } + return -1; + } + + // Find the first entry <= to the child + int start = 0, end = GetChildrenCount() - 1; + while (start <= end) + { + int mid = (start + end) >> 1; + + if ( m_Children[mid] == pChild ) + return mid; + + if ( m_pTreeView->m_pSortFunc( m_Children[mid]->m_pData, pChild->m_pData ) ) + { + start = mid + 1; + } + else + { + end = mid - 1; + } + } + + int nMax = GetChildrenCount(); + while( end < nMax ) + { + // Stop when we reach a child that has a different value + if ( m_pTreeView->m_pSortFunc( pChild->m_pData, m_Children[end]->m_pData ) ) + return -1; + + if ( m_Children[end] == pChild ) + return end; + + ++end; + } + + return -1; +} + +void TreeNode::AddChild(TreeNode *pChild) +{ + int i = ComputeInsertionPosition( pChild ); + m_Children.InsertAfter( i, pChild ); +} + +void TreeNode::SetNodeExpanded(bool bExpanded) +{ + m_bExpand = bExpanded; + + if (m_bExpand) + { + // see if we have any child nodes + if (GetChildrenCount() < 1) + { + // we need to get our children from the control + m_pTreeView->GenerateChildrenOfNode(m_ItemIndex); + + // if we still don't have any children, then hide the expand button + if (GetChildrenCount() < 1) + { + m_bExpand = false; + m_bExpandableWithoutChildren = false; + m_pTreeView->InvalidateLayout(); + return; + } + } + + m_pExpandImage->SetText("-"); + } + else + { + m_pExpandImage->SetText("+"); + + if ( m_bExpandableWithoutChildren && GetChildrenCount() > 0 ) + { + m_pTreeView->RemoveChildrenOfNode( m_ItemIndex ); + } + + // check if we've closed down on one of our children, if so, we get the focus + int selectedItem = m_pTreeView->GetFirstSelectedItem(); + if (selectedItem != -1 && m_pTreeView->m_NodeList[selectedItem]->HasParent(this)) + { + m_pTreeView->AddSelectedItem( m_ItemIndex, true ); + } + } + CalculateVisibleMaxWidth(); + m_pTreeView->InvalidateLayout(); +} + +bool TreeNode::IsExpanded() +{ + return m_bExpand; +} + +int TreeNode::CountVisibleNodes() +{ + int count = 1; // count myself + if (m_bExpand) + { + int i; + for (i=0;iCountVisibleNodes(); + } + } + return count; +} + +void TreeNode::CalculateVisibleMaxWidth() +{ + int width; + if (m_bExpand) + { + int childMaxWidth = GetMaxChildrenWidth(); + childMaxWidth += TREE_INDENT_AMOUNT; + + width = max(childMaxWidth, m_iNodeWidth); + } + else + { + width = m_iNodeWidth; + } + if (width != m_iMaxVisibleWidth) + { + m_iMaxVisibleWidth = width; + if (GetParentNode()) + { + GetParentNode()->OnChildWidthChange(); + } + else + { + m_pTreeView->InvalidateLayout(); + } + } +} + +void TreeNode::OnChildWidthChange() +{ + CalculateVisibleMaxWidth(); +} + +int TreeNode::GetMaxChildrenWidth() +{ + int maxWidth = 0; + int i; + for (i=0;iGetVisibleMaxWidth(); + if (childWidth > maxWidth) + { + maxWidth = childWidth; + } + } + return maxWidth; +} + +int TreeNode::GetVisibleMaxWidth() +{ + return m_iMaxVisibleWidth; +} + +int TreeNode::GetDepth() +{ + int depth = 0; + TreeNode *pParent = GetParentNode(); + while (pParent) + { + depth++; + pParent = pParent->GetParentNode(); + } + return depth; +} + +bool TreeNode::HasParent(TreeNode *pTreeNode) +{ + TreeNode *pParent = GetParentNode(); + while (pParent) + { + if (pParent == pTreeNode) + return true; + pParent = pParent->GetParentNode(); + } + return false; +} + +bool TreeNode::IsBeingDisplayed() +{ + TreeNode *pParent = GetParentNode(); + while (pParent) + { + // our parents aren't showing us + if (!pParent->m_bExpand) + return false; + + pParent = pParent->GetParentNode(); + } + return true; +} + +void TreeNode::SetVisible(bool state) +{ + BaseClass::SetVisible(state); + + bool bChildrenVisible = state && m_bExpand; + int i; + for (i=0;iSetVisible(bChildrenVisible); + } +} + +void TreeNode::Paint() +{ + if (GetChildrenCount() > 0 || m_bExpandableWithoutChildren) + { + m_pExpandImage->Paint(); + } + + // set image + int imageIndex = 0; + if (IsSelected()) + { + imageIndex = m_pData->GetInt("SelectedImage", 0); + } + else + { + imageIndex = m_pData->GetInt("Image", 0); + } + + if (imageIndex) + { + IImage *pImage = m_pTreeView->GetImage(imageIndex); + if (pImage) + { + m_pImagePanel->SetImage(pImage); + } + m_pImagePanel->Paint(); + } + + m_pText->Paint(); +} + +void TreeNode::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBorder( NULL ); + SetFgColor( m_pTreeView->GetFgColor() ); + SetBgColor( m_pTreeView->GetBgColor() ); + SetFont( m_pTreeView->GetFont() ); +} + +void TreeNode::SetSelectionTextColor( const Color& clr ) +{ + if ( m_pText ) + { + m_pText->SetSelectionTextColor( clr ); + } +} + +void TreeNode::SetSelectionBgColor( const Color& clr ) +{ + if ( m_pText ) + { + m_pText->SetSelectionBgColor( clr ); + } +} + +void TreeNode::SetSelectionUnfocusedBgColor( const Color& clr ) +{ + if ( m_pText ) + { + m_pText->SetSelectionUnfocusedBgColor( clr ); + } +} + +void TreeNode::SetBgColor( Color color ) +{ + BaseClass::SetBgColor( color ); + if ( m_pText ) + { + m_pText->SetBgColor( color ); + } + +} + +void TreeNode::SetFgColor( Color color ) +{ + BaseClass::SetFgColor( color ); + if ( m_pText ) + { + m_pText->SetFgColor( color ); + } +} + +void TreeNode::OnSetFocus() +{ + m_pText->RequestFocus(); +} + +int TreeNode::GetPrevChildItemIndex( TreeNode *pCurrentChild ) +{ + int i; + for (i=0;im_ItemIndex; + } + } + return -1; +} + +int TreeNode::GetNextChildItemIndex( TreeNode *pCurrentChild ) +{ + int i; + for (i=0;i= GetChildrenCount() - 1 ) + return -1; + + TreeNode *pChild = m_Children[i+1]; + return pChild->m_ItemIndex; + } + } + return -1; +} + +void TreeNode::SelectPrevChild(TreeNode *pCurrentChild) +{ + int i; + for (i=0;iAddSelectedItem( m_ItemIndex, true ); + } + else + { + // see if we need to find a grandchild of the previous sibling + TreeNode *pChild = m_Children[i-1]; + + // if this child is expanded with children, then we have to find the last child + while (pChild->m_bExpand && pChild->GetChildrenCount()>0) + { + // find the last child + pChild = pChild->m_Children[pChild->GetChildrenCount()-1]; + } + m_pTreeView->AddSelectedItem( pChild->m_ItemIndex, true ); + } +} + +void TreeNode::SelectNextChild(TreeNode *pCurrentChild) +{ + int i; + for (i=0;iSelectNextChild(this); + } + } + else + { + m_pTreeView->AddSelectedItem( m_Children[i+1]->m_ItemIndex, true ); + } +} + +void TreeNode::ClosePreviousParents( TreeNode *pPreviousParent ) +{ + // close up all the open nodes we've just stepped out of. + CUtlVector< int > selected; + m_pTreeView->GetSelectedItems( selected ); + if ( selected.Count() == 0 ) + { + Assert( 0 ); + return; + } + + // Most recently clicked item + TreeNode *selectedItem = m_pTreeView->GetItem( selected[ 0 ] ); + TreeNode *pNewParent = selectedItem->GetParentNode(); + if ( pPreviousParent && pNewParent ) + { + while ( pPreviousParent->m_ItemIndex > pNewParent->m_ItemIndex ) + { + pPreviousParent->SetNodeExpanded(false); + pPreviousParent = pPreviousParent->GetParentNode(); + } + } +} + +void TreeNode::StepInto( bool bClosePrevious ) +{ + if ( !m_bExpand ) + { + SetNodeExpanded(true); + } + + if ( ( GetChildrenCount() > 0 ) && m_bExpand ) + { + m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); + } + else if ( GetParentNode() ) + { + TreeNode *pParent = GetParentNode(); + pParent->SelectNextChild(this); + + if ( bClosePrevious ) + { + ClosePreviousParents( pParent ); + } + } +} + +void TreeNode::StepOut( bool bClosePrevious ) +{ + TreeNode *pParent = GetParentNode(); + if ( pParent ) + { + m_pTreeView->AddSelectedItem( pParent->m_ItemIndex, true ); + if ( pParent->GetParentNode() ) + { + pParent->GetParentNode()->SelectNextChild(pParent); + } + if ( bClosePrevious ) + { + ClosePreviousParents( pParent ); + } + else + { + pParent->SetNodeExpanded(true); + } + } +} + +void TreeNode::StepOver( bool bClosePrevious ) +{ + TreeNode *pParent = GetParentNode(); + if ( pParent ) + { + GetParentNode()->SelectNextChild(this); + if ( bClosePrevious ) + { + ClosePreviousParents( pParent ); + } + } +} + +void TreeNode::OnKeyCodeTyped(KeyCode code) +{ + switch (code) + { + case KEY_LEFT: + { + if (m_bExpand && GetChildrenCount() > 0) + { + SetNodeExpanded(false); + } + else + { + if (GetParentNode()) + { + m_pTreeView->AddSelectedItem( GetParentNode()->m_ItemIndex, true ); + } + } + break; + } + case KEY_RIGHT: + { + if (!m_bExpand) + { + SetNodeExpanded(true); + } + else if (GetChildrenCount() > 0) + { + m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); + } + break; + } + case KEY_UP: + { + if (GetParentNode()) + { + GetParentNode()->SelectPrevChild(this); + } + break; + } + case KEY_DOWN: + { + if (GetChildrenCount() > 0 && m_bExpand) + { + m_pTreeView->AddSelectedItem( m_Children[0]->m_ItemIndex, true ); + } + else if (GetParentNode()) + { + GetParentNode()->SelectNextChild(this); + } + break; + } + case KEY_SPACE: + { + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool alt = (input()->IsKeyDown(KEY_LALT) || input()->IsKeyDown(KEY_RALT)); + if ( shift ) + { + StepOut( !ctrl ); + } + else if ( alt ) + { + StepOver( !ctrl ); + } + else + { + StepInto( !ctrl ); + } + break; + } + case KEY_I: + { + StepInto(); + break; + } + case KEY_U: + { + StepOut(); + break; + } + case KEY_O: + { + StepOver(); + break; + } + case KEY_ESCAPE: + { + if ( m_pTreeView->GetSelectedItemCount() > 0 ) + { + m_pTreeView->ClearSelection(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } + break; + case KEY_A: + { + bool ctrldown = input()->IsKeyDown( KEY_LCONTROL ) || input()->IsKeyDown( KEY_RCONTROL ); + if ( ctrldown ) + { + m_pTreeView->SelectAll(); + } + else + { + BaseClass::OnKeyCodeTyped(code); + } + } + break; + default: + BaseClass::OnKeyCodeTyped(code); + return; + } +} + +void TreeNode::OnMouseWheeled(int delta) +{ + CallParentFunction(new KeyValues("MouseWheeled", "delta", delta)); +} + +void TreeNode::OnMouseDoublePressed( MouseCode code ) +{ + int x, y; + input()->GetCursorPos(x, y); + + if (code == MOUSE_LEFT) + { + ScreenToLocal(x, y); + if (x > TREE_INDENT_AMOUNT) + { + SetNodeExpanded(!m_bExpand); + } + } +} + +bool TreeNode::IsDragEnabled() const +{ + int x, y; + input()->GetCursorPos(x, y); + ((TreeNode *)this)->ScreenToLocal(x, y); + if ( x < TREE_INDENT_AMOUNT ) + return false; + + return BaseClass::IsDragEnabled(); +} + +void TreeNode::OnMouseReleased(MouseCode code) +{ + BaseClass::OnMouseReleased( code ); + + if ( input()->GetMouseCapture() == GetVPanel() ) + { + input()->SetMouseCapture( NULL ); + return; + } + int x, y; + input()->GetCursorPos(x, y); + ScreenToLocal(x, y); + + if ( x < TREE_INDENT_AMOUNT ) + return; + + bool ctrldown = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool shiftdown = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + + if ( !ctrldown && !shiftdown && ( code == MOUSE_LEFT ) ) + { + m_pTreeView->AddSelectedItem( m_ItemIndex, true ); + } +} + +void TreeNode::OnCursorMoved( int x, int y ) +{ + if ( input()->GetMouseCapture() != GetVPanel() ) + return; + + LocalToScreen( x, y ); + m_pTreeView->ScreenToLocal( x, y ); + int newItem = m_pTreeView->FindItemUnderMouse( x, y ); + if ( newItem == -1 ) + { + // Fixme: Figure out best item + return; + } + + int startItem = m_nClickedItem; + int endItem = newItem; + if ( startItem > endItem ) + { + int temp = startItem; + startItem = endItem; + endItem = temp; + } + + CUtlVector< TreeNode * > list; + m_pTreeView->m_pRootNode->FindNodesInRange( list, startItem, endItem ); + + int c = list.Count(); + for ( int i = 0; i < c; ++i ) + { + TreeNode *item = list[ i ]; + if ( m_bClickedSelected ) + { + m_pTreeView->AddSelectedItem( item->m_ItemIndex, false ); + } + else + { + m_pTreeView->RemoveSelectedItem( item->m_ItemIndex ); + } + } +} + +void TreeNode::OnMousePressed( MouseCode code) +{ + BaseClass::OnMousePressed( code ); + + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + int x, y; + input()->GetCursorPos(x, y); + + bool bExpandTree = m_pTreeView->m_bLeftClickExpandsTree; + + if ( code == MOUSE_LEFT ) + { + ScreenToLocal(x, y); + if ( x < TREE_INDENT_AMOUNT ) + { + if ( bExpandTree ) + { + SetNodeExpanded(!m_bExpand); + } + // m_pTreeView->SetSelectedItem(m_ItemIndex); // explorer doesn't actually select item when it expands an item + // purposely commented out in case we want to change the behavior + } + else + { + m_nClickedItem = m_ItemIndex; + if ( m_pTreeView->IsMultipleItemDragEnabled() ) + { + input()->SetMouseCapture( GetVPanel() ); + } + + if ( shift ) + { + m_pTreeView->RangeSelectItems( m_ItemIndex ); + } + else + { + if ( !IsSelected() || ctrl ) + { + if ( IsSelected() && ctrl ) + { + m_pTreeView->RemoveSelectedItem( m_ItemIndex ); + } + else + { + m_pTreeView->AddSelectedItem( m_ItemIndex, !ctrl ); + } + } + else if ( IsSelected() && m_pTreeView->IsMultipleItemDragEnabled() ) + { + m_pTreeView->AddSelectedItem( m_ItemIndex, !shift ); + } + } + + m_bClickedSelected = m_pTreeView->IsItemSelected( m_ItemIndex ); + } + } + else if (code == MOUSE_RIGHT) + { + // context menu selection + // If the item was selected, leave selected items alone, otherwise make it the only selected item + if ( !m_pTreeView->IsItemSelected( m_ItemIndex ) ) + { + m_pTreeView->AddSelectedItem( m_ItemIndex, true ); + } + + // ask parent to context menu + m_pTreeView->GenerateContextMenu(m_ItemIndex, x, y); + } +} + +void TreeNode::RemoveChildren() +{ + int c = m_Children.Count(); + for ( int i = c - 1 ; i >= 0 ; --i ) + { + m_pTreeView->RemoveItem( m_Children[ i ]->m_ItemIndex, false, true ); + } + m_Children.RemoveAll(); +} + +void TreeNode::FindNodesInRange( CUtlVector< TreeNode * >& list, int startIndex, int endIndex ) +{ + list.RemoveAll(); + bool finished = false; + bool foundstart = false; + FindNodesInRange_R( list, finished, foundstart, startIndex, endIndex ); +} + +void TreeNode::FindNodesInRange_R( CUtlVector< TreeNode * >& list, bool& finished, bool& foundStart, int startIndex, int endIndex ) +{ + if ( finished ) + return; + if ( foundStart == true ) + { + list.AddToTail( this ); + + if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex ) + { + finished = true; + return; + } + } + else if ( m_ItemIndex == startIndex || m_ItemIndex == endIndex ) + { + foundStart = true; + list.AddToTail( this ); + if ( startIndex == endIndex ) + { + finished = true; + return; + } + } + + if ( !m_bExpand ) + return; + + + int i; + int c = GetChildrenCount(); + for (i=0;iFindNodesInRange_R( list, finished, foundStart, startIndex, endIndex ); + } +} + +void TreeNode::PositionAndSetVisibleNodes(int &nStart, int &nCount, int x, int &y) +{ + // position ourselves + if (nStart == 0) + { + BaseClass::SetVisible(true); + SetPos(x, y); + y += m_pTreeView->GetRowHeight(); // m_nRowHeight + nCount--; + } + else // still looking for first element + { + nStart--; + BaseClass::SetVisible(false); + } + + x += TREE_INDENT_AMOUNT; + int i; + for (i=0;i 0 && m_bExpand) + { + m_Children[i]->PositionAndSetVisibleNodes(nStart, nCount, x, y); + } + else + { + m_Children[i]->SetVisible(false); // this will make all grand children hidden as well + } + } +} + +TreeNode *TreeNode::FindItemUnderMouse( int &nStart, int& nCount, int x, int &y, int mx, int my ) +{ + // position ourselves + if (nStart == 0) + { + int posx, posy; + GetPos(posx, posy); + if ( my >= posy && my < posy + m_pTreeView->GetRowHeight() ) + { + return this; + } + y += m_pTreeView->GetRowHeight(); + nCount--; + } + else // still looking for first element + { + nStart--; + } + + x += TREE_INDENT_AMOUNT; + int i; + for (i=0;i 0 && m_bExpand) + { + TreeNode *child = m_Children[i]->FindItemUnderMouse(nStart, nCount, x, y, mx, my); + if ( child != NULL ) + { + return child; + } + } + } + + return NULL; +} + +// counts items above this item including itself +int TreeNode::CountVisibleIndex() +{ + int nCount = 1; // myself + if (GetParentNode()) + { + int i; + for (i=0;iGetChildrenCount();i++) + { + if (GetParentNode()->m_Children[i] == this) + break; + + nCount += GetParentNode()->m_Children[i]->CountVisibleNodes(); + } + return nCount + GetParentNode()->CountVisibleIndex(); + } + else + return nCount; +} + + +}; // namespace vgui + +DECLARE_BUILD_FACTORY( TreeView ); + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +TreeView::TreeView(Panel *parent, const char *panelName) : Panel(parent, panelName) +{ + m_bScrollbarExternal[ 0 ] = m_bScrollbarExternal[ 1 ] = false; + m_nRowHeight = 20; + m_pRootNode = NULL; + m_pImageList = NULL; + m_pSortFunc = NULL; + m_Font = 0; + + m_pSubPanel = new TreeViewSubPanel(this); + m_pSubPanel->SetVisible(true); + m_pSubPanel->SetPos(0,0); + + m_pHorzScrollBar = new ScrollBar(this, "HorizScrollBar", false); + m_pHorzScrollBar->AddActionSignalTarget(this); + m_pHorzScrollBar->SetVisible(false); + + m_pVertScrollBar = new ScrollBar(this, "VertScrollBar", true); + m_pVertScrollBar->SetVisible(false); + m_pVertScrollBar->AddActionSignalTarget(this); + + m_bAllowLabelEditing = false; + m_bDragEnabledItems = false; + m_bDeleteImageListWhenDone = false; + m_bLabelBeingEdited = false; + m_bMultipleItemDragging = false; + m_bLeftClickExpandsTree = true; + m_bAllowMultipleSelections = false; + m_nMostRecentlySelectedItem = -1; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +TreeView::~TreeView() +{ + CleanUpImageList(); +} + + +//----------------------------------------------------------------------------- +// Clean up the image list +//----------------------------------------------------------------------------- +void TreeView::CleanUpImageList( ) +{ + if ( m_pImageList ) + { + if ( m_bDeleteImageListWhenDone ) + { + delete m_pImageList; + } + m_pImageList = NULL; + } +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::SetSortFunc(TreeViewSortFunc_t pSortFunc) +{ + m_pSortFunc = pSortFunc; +} + +HFont TreeView::GetFont() +{ + return m_Font; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::SetFont(HFont font) +{ + Assert( font ); + if ( !font ) + return; + + m_Font = font; + m_nRowHeight = surface()->GetFontTall(font) + 2; + + if (m_pRootNode) + { + m_pRootNode->SetFont(font); + } + InvalidateLayout(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetRowHeight() +{ + return m_nRowHeight; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetVisibleMaxWidth() +{ + if (m_pRootNode) + { + return m_pRootNode->GetVisibleMaxWidth(); + } + else + { + return 0; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::AddItem(KeyValues *data, int parentItemIndex) +{ + Assert(parentItemIndex == -1 || m_NodeList.IsValidIndex(parentItemIndex)); + + TreeNode *pTreeNode = new TreeNode(m_pSubPanel, this); + pTreeNode->SetDragEnabled( m_bDragEnabledItems ); + pTreeNode->m_ItemIndex = m_NodeList.AddToTail(pTreeNode); + pTreeNode->SetKeyValues(data); + + if ( m_Font != 0 ) + { + pTreeNode->SetFont( m_Font ); + } + pTreeNode->SetBgColor( GetBgColor() ); + + if ( data->GetInt( "droppable", 0 ) != 0 ) + { + float flContextDelay = data->GetFloat( "drophoverdelay" ); + if ( flContextDelay ) + { + pTreeNode->SetDropEnabled( true, flContextDelay ); + } + else + { + pTreeNode->SetDropEnabled( true ); + } + } + + // there can be only one root + if (parentItemIndex == -1) + { + Assert(m_pRootNode == NULL); + m_pRootNode = pTreeNode; + pTreeNode->m_ParentIndex = -1; + } + else + { + pTreeNode->m_ParentIndex = parentItemIndex; + + // add to parent list + pTreeNode->GetParentNode()->AddChild(pTreeNode); + } + + SETUP_PANEL( pTreeNode ); + + return pTreeNode->m_ItemIndex; +} + + +int TreeView::GetRootItemIndex() +{ + if ( m_pRootNode ) + return m_pRootNode->m_ItemIndex; + else + return -1; +} + + +int TreeView::GetNumChildren( int itemIndex ) +{ + if ( itemIndex == -1 ) + return 0; + + return m_NodeList[itemIndex]->m_Children.Count(); +} + + +int TreeView::GetChild( int iParentItemIndex, int iChild ) +{ + return m_NodeList[iParentItemIndex]->m_Children[iChild]->m_ItemIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : itemIndex - +// Output : TreeNode +//----------------------------------------------------------------------------- +TreeNode *TreeView::GetItem( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + { + Assert( 0 ); + return NULL; + } + + return m_NodeList[ itemIndex ]; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetItemCount(void) +{ + return m_NodeList.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +KeyValues* TreeView::GetItemData(int itemIndex) +{ + if (!m_NodeList.IsValidIndex(itemIndex)) + return NULL; + else + return m_NodeList[itemIndex]->m_pData; +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::RemoveItem(int itemIndex, bool bPromoteChildren, bool bFullDelete ) +{ + // HACK: there's a bug with RemoveItem where panels are lingering. This gets around it temporarily. + + // FIXME: Negative item indices is a bogus interface method! + // because what if you want to recursively remove everything under node 0? + // Use the bFullDelete parameter instead. + if ( itemIndex < 0 ) + { + itemIndex = -itemIndex; + bFullDelete = true; + } + + if (!m_NodeList.IsValidIndex(itemIndex)) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + TreeNode *pParent = pNode->GetParentNode(); + + // are we promoting the children + if (bPromoteChildren && pParent) + { + int i; + for (i=0;iGetChildrenCount();i++) + { + TreeNode *pChild = pNode->m_Children[i]; + pChild->m_ParentIndex = pParent->m_ItemIndex; + } + } + else + { + // delete our children + if ( bFullDelete ) + { + while ( pNode->GetChildrenCount() ) + RemoveItem( -pNode->m_Children[0]->m_ItemIndex, false ); + } + else + { + int i; + for (i=0;iGetChildrenCount();i++) + { + TreeNode *pDeleteChild = pNode->m_Children[i]; + RemoveItem(pDeleteChild->m_ItemIndex, false); + } + } + } + + // remove from our parent's children list + if (pParent) + { + pParent->m_Children.FindAndRemove(pNode); + } + + // finally get rid of ourselves from the main list + m_NodeList.Remove(itemIndex); + + if ( bFullDelete ) + delete pNode; + else + pNode->MarkForDeletion(); + + // Make sure we don't leave ourselves with an invalid selected item. + m_SelectedItems.FindAndRemove( pNode ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::RemoveAll() +{ + int i; + for (i=0;iMarkForDeletion(); + } + m_NodeList.RemoveAll(); + m_pRootNode = NULL; + ClearSelection(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool TreeView::ModifyItem(int itemIndex, KeyValues *data) +{ + if (!m_NodeList.IsValidIndex(itemIndex)) + return false; + + TreeNode *pNode = m_NodeList[itemIndex]; + TreeNode *pParent = pNode->GetParentNode(); + bool bReSort = ( m_pSortFunc && pParent ); + int nChildIndex = -1; + if ( bReSort ) + { + nChildIndex = pParent->FindChild( pNode ); + } + + pNode->SetKeyValues(data); + + // Changing the data can cause it to re-sort + if ( bReSort ) + { + int nChildren = pParent->GetChildrenCount(); + bool bLeftBad = (nChildIndex > 0) && m_pSortFunc( pNode->m_pData, pParent->m_Children[nChildIndex-1]->m_pData ); + bool bRightBad = (nChildIndex < nChildren - 1) && m_pSortFunc( pParent->m_Children[nChildIndex+1]->m_pData, pNode->m_pData ); + if ( bLeftBad || bRightBad ) + { + pParent->m_Children.Remove( nChildIndex ); + pParent->AddChild( pNode ); + } + } + + InvalidateLayout(); + + return true; +} + +//----------------------------------------------------------------------------- +// Purpose: set the selection colors of an element in the tree view +//----------------------------------------------------------------------------- + +void TreeView::SetItemSelectionTextColor( int itemIndex, const Color& clr ) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetSelectionTextColor( clr ); +} + +void TreeView::SetItemSelectionBgColor( int itemIndex, const Color& clr ) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetSelectionBgColor( clr ); +} + +void TreeView::SetItemSelectionUnfocusedBgColor( int itemIndex, const Color& clr ) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetSelectionUnfocusedBgColor( clr ); +} + +//----------------------------------------------------------------------------- +// Purpose: set the fg color of an element in the tree view +//----------------------------------------------------------------------------- +void TreeView::SetItemFgColor(int itemIndex, const Color& color) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetFgColor( color ); +} + +//----------------------------------------------------------------------------- +// Purpose: set the bg color of an element in the tree view +//----------------------------------------------------------------------------- +void TreeView::SetItemBgColor(int itemIndex, const Color& color) +{ + Assert( m_NodeList.IsValidIndex(itemIndex) ); + if ( !m_NodeList.IsValidIndex(itemIndex) ) + return; + + TreeNode *pNode = m_NodeList[itemIndex]; + pNode->SetBgColor( color ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetItemParent(int itemIndex) +{ + return m_NodeList[itemIndex]->m_ParentIndex; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::SetImageList(ImageList *imageList, bool deleteImageListWhenDone) +{ + CleanUpImageList(); + m_pImageList = imageList; + m_bDeleteImageListWhenDone = deleteImageListWhenDone; +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +IImage *TreeView::GetImage(int index) +{ + return m_pImageList->GetImage(index); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::GetSelectedItems( CUtlVector< int >& list ) +{ + list.RemoveAll(); + + int c = m_SelectedItems.Count(); + for ( int i = 0 ; i < c; ++i ) + { + list.AddToTail( m_SelectedItems[ i ]->m_ItemIndex ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::GetSelectedItemData( CUtlVector< KeyValues * >& list ) +{ + list.RemoveAll(); + + int c = m_SelectedItems.Count(); + for ( int i = 0 ; i < c; ++i ) + { + list.AddToTail( m_SelectedItems[ i ]->m_pData ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +bool TreeView::IsItemIDValid(int itemIndex) +{ + return m_NodeList.IsValidIndex(itemIndex); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +int TreeView::GetHighestItemID() +{ + return m_NodeList.MaxElementIndex(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::ExpandItem(int itemIndex, bool bExpand) +{ + if (!m_NodeList.IsValidIndex(itemIndex)) + return; + + m_NodeList[itemIndex]->SetNodeExpanded(bExpand); + InvalidateLayout(); +} + +bool TreeView::IsItemExpanded( int itemIndex ) +{ + if (!m_NodeList.IsValidIndex(itemIndex)) + return false; + + return m_NodeList[itemIndex]->IsExpanded(); +} + + +//----------------------------------------------------------------------------- +// Purpose: Scrolls the list according to the mouse wheel movement +//----------------------------------------------------------------------------- +void TreeView::OnMouseWheeled(int delta) +{ + if ( !m_pVertScrollBar->IsVisible() ) + { + return; + } + int val = m_pVertScrollBar->GetValue(); + val -= (delta * 3); + m_pVertScrollBar->SetValue(val); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::OnSizeChanged(int wide, int tall) +{ + BaseClass::OnSizeChanged(wide, tall); + InvalidateLayout(); + Repaint(); +} + +void TreeView::GetScrollBarSize( bool vertical, int& w, int& h ) +{ + int idx = vertical ? 0 : 1; + + if ( m_bScrollbarExternal[ idx ] ) + { + w = h = 0; + return; + } + + if ( vertical ) + { + m_pVertScrollBar->GetSize( w, h ); + } + else + { + m_pHorzScrollBar->GetSize( w, h ); + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::PerformLayout() +{ + int wide, tall; + GetSize( wide, tall ); + + if ( !m_pRootNode ) + { + m_pSubPanel->SetSize( wide, tall ); + return; + } + + int sbhw, sbhh; + GetScrollBarSize( false, sbhw, sbhh ); + int sbvw, sbvh; + GetScrollBarSize( true, sbvw, sbvh ); + + bool vbarNeeded = false; + bool hbarNeeded = false; + + // okay we have to check if we need either scroll bars, since if we need one + // it might make it necessary to have the other one + int nodesVisible = tall / m_nRowHeight; + + // count the number of visible items + int visibleItemCount = m_pRootNode->CountVisibleNodes(); + int maxWidth = m_pRootNode->GetVisibleMaxWidth() + 10; // 10 pixel buffer + + vbarNeeded = visibleItemCount > nodesVisible; + + if (!vbarNeeded) + { + if (maxWidth > wide) + { + hbarNeeded = true; + + // recalculate if vbar is needed now + // double check that we really don't need it + nodesVisible = (tall - sbhh) / m_nRowHeight; + vbarNeeded = visibleItemCount > nodesVisible; + } + } + else + { + // we've got the vertical bar here, so shrink the width + hbarNeeded = maxWidth > (wide - (sbvw+2)); + + if (hbarNeeded) + { + nodesVisible = (tall - sbhh) / m_nRowHeight; + } + } + + int subPanelWidth = wide; + int subPanelHeight = tall; + + int vbarPos = 0; + if (vbarNeeded) + { + subPanelWidth -= (sbvw + 2); + int barSize = tall; + if (hbarNeeded) + { + barSize -= sbhh; + } + + //!! need to make it recalculate scroll positions + m_pVertScrollBar->SetVisible(true); + m_pVertScrollBar->SetEnabled(false); + m_pVertScrollBar->SetRangeWindow( nodesVisible ); + m_pVertScrollBar->SetRange( 0, visibleItemCount); + m_pVertScrollBar->SetButtonPressedScrollValue( 1 ); + + if ( !m_bScrollbarExternal[ 0 ] ) + { + m_pVertScrollBar->SetPos(wide - (sbvw + WINDOW_BORDER_WIDTH), 0); + m_pVertScrollBar->SetSize(sbvw, barSize - 2); + } + + // need to figure out + vbarPos = m_pVertScrollBar->GetValue(); + } + else + { + m_pVertScrollBar->SetVisible(false); + m_pVertScrollBar->SetValue( 0 ); + } + + int hbarPos = 0; + if (hbarNeeded) + { + subPanelHeight -= (sbhh + 2); + int barSize = wide; + if (vbarNeeded) + { + barSize -= sbvw; + } + m_pHorzScrollBar->SetVisible(true); + m_pHorzScrollBar->SetEnabled(false); + m_pHorzScrollBar->SetRangeWindow( barSize ); + m_pHorzScrollBar->SetRange( 0, maxWidth); + m_pHorzScrollBar->SetButtonPressedScrollValue( 10 ); + + if ( !m_bScrollbarExternal[ 1 ] ) + { + m_pHorzScrollBar->SetPos(0, tall - (sbhh + WINDOW_BORDER_WIDTH)); + m_pHorzScrollBar->SetSize(barSize - 2, sbhh); + } + + hbarPos = m_pHorzScrollBar->GetValue(); + } + else + { + m_pHorzScrollBar->SetVisible(false); + m_pHorzScrollBar->SetValue( 0 ); + } + + m_pSubPanel->SetSize(subPanelWidth, subPanelHeight); + + int y = 0; + m_pRootNode->PositionAndSetVisibleNodes(vbarPos, visibleItemCount, -hbarPos, y); + + Repaint(); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::MakeItemVisible(int itemIndex) +{ + // first make sure that all parents are expanded + TreeNode *pNode = m_NodeList[itemIndex]; + TreeNode *pParent = pNode->GetParentNode(); + while (pParent) + { + if (!pParent->m_bExpand) + { + pParent->SetNodeExpanded(true); + } + pParent = pParent->GetParentNode(); + } + + // recalculate scroll bar due to possible exapnsion + PerformLayout(); + + if (!m_pVertScrollBar->IsVisible()) + return; + + int visibleIndex = pNode->CountVisibleIndex()-1; + int range = m_pVertScrollBar->GetRangeWindow(); + int vbarPos = m_pVertScrollBar->GetValue(); + + // do we need to scroll up or down? + if (visibleIndex < vbarPos) + { + m_pVertScrollBar->SetValue(visibleIndex); + } + else if (visibleIndex+1 > vbarPos+range) + { + m_pVertScrollBar->SetValue(visibleIndex+1-range); + } + InvalidateLayout(); +} + +void TreeView::GetVBarInfo( int &top, int &nItemsVisible, bool& hbarVisible ) +{ + int wide, tall; + GetSize( wide, tall ); + nItemsVisible = tall / m_nRowHeight; + + if ( m_pVertScrollBar->IsVisible() ) + { + top = m_pVertScrollBar->GetValue(); + } + else + { + top = 0; + } + hbarVisible = m_pHorzScrollBar->IsVisible(); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::ApplySchemeSettings(IScheme *pScheme) +{ + BaseClass::ApplySchemeSettings(pScheme); + + SetBorder(pScheme->GetBorder("ButtonDepressedBorder")); + SetBgColor(GetSchemeColor("TreeView.BgColor", GetSchemeColor("WindowDisabledBgColor", pScheme), pScheme)); + SetFont( pScheme->GetFont( "Default", IsProportional() ) ); + m_pSubPanel->SetBgColor( GetBgColor() ); +} + + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::SetBgColor( Color color ) +{ + BaseClass::SetBgColor( color ); + m_pSubPanel->SetBgColor( color ); +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::OnSliderMoved( int position ) +{ + InvalidateLayout(); + Repaint(); +} + +void TreeView::GenerateDragDataForItem( int itemIndex, KeyValues *msg ) +{ + // Implemented by subclassed TreeView +} + +void TreeView::SetDragEnabledItems( bool state ) +{ + m_bDragEnabledItems = state; +} + +void TreeView::OnLabelChanged( int itemIndex, char const *oldString, char const *newString ) +{ +} + +bool TreeView::IsLabelEditingAllowed() const +{ + return m_bAllowLabelEditing; +} + +void TreeView::SetLabelBeingEdited( bool state ) +{ + m_bLabelBeingEdited = state; +} + +bool TreeView::IsLabelBeingEdited() const +{ + return m_bLabelBeingEdited; +} + +void TreeView::SetAllowLabelEditing( bool state ) +{ + m_bAllowLabelEditing = state; +} + +void TreeView::EnableExpandTreeOnLeftClick( bool bEnable ) +{ + m_bLeftClickExpandsTree = bEnable; +} + +int TreeView::FindItemUnderMouse( int mx, int my ) +{ + mx = clamp( mx, 0, GetWide() - 1 ); + my = clamp( my, 0, GetTall() - 1 ); + if ( mx >= TREE_INDENT_AMOUNT ) + { + // Find what's under this position + // need to figure out + int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0; + int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0; + int count = m_pRootNode->CountVisibleNodes(); + + int y = 0; + TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my ); + if ( item ) + { + return item->m_ItemIndex; + } + } + + return -1; +} + +void TreeView::OnMousePressed( MouseCode code ) +{ + bool ctrl = (input()->IsKeyDown(KEY_LCONTROL) || input()->IsKeyDown(KEY_RCONTROL)); + bool shift = (input()->IsKeyDown(KEY_LSHIFT) || input()->IsKeyDown(KEY_RSHIFT)); + + // Try to map mouse position to a row + if ( code == MOUSE_LEFT && m_pRootNode ) + { + int mx, my; + input()->GetCursorPos( mx, my ); + ScreenToLocal( mx, my ); + if ( mx >= TREE_INDENT_AMOUNT ) + { + // Find what's under this position + // need to figure out + int vbarPos = m_pVertScrollBar->IsVisible() ? m_pVertScrollBar->GetValue() : 0; + int hbarPos = m_pHorzScrollBar->IsVisible() ? m_pHorzScrollBar->GetValue() : 0; + int count = m_pRootNode->CountVisibleNodes(); + + int y = 0; + TreeNode *item = m_pRootNode->FindItemUnderMouse( vbarPos, count, -hbarPos, y, mx, my ); + if ( item ) + { + if ( !item->IsSelected() ) + { + AddSelectedItem( item->m_ItemIndex, !ctrl && !shift ); + } + return; + } + else + { + ClearSelection(); + } + } + } + + BaseClass::OnMousePressed( code ); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : state - +//----------------------------------------------------------------------------- +void TreeView::SetAllowMultipleSelections( bool state ) +{ + m_bAllowMultipleSelections = state; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool TreeView::IsMultipleSelectionAllowed() const +{ + return m_bAllowMultipleSelections; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : int +//----------------------------------------------------------------------------- +int TreeView::GetSelectedItemCount() const +{ + return m_SelectedItems.Count(); +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +//----------------------------------------------------------------------------- +void TreeView::ClearSelection() +{ + m_SelectedItems.RemoveAll(); + m_nMostRecentlySelectedItem = -1; + PostActionSignal( new KeyValues( "TreeViewItemSelectionCleared" ) ); +} + +void TreeView::RangeSelectItems( int endItem ) +{ + int startItem = m_nMostRecentlySelectedItem; + ClearSelection(); + m_nMostRecentlySelectedItem = startItem; + + if ( !m_NodeList.IsValidIndex( startItem ) ) + { + AddSelectedItem( endItem, false ); + return; + } + + Assert( m_NodeList.IsValidIndex( endItem ) ); + + if ( !m_pRootNode ) + { + return; + } + + CUtlVector< TreeNode * > list; + m_pRootNode->FindNodesInRange( list, startItem, endItem ); + + int c = list.Count(); + for ( int i = 0; i < c; ++i ) + { + TreeNode *item = list[ i ]; + AddSelectedItem( item->m_ItemIndex, false ); + } +} + +void TreeView::FindNodesInRange( int startItem, int endItem, CUtlVector< int >& itemIndices ) +{ + CUtlVector< TreeNode * > nodes; + m_pRootNode->FindNodesInRange( nodes, startItem, endItem ); + + int c = nodes.Count(); + for ( int i = 0; i < c; ++i ) + { + TreeNode *item = nodes[ i ]; + itemIndices.AddToTail( item->m_ItemIndex ); + } +} + +void TreeView::RemoveSelectedItem( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + TreeNode *sel = m_NodeList[ itemIndex ]; + Assert( sel ); + int slot = m_SelectedItems.Find( sel ); + if ( slot != m_SelectedItems.InvalidIndex() ) + { + m_SelectedItems.Remove( slot ); + PostActionSignal( new KeyValues( "TreeViewItemDeselected", "itemIndex", itemIndex ) ); + + m_nMostRecentlySelectedItem = itemIndex; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +//----------------------------------------------------------------------------- +void TreeView::AddSelectedItem( int itemIndex, bool clearCurrentSelection, bool requestFocus /* = true */, bool bMakeItemVisible /*= true*/ ) +{ + if ( clearCurrentSelection ) + { + ClearSelection(); + } + + // Assume it's bogus + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + TreeNode *sel = m_NodeList[ itemIndex ]; + Assert( sel ); + if ( requestFocus ) + { + sel->RequestFocus(); + } + + // Item 0 is most recently selected!!! + int slot = m_SelectedItems.Find( sel ); + if ( slot == m_SelectedItems.InvalidIndex() ) + { + m_SelectedItems.AddToHead( sel ); + } + else if ( slot != 0 ) + { + m_SelectedItems.Remove( slot ); + m_SelectedItems.AddToHead( sel ); + } + + if ( bMakeItemVisible ) + { + MakeItemVisible( itemIndex ); + } + + PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", itemIndex ) ); + InvalidateLayout(); + + if ( clearCurrentSelection ) + { + m_nMostRecentlySelectedItem = itemIndex; + } +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : - +// Output : int +//----------------------------------------------------------------------------- +int TreeView::GetFirstSelectedItem() const +{ + if ( m_SelectedItems.Count() <= 0 ) + return -1; + return m_SelectedItems[ 0 ]->m_ItemIndex; +} + +//----------------------------------------------------------------------------- +// Purpose: +// Input : itemIndex - +// Output : Returns true on success, false on failure. +//----------------------------------------------------------------------------- +bool TreeView::IsItemSelected( int itemIndex ) +{ + // Assume it's bogus + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return false; + + TreeNode *sel = m_NodeList[ itemIndex ]; + return m_SelectedItems.Find( sel ) != m_SelectedItems.InvalidIndex(); +} + +void TreeView::SetLabelEditingAllowed( int itemIndex, bool state ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + TreeNode *sel = m_NodeList[ itemIndex ]; + sel->SetLabelEditingAllowed( state ); +} + +void TreeView::StartEditingLabel( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + Assert( IsLabelEditingAllowed() ); + + TreeNode *sel = m_NodeList[ itemIndex ]; + Assert( sel->IsLabelEditingAllowed() ); + if ( !sel->IsLabelEditingAllowed() ) + return; + + sel->EditLabel(); +} + +int TreeView::GetPrevChildItemIndex( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return -1; + TreeNode *sel = m_NodeList[ itemIndex ]; + TreeNode *parent = sel->GetParentNode(); + if ( !parent ) + return -1; + + return parent->GetPrevChildItemIndex( sel ); +} + +int TreeView::GetNextChildItemIndex( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return -1; + TreeNode *sel = m_NodeList[ itemIndex ]; + TreeNode *parent = sel->GetParentNode(); + if ( !parent ) + return -1; + + return parent->GetNextChildItemIndex( sel ); +} + +bool TreeView::IsItemDroppable( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + // Derived classes should implement + return false; +} + +void TreeView::OnItemDropped( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ +} + +bool TreeView::GetItemDropContextMenu( int itemIndex, Menu *menu, CUtlVector< KeyValues * >& msglist ) +{ + return false; +} + +HCursor TreeView::GetItemDropCursor( int itemIndex, CUtlVector< KeyValues * >& msglist ) +{ + return dc_arrow; +} + +void TreeView::RemoveChildrenOfNode( int itemIndex ) +{ + if ( !m_NodeList.IsValidIndex( itemIndex ) ) + return; + + TreeNode *node = m_NodeList[ itemIndex ]; + node->RemoveChildren(); +} + +ScrollBar *TreeView::SetScrollBarExternal( bool vertical, Panel *newParent ) +{ + if ( vertical ) + { + m_bScrollbarExternal[ 0 ] = true; + m_pVertScrollBar->SetParent( newParent ); + return m_pVertScrollBar; + } + m_bScrollbarExternal[ 1 ] = true; + m_pHorzScrollBar->SetParent( newParent ); + return m_pHorzScrollBar; +} + +// if this is set, then clicking on one row and dragging will select a run or items, etc. +void TreeView::SetMultipleItemDragEnabled( bool state ) +{ + m_bMultipleItemDragging = state; +} + +bool TreeView::IsMultipleItemDragEnabled() const +{ + return m_bMultipleItemDragging; +} + +void TreeView::SelectAll() +{ + m_SelectedItems.RemoveAll(); + FOR_EACH_LL( m_NodeList, i ) + { + m_SelectedItems.AddToTail( m_NodeList[ i ] ); + } + + PostActionSignal( new KeyValues( "TreeViewItemSelected", "itemIndex", GetRootItemIndex() ) ); + InvalidateLayout(); +} -- cgit v1.2.3