aboutsummaryrefslogtreecommitdiff
path: root/mp/src/vgui2/vgui_controls/TreeView.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'mp/src/vgui2/vgui_controls/TreeView.cpp')
-rw-r--r--mp/src/vgui2/vgui_controls/TreeView.cpp5708
1 files changed, 2854 insertions, 2854 deletions
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 <assert.h>
-
-#define PROTECTED_THINGS_DISABLE
-
-#include <vgui/Cursor.h>
-#include <vgui/IScheme.h>
-#include <vgui/IInput.h>
-#include <vgui/IPanel.h>
-#include <vgui/ISurface.h>
-#include <vgui/ISystem.h>
-#include <vgui/IVGui.h>
-#include <vgui/KeyCode.h>
-#include <KeyValues.h>
-#include <vgui/MouseCode.h>
-
-#include <vgui_controls/TreeView.h>
-#include <vgui_controls/ScrollBar.h>
-#include <vgui_controls/TextEntry.h>
-#include <vgui_controls/Label.h>
-#include <vgui_controls/Button.h>
-#include <vgui_controls/TextImage.h>
-#include <vgui_controls/ImageList.h>
-#include <vgui_controls/ImagePanel.h>
-
-#include "tier1/utlstring.h"
-
-// memdbgon must be the last include file in a .cpp file!!!
-#include <tier0/memdbgon.h>
-
-#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<TreeNode *> 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;i<GetChildrenCount();i++)
- {
- m_Children[i]->SetFont(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;i<m_Children.Count();i++)
- {
- count += m_Children[i]->CountVisibleNodes();
- }
- }
- 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;i<GetChildrenCount();i++)
- {
- int childWidth = m_Children[i]->GetVisibleMaxWidth();
- 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;i<GetChildrenCount();i++)
- {
- m_Children[i]->SetVisible(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;i<GetChildrenCount();i++)
- {
- if ( m_Children[i] == pCurrentChild )
- {
- if ( i <= 0 )
- return -1;
-
- TreeNode *pChild = m_Children[i-1];
- return pChild->m_ItemIndex;
- }
- }
- return -1;
-}
-
-int TreeNode::GetNextChildItemIndex( TreeNode *pCurrentChild )
-{
- int i;
- for (i=0;i<GetChildrenCount();i++)
- {
- if ( m_Children[i] == pCurrentChild )
- {
- if ( 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;i<GetChildrenCount();i++)
- {
- if (m_Children[i] == pCurrentChild)
- break;
- }
-
- // this shouldn't happen
- if (i == GetChildrenCount())
- {
- Assert(0);
- return;
- }
-
- // were we on the first child?
- if (i == 0)
- {
- // if so, then we take over!
- m_pTreeView->AddSelectedItem( 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;i<GetChildrenCount();i++)
- {
- if (m_Children[i] == pCurrentChild)
- break;
- }
-
- // this shouldn't happen
- if (i == GetChildrenCount())
- {
- Assert(0);
- return;
- }
-
- // were we on the last child?
- if (i == GetChildrenCount() - 1)
- {
- // tell our parent to get the next child
- if (GetParentNode())
- {
- GetParentNode()->SelectNextChild(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;i<c;i++)
- {
- m_Children[i]->FindNodesInRange_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<GetChildrenCount();i++)
- {
- if (nCount > 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<GetChildrenCount();i++)
- {
- if (nCount > 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;i<GetParentNode()->GetChildrenCount();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;i<pNode->GetChildrenCount();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;i<pNode->GetChildrenCount();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;i<m_NodeList.MaxElementIndex();i++)
- {
- if (!m_NodeList.IsValidIndex(i))
- continue;
-
- m_NodeList[i]->MarkForDeletion();
- }
- 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 <assert.h>
+
+#define PROTECTED_THINGS_DISABLE
+
+#include <vgui/Cursor.h>
+#include <vgui/IScheme.h>
+#include <vgui/IInput.h>
+#include <vgui/IPanel.h>
+#include <vgui/ISurface.h>
+#include <vgui/ISystem.h>
+#include <vgui/IVGui.h>
+#include <vgui/KeyCode.h>
+#include <KeyValues.h>
+#include <vgui/MouseCode.h>
+
+#include <vgui_controls/TreeView.h>
+#include <vgui_controls/ScrollBar.h>
+#include <vgui_controls/TextEntry.h>
+#include <vgui_controls/Label.h>
+#include <vgui_controls/Button.h>
+#include <vgui_controls/TextImage.h>
+#include <vgui_controls/ImageList.h>
+#include <vgui_controls/ImagePanel.h>
+
+#include "tier1/utlstring.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include <tier0/memdbgon.h>
+
+#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<TreeNode *> 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;i<GetChildrenCount();i++)
+ {
+ m_Children[i]->SetFont(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;i<m_Children.Count();i++)
+ {
+ count += m_Children[i]->CountVisibleNodes();
+ }
+ }
+ 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;i<GetChildrenCount();i++)
+ {
+ int childWidth = m_Children[i]->GetVisibleMaxWidth();
+ 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;i<GetChildrenCount();i++)
+ {
+ m_Children[i]->SetVisible(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;i<GetChildrenCount();i++)
+ {
+ if ( m_Children[i] == pCurrentChild )
+ {
+ if ( i <= 0 )
+ return -1;
+
+ TreeNode *pChild = m_Children[i-1];
+ return pChild->m_ItemIndex;
+ }
+ }
+ return -1;
+}
+
+int TreeNode::GetNextChildItemIndex( TreeNode *pCurrentChild )
+{
+ int i;
+ for (i=0;i<GetChildrenCount();i++)
+ {
+ if ( m_Children[i] == pCurrentChild )
+ {
+ if ( 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;i<GetChildrenCount();i++)
+ {
+ if (m_Children[i] == pCurrentChild)
+ break;
+ }
+
+ // this shouldn't happen
+ if (i == GetChildrenCount())
+ {
+ Assert(0);
+ return;
+ }
+
+ // were we on the first child?
+ if (i == 0)
+ {
+ // if so, then we take over!
+ m_pTreeView->AddSelectedItem( 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;i<GetChildrenCount();i++)
+ {
+ if (m_Children[i] == pCurrentChild)
+ break;
+ }
+
+ // this shouldn't happen
+ if (i == GetChildrenCount())
+ {
+ Assert(0);
+ return;
+ }
+
+ // were we on the last child?
+ if (i == GetChildrenCount() - 1)
+ {
+ // tell our parent to get the next child
+ if (GetParentNode())
+ {
+ GetParentNode()->SelectNextChild(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;i<c;i++)
+ {
+ m_Children[i]->FindNodesInRange_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<GetChildrenCount();i++)
+ {
+ if (nCount > 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<GetChildrenCount();i++)
+ {
+ if (nCount > 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;i<GetParentNode()->GetChildrenCount();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;i<pNode->GetChildrenCount();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;i<pNode->GetChildrenCount();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;i<m_NodeList.MaxElementIndex();i++)
+ {
+ if (!m_NodeList.IsValidIndex(i))
+ continue;
+
+ m_NodeList[i]->MarkForDeletion();
+ }
+ 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();
+}