|
|
//========= 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(); }
|