//========= Copyright © 1996-2005, 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 )
	{
		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();
}
