diff options
Diffstat (limited to 'mp/src/vgui2/vgui_controls/TreeView.cpp')
| -rw-r--r-- | mp/src/vgui2/vgui_controls/TreeView.cpp | 5708 |
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(); +} |