//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
# include <stdio.h>
# define PROTECTED_THINGS_DISABLE
# include <vgui/Cursor.h>
# include <vgui/IInput.h>
# include <vgui/ILocalize.h>
# include <vgui/IPanel.h>
# include <vgui/IScheme.h>
# include <vgui/ISystem.h>
# include <vgui/ISurface.h>
# include <vgui/IVGui.h>
# include <vgui/KeyCode.h>
# include <KeyValues.h>
# include <vgui/MouseCode.h>
# include <vgui_controls/Button.h>
# include <vgui_controls/Controls.h>
# include <vgui_controls/ImageList.h>
# include <vgui_controls/ImagePanel.h>
# include <vgui_controls/Label.h>
# include <vgui_controls/ListPanel.h>
# include <vgui_controls/ScrollBar.h>
# include <vgui_controls/TextImage.h>
# include <vgui_controls/Menu.h>
# include <vgui_controls/Tooltip.h>
// memdbgon must be the last include file in a .cpp file
# include "tier0/memdbgon.h"
using namespace vgui ;
enum
{
WINDOW_BORDER_WIDTH = 2 // the width of the window's border
} ;
# ifndef max
# define max(a,b) (((a) > (b)) ? (a) : (b))
# endif
# ifndef min
# define min(a,b) (((a) < (b)) ? (a) : (b))
# endif
# ifndef clamp
# define clamp( val, min, max ) ( ((val) > (max)) ? (max) : ( ((val) < (min)) ? (min) : (val) ) )
# endif
//-----------------------------------------------------------------------------
//
// Button at the top of columns used to re-sort
//
//-----------------------------------------------------------------------------
class ColumnButton : public Button
{
public :
ColumnButton ( vgui : : Panel * parent , const char * name , const char * text ) ;
// Inherited from Button
virtual void ApplySchemeSettings ( IScheme * pScheme ) ;
virtual void OnMousePressed ( MouseCode code ) ;
void OpenColumnChoiceMenu ( ) ;
} ;
ColumnButton : : ColumnButton ( vgui : : Panel * parent , const char * name , const char * text ) : Button ( parent , name , text )
{
SetBlockDragChaining ( true ) ;
}
void ColumnButton : : ApplySchemeSettings ( IScheme * pScheme )
{
Button : : ApplySchemeSettings ( pScheme ) ;
SetContentAlignment ( Label : : a_west ) ;
SetFont ( pScheme - > GetFont ( " DefaultSmall " , IsProportional ( ) ) ) ;
}
// Don't request focus.
// This will keep items in the listpanel selected.
void ColumnButton : : OnMousePressed ( MouseCode code )
{
if ( ! IsEnabled ( ) )
return ;
if ( code = = MOUSE_RIGHT )
{
OpenColumnChoiceMenu ( ) ;
return ;
}
if ( ! IsMouseClickEnabled ( code ) )
return ;
if ( IsUseCaptureMouseEnabled ( ) )
{
{
SetSelected ( true ) ;
Repaint ( ) ;
}
// lock mouse input to going to this button
input ( ) - > SetMouseCapture ( GetVPanel ( ) ) ;
}
}
void ColumnButton : : OpenColumnChoiceMenu ( )
{
CallParentFunction ( new KeyValues ( " OpenColumnChoiceMenu " ) ) ;
}
//-----------------------------------------------------------------------------
//
// Purpose: Handles resizing of columns
//
//-----------------------------------------------------------------------------
class Dragger : public Panel
{
public :
Dragger ( int column ) ;
// Inherited from Panel
virtual void OnMousePressed ( MouseCode code ) ;
virtual void OnMouseDoublePressed ( MouseCode code ) ;
virtual void OnMouseReleased ( MouseCode code ) ;
virtual void OnCursorMoved ( int x , int y ) ;
virtual void SetMovable ( bool state ) ;
private :
int m_iDragger ;
bool m_bDragging ;
int m_iDragPos ;
bool m_bMovable ; // whether this dragger is movable using mouse or not
} ;
Dragger : : Dragger ( int column )
{
m_iDragger = column ;
SetPaintBackgroundEnabled ( false ) ;
SetPaintEnabled ( false ) ;
SetPaintBorderEnabled ( false ) ;
SetCursor ( dc_sizewe ) ;
m_bDragging = false ;
m_bMovable = true ; // movable by default
m_iDragPos = 0 ;
SetBlockDragChaining ( true ) ;
}
void Dragger : : OnMousePressed ( MouseCode code )
{
if ( m_bMovable )
{
input ( ) - > SetMouseCapture ( GetVPanel ( ) ) ;
int x , y ;
input ( ) - > GetCursorPos ( x , y ) ;
m_iDragPos = x ;
m_bDragging = true ;
}
}
void Dragger : : OnMouseDoublePressed ( MouseCode code )
{
if ( m_bMovable )
{
// resize the column to the size of it's contents
PostMessage ( GetParent ( ) , new KeyValues ( " ResizeColumnToContents " , " column " , m_iDragger ) ) ;
}
}
void Dragger : : OnMouseReleased ( MouseCode code )
{
if ( m_bMovable )
{
input ( ) - > SetMouseCapture ( NULL ) ;
m_bDragging = false ;
}
}
void Dragger : : OnCursorMoved ( int x , int y )
{
if ( m_bDragging )
{
input ( ) - > GetCursorPos ( x , y ) ;
KeyValues * msg = new KeyValues ( " ColumnResized " ) ;
msg - > SetInt ( " column " , m_iDragger ) ;
msg - > SetInt ( " delta " , x - m_iDragPos ) ;
m_iDragPos = x ;
if ( GetVParent ( ) )
{
ivgui ( ) - > PostMessage ( GetVParent ( ) , msg , GetVPanel ( ) ) ;
}
}
}
void Dragger : : SetMovable ( bool state )
{
m_bMovable = state ;
// disable cursor change if the dragger is not movable
if ( IsVisible ( ) )
{
if ( state )
{
// if its not movable we stick with the default arrow
// if parent windows Start getting fancy cursors we should probably retrive a parent
// cursor and set it to that
SetCursor ( dc_sizewe ) ;
}
else
{
SetCursor ( dc_arrow ) ;
}
}
}
namespace vgui
{
// optimized for sorting
class FastSortListPanelItem : public ListPanelItem
{
public :
// index into accessing item to sort
CUtlVector < int > m_SortedTreeIndexes ;
// visibility flag (for quick hide/filter)
bool visible ;
// precalculated sort orders
int primarySortIndexValue ;
int secondarySortIndexValue ;
} ;
}
static ListPanel * s_pCurrentSortingListPanel = NULL ;
static const char * s_pCurrentSortingColumn = NULL ;
static bool s_currentSortingColumnTypeIsText = false ;
static SortFunc * s_pSortFunc = NULL ;
static bool s_bSortAscending = true ;
static SortFunc * s_pSortFuncSecondary = NULL ;
static bool s_bSortAscendingSecondary = true ;
//-----------------------------------------------------------------------------
// Purpose: Basic sort function, for use in qsort
//-----------------------------------------------------------------------------
static int __cdecl AscendingSortFunc ( const void * elem1 , const void * elem2 )
{
int itemID1 = * ( ( int * ) elem1 ) ;
int itemID2 = * ( ( int * ) elem2 ) ;
// convert the item index into the ListPanelItem pointers
vgui : : ListPanelItem * p1 , * p2 ;
p1 = s_pCurrentSortingListPanel - > GetItemData ( itemID1 ) ;
p2 = s_pCurrentSortingListPanel - > GetItemData ( itemID2 ) ;
int result = s_pSortFunc ( s_pCurrentSortingListPanel , * p1 , * p2 ) ;
if ( result = = 0 )
{
// use the secondary sort functino
result = s_pSortFuncSecondary ( s_pCurrentSortingListPanel , * p1 , * p2 ) ;
if ( ! s_bSortAscendingSecondary )
{
result = - result ;
}
if ( result = = 0 )
{
// sort by the pointers to make sure we get consistent results
if ( p1 > p2 )
{
result = 1 ;
}
else
{
result = - 1 ;
}
}
}
else
{
// flip result if not doing an ascending sort
if ( ! s_bSortAscending )
{
result = - result ;
}
}
return result ;
}
//-----------------------------------------------------------------------------
// Purpose: Default column sorting function, puts things in alpabetical order
// If images are the same returns 1, else 0
//-----------------------------------------------------------------------------
static int __cdecl DefaultSortFunc (
ListPanel * pPanel ,
const ListPanelItem & item1 ,
const ListPanelItem & item2 )
{
const vgui : : ListPanelItem * p1 = & item1 ;
const vgui : : ListPanelItem * p2 = & item2 ;
if ( ! p1 | | ! p2 ) // No meaningful comparison
{
return 0 ;
}
const char * col = s_pCurrentSortingColumn ;
if ( s_currentSortingColumnTypeIsText ) // textImage column
{
if ( p1 - > kv - > FindKey ( col , true ) - > GetDataType ( ) = = KeyValues : : TYPE_INT )
{
// compare ints
int s1 = p1 - > kv - > GetInt ( col , 0 ) ;
int s2 = p2 - > kv - > GetInt ( col , 0 ) ;
if ( s1 < s2 )
{
return - 1 ;
}
else if ( s1 > s2 )
{
return 1 ;
}
return 0 ;
}
else
{
// compare as string
const char * s1 = p1 - > kv - > GetString ( col , " " ) ;
const char * s2 = p2 - > kv - > GetString ( col , " " ) ;
return Q_stricmp ( s1 , s2 ) ;
}
}
else // its an imagePanel column
{
const ImagePanel * s1 = ( const ImagePanel * ) p1 - > kv - > GetPtr ( col , NULL ) ;
const ImagePanel * s2 = ( const ImagePanel * ) p2 - > kv - > GetPtr ( col , NULL ) ;
if ( s1 < s2 )
{
return - 1 ;
}
else if ( s1 > s2 )
{
return 1 ;
}
return 0 ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Sorts items by comparing precalculated list values
//-----------------------------------------------------------------------------
static int __cdecl FastSortFunc (
ListPanel * pPanel ,
const ListPanelItem & item1 ,
const ListPanelItem & item2 )
{
const vgui : : FastSortListPanelItem * p1 = ( vgui : : FastSortListPanelItem * ) & item1 ;
const vgui : : FastSortListPanelItem * p2 = ( vgui : : FastSortListPanelItem * ) & item2 ;
Assert ( p1 & & p2 ) ;
// compare the precalculated indices
if ( p1 - > primarySortIndexValue < p2 - > primarySortIndexValue )
{
return 1 ;
}
else if ( p1 - > primarySortIndexValue > p2 - > primarySortIndexValue )
{
return - 1 ;
}
// they're equal, compare the secondary indices
if ( p1 - > secondarySortIndexValue < p2 - > secondarySortIndexValue )
{
return 1 ;
}
else if ( p1 - > secondarySortIndexValue > p2 - > secondarySortIndexValue )
{
return - 1 ;
}
// still equal; just compare the pointers (so we get deterministic results)
return ( p1 < p2 ) ? 1 : - 1 ;
}
static int s_iDuplicateIndex = 1 ;
//-----------------------------------------------------------------------------
// Purpose: sorting function used in the column index redblack tree
//-----------------------------------------------------------------------------
bool ListPanel : : RBTreeLessFunc ( vgui : : ListPanel : : IndexItem_t & item1 , vgui : : ListPanel : : IndexItem_t & item2 )
{
int result = s_pSortFunc ( s_pCurrentSortingListPanel , * item1 . dataItem , * item2 . dataItem ) ;
if ( result = = 0 )
{
// they're the same value, set their duplicate index to reflect that
if ( item1 . duplicateIndex )
{
item2 . duplicateIndex = item1 . duplicateIndex ;
}
else if ( item2 . duplicateIndex )
{
item1 . duplicateIndex = item2 . duplicateIndex ;
}
else
{
item1 . duplicateIndex = item2 . duplicateIndex = s_iDuplicateIndex + + ;
}
}
return ( result > 0 ) ;
}
DECLARE_BUILD_FACTORY ( ListPanel ) ;
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
ListPanel : : ListPanel ( Panel * parent , const char * panelName ) : BaseClass ( parent , panelName )
{
m_bIgnoreDoubleClick = false ;
m_bMultiselectEnabled = true ;
m_iEditModeItemID = 0 ;
m_iEditModeColumn = 0 ;
m_iHeaderHeight = 20 ;
m_iRowHeight = 20 ;
m_bCanSelectIndividualCells = false ;
m_iSelectedColumn = - 1 ;
m_bAllowUserAddDeleteColumns = false ;
m_hbar = new ScrollBar ( this , " HorizScrollBar " , false ) ;
m_hbar - > AddActionSignalTarget ( this ) ;
m_hbar - > SetVisible ( false ) ;
m_vbar = new ScrollBar ( this , " VertScrollBar " , true ) ;
m_vbar - > SetVisible ( false ) ;
m_vbar - > AddActionSignalTarget ( this ) ;
m_pLabel = new Label ( this , NULL , " " ) ;
m_pLabel - > SetVisible ( false ) ;
m_pLabel - > SetPaintBackgroundEnabled ( false ) ;
m_pLabel - > SetContentAlignment ( Label : : a_west ) ;
m_pTextImage = new TextImage ( " " ) ;
m_pImagePanel = new ImagePanel ( NULL , " ListImage " ) ;
m_pImagePanel - > SetAutoDelete ( false ) ;
m_iSortColumn = - 1 ;
m_iSortColumnSecondary = - 1 ;
m_bSortAscending = true ;
m_bSortAscendingSecondary = true ;
m_lastBarWidth = 0 ;
m_iColumnDraggerMoved = - 1 ;
m_bNeedsSort = false ;
m_LastItemSelected = - 1 ;
m_pImageList = NULL ;
m_bDeleteImageListWhenDone = false ;
m_pEmptyListText = new TextImage ( " " ) ;
m_nUserConfigFileVersion = 1 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
ListPanel : : ~ ListPanel ( )
{
// free data from table
RemoveAll ( ) ;
// free column headers
unsigned char i ;
for ( i = m_ColumnsData . Head ( ) ; i ! = m_ColumnsData . InvalidIndex ( ) ; i = m_ColumnsData . Next ( i ) )
{
m_ColumnsData [ i ] . m_pHeader - > MarkForDeletion ( ) ;
m_ColumnsData [ i ] . m_pResizer - > MarkForDeletion ( ) ;
}
m_ColumnsData . RemoveAll ( ) ;
delete m_pTextImage ;
delete m_pImagePanel ;
delete m_vbar ;
if ( m_bDeleteImageListWhenDone )
{
delete m_pImageList ;
}
delete m_pEmptyListText ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SetImageList ( ImageList * imageList , bool deleteImageListWhenDone )
{
// get rid of existing list image if there's one and we're supposed to get rid of it
if ( m_pImageList & & m_bDeleteImageListWhenDone )
{
delete m_pImageList ;
}
m_bDeleteImageListWhenDone = deleteImageListWhenDone ;
m_pImageList = imageList ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SetColumnHeaderHeight ( int height )
{
m_iHeaderHeight = height ;
}
//-----------------------------------------------------------------------------
// Purpose: adds a column header.
// this->FindChildByName(columnHeaderName) can be used to retrieve a pointer to a header panel by name
//
// if minWidth and maxWidth are BOTH NOTRESIZABLE or RESIZABLE
// the min and max size will be calculated automatically for you with that attribute
// columns are resizable by default
// if min and max size are specified column is resizable
//
// A small note on passing numbers for minWidth and maxWidth,
// If the initial window size is larger than the sum of the original widths of the columns,
// you can wind up with the columns "snapping" to size after the first window focus
// This is because the dxPerBar being calculated in PerformLayout()
// is making resizable bounded headers exceed thier maxWidths at the Start.
// Solution is to either put in support for redistributing the extra dx being truncated and
// therefore added to the last column on window opening, which is what causes the snapping.
// OR to
// ensure the difference between the starting sum of widths is not too much smaller/bigger
// than the starting window size so the starting dx doesn't cause snapping to occur.
// The easiest thing is to simply set it so your column widths add up to the starting size of the window on opening.
//
// Another note: Always give bounds for the last column you add or make it not resizable.
//
// Columns can have text headers or images for headers (e.g. password icon)
//-----------------------------------------------------------------------------
void ListPanel : : AddColumnHeader ( int index , const char * columnName , const char * columnText , int width , int columnFlags )
{
if ( columnFlags & COLUMN_FIXEDSIZE & & ! ( columnFlags & COLUMN_RESIZEWITHWINDOW ) )
{
// for fixed size columns, set the min & max widths to be the same as the initial width
AddColumnHeader ( index , columnName , columnText , width , width , width , columnFlags ) ;
}
else
{
AddColumnHeader ( index , columnName , columnText , width , 20 , 10000 , columnFlags ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Adds a new column
//-----------------------------------------------------------------------------
void ListPanel : : AddColumnHeader ( int index , const char * columnName , const char * columnText , int width , int minWidth , int maxWidth , int columnFlags )
{
Assert ( minWidth < = width ) ;
Assert ( maxWidth > = width ) ;
// get our permanent index
unsigned char columnDataIndex = m_ColumnsData . AddToTail ( ) ;
// put this index on the tail, so all item's m_SortedTreeIndexes have a consistent mapping
m_ColumnsHistory . AddToTail ( columnDataIndex ) ;
// put this column in the right place visually
m_CurrentColumns . InsertBefore ( index , columnDataIndex ) ;
// create the actual column object
column_t & column = m_ColumnsData [ columnDataIndex ] ;
// create the column header button
Button * pButton = SETUP_PANEL ( new ColumnButton ( this , columnName , columnText ) ) ; // the cell rendering mucks with the button visibility during the solvetraverse loop,
//so force applyschemesettings to make sure its run
pButton - > SetSize ( width , 24 ) ;
pButton - > AddActionSignalTarget ( this ) ;
pButton - > SetContentAlignment ( Label : : a_west ) ;
pButton - > SetTextInset ( 5 , 0 ) ;
column . m_pHeader = pButton ;
column . m_iMinWidth = minWidth ;
column . m_iMaxWidth = maxWidth ;
column . m_bResizesWithWindow = columnFlags & COLUMN_RESIZEWITHWINDOW ;
column . m_bTypeIsText = ! ( columnFlags & COLUMN_IMAGE ) ;
column . m_bHidden = false ;
column . m_bUnhidable = ( columnFlags & COLUMN_UNHIDABLE ) ;
column . m_nContentAlignment = Label : : a_west ;
Dragger * dragger = new Dragger ( index ) ;
dragger - > SetParent ( this ) ;
dragger - > AddActionSignalTarget ( this ) ;
dragger - > MoveToFront ( ) ;
if ( minWidth = = maxWidth | | ( columnFlags & COLUMN_FIXEDSIZE ) )
{
// not resizable so disable the slider
dragger - > SetMovable ( false ) ;
}
column . m_pResizer = dragger ;
// add default sort function
column . m_pSortFunc = NULL ;
// Set the SortedTree less than func to the generic RBTreeLessThanFunc
m_ColumnsData [ columnDataIndex ] . m_SortedTree . SetLessFunc ( ( IndexRBTree_t : : LessFunc_t ) RBTreeLessFunc ) ;
// go through all the headers and make sure their Command has the right column ID
ResetColumnHeaderCommands ( ) ;
// create the new data index
ResortColumnRBTree ( index ) ;
// ensure scroll bar is topmost compared to column headers
m_vbar - > MoveToFront ( ) ;
// fix up our visibility
SetColumnVisible ( index , ! ( columnFlags & COLUMN_HIDDEN ) ) ;
InvalidateLayout ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Recreates a column's RB Sorted Tree
//-----------------------------------------------------------------------------
void ListPanel : : ResortColumnRBTree ( int col )
{
Assert ( m_CurrentColumns . IsValidIndex ( col ) ) ;
unsigned char dataColumnIndex = m_CurrentColumns [ col ] ;
int columnHistoryIndex = m_ColumnsHistory . Find ( dataColumnIndex ) ;
column_t & column = m_ColumnsData [ dataColumnIndex ] ;
IndexRBTree_t & rbtree = column . m_SortedTree ;
// remove all elements - we're going to create from scratch
rbtree . RemoveAll ( ) ;
s_pCurrentSortingListPanel = this ;
s_currentSortingColumnTypeIsText = column . m_bTypeIsText ; // type of data in the column
SortFunc * sortFunc = column . m_pSortFunc ;
if ( ! sortFunc )
{
sortFunc = DefaultSortFunc ;
}
s_pSortFunc = sortFunc ;
s_bSortAscending = true ;
s_pSortFuncSecondary = NULL ;
// sort all current data items for this column
FOR_EACH_LL ( m_DataItems , i )
{
IndexItem_t item ;
item . dataItem = m_DataItems [ i ] ;
item . duplicateIndex = 0 ;
FastSortListPanelItem * dataItem = ( FastSortListPanelItem * ) m_DataItems [ i ] ;
// if this item doesn't already have a SortedTreeIndex for this column,
// if can only be because this is the brand new column, so add it to the SortedTreeIndexes
if ( dataItem - > m_SortedTreeIndexes . Count ( ) = = m_ColumnsHistory . Count ( ) - 1 & &
columnHistoryIndex = = m_ColumnsHistory . Count ( ) - 1 )
{
dataItem - > m_SortedTreeIndexes . AddToTail ( ) ;
}
Assert ( dataItem - > m_SortedTreeIndexes . IsValidIndex ( columnHistoryIndex ) ) ;
dataItem - > m_SortedTreeIndexes [ columnHistoryIndex ] = rbtree . Insert ( item ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Resets the "SetSortColumn" command for each column - in case columns were added or removed
//-----------------------------------------------------------------------------
void ListPanel : : ResetColumnHeaderCommands ( )
{
int i ;
for ( i = 0 ; i < m_CurrentColumns . Count ( ) ; i + + )
{
Button * pButton = m_ColumnsData [ m_CurrentColumns [ i ] ] . m_pHeader ;
pButton - > SetCommand ( new KeyValues ( " SetSortColumn " , " column " , i ) ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Sets the header text for a particular column.
//-----------------------------------------------------------------------------
void ListPanel : : SetColumnHeaderText ( int col , const char * text )
{
m_ColumnsData [ m_CurrentColumns [ col ] ] . m_pHeader - > SetText ( text ) ;
}
void ListPanel : : SetColumnHeaderText ( int col , wchar_t * text )
{
m_ColumnsData [ m_CurrentColumns [ col ] ] . m_pHeader - > SetText ( text ) ;
}
void ListPanel : : SetColumnTextAlignment ( int col , int align )
{
m_ColumnsData [ m_CurrentColumns [ col ] ] . m_nContentAlignment = align ;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the column header to have an image instead of text
//-----------------------------------------------------------------------------
void ListPanel : : SetColumnHeaderImage ( int column , int imageListIndex )
{
Assert ( m_pImageList ) ;
m_ColumnsData [ m_CurrentColumns [ column ] ] . m_pHeader - > SetTextImageIndex ( - 1 ) ;
m_ColumnsData [ m_CurrentColumns [ column ] ] . m_pHeader - > SetImageAtIndex ( 0 , m_pImageList - > GetImage ( imageListIndex ) , 0 ) ;
}
//-----------------------------------------------------------------------------
// Purpose: associates a tooltip with the column header
//-----------------------------------------------------------------------------
void ListPanel : : SetColumnHeaderTooltip ( int column , const char * tooltipText )
{
m_ColumnsData [ m_CurrentColumns [ column ] ] . m_pHeader - > GetTooltip ( ) - > SetText ( tooltipText ) ;
m_ColumnsData [ m_CurrentColumns [ column ] ] . m_pHeader - > GetTooltip ( ) - > SetTooltipFormatToSingleLine ( ) ;
m_ColumnsData [ m_CurrentColumns [ column ] ] . m_pHeader - > GetTooltip ( ) - > SetTooltipDelay ( 0 ) ;
}
int ListPanel : : GetNumColumnHeaders ( ) const
{
return m_CurrentColumns . Count ( ) ;
}
bool ListPanel : : GetColumnHeaderText ( int index , char * pOut , int maxLen )
{
if ( index < m_CurrentColumns . Count ( ) )
{
m_ColumnsData [ m_CurrentColumns [ index ] ] . m_pHeader - > GetText ( pOut , maxLen ) ;
return true ;
}
else
{
return false ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SetColumnSortable ( int col , bool sortable )
{
if ( sortable )
{
m_ColumnsData [ m_CurrentColumns [ col ] ] . m_pHeader - > SetCommand ( new KeyValues ( " SetSortColumn " , " column " , col ) ) ;
}
else
{
m_ColumnsData [ m_CurrentColumns [ col ] ] . m_pHeader - > SetCommand ( ( const char * ) NULL ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Changes the visibility of a column
//-----------------------------------------------------------------------------
void ListPanel : : SetColumnVisible ( int col , bool visible )
{
column_t & column = m_ColumnsData [ m_CurrentColumns [ col ] ] ;
bool bHidden = ! visible ;
if ( column . m_bHidden = = bHidden )
return ;
if ( column . m_bUnhidable )
return ;
column . m_bHidden = bHidden ;
if ( bHidden )
{
column . m_pHeader - > SetVisible ( false ) ;
column . m_pResizer - > SetVisible ( false ) ;
}
else
{
column . m_pHeader - > SetVisible ( true ) ;
column . m_pResizer - > SetVisible ( true ) ;
}
InvalidateLayout ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : RemoveColumn ( int col )
{
if ( ! m_CurrentColumns . IsValidIndex ( col ) )
return ;
// find the appropriate column data
unsigned char columnDataIndex = m_CurrentColumns [ col ] ;
// remove it from the current columns
m_CurrentColumns . Remove ( col ) ;
// zero out this entry in m_ColumnsHistory
unsigned char i ;
for ( i = 0 ; i < m_ColumnsHistory . Count ( ) ; i + + )
{
if ( m_ColumnsHistory [ i ] = = columnDataIndex )
{
m_ColumnsHistory [ i ] = m_ColumnsData . InvalidIndex ( ) ;
break ;
}
}
Assert ( i ! = m_ColumnsHistory . Count ( ) ) ;
// delete and remove the column data
m_ColumnsData [ columnDataIndex ] . m_SortedTree . RemoveAll ( ) ;
m_ColumnsData [ columnDataIndex ] . m_pHeader - > MarkForDeletion ( ) ;
m_ColumnsData [ columnDataIndex ] . m_pResizer - > MarkForDeletion ( ) ;
m_ColumnsData . Remove ( columnDataIndex ) ;
ResetColumnHeaderCommands ( ) ;
InvalidateLayout ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the index of a column by column->GetName()
//-----------------------------------------------------------------------------
int ListPanel : : FindColumn ( const char * columnName )
{
for ( int i = 0 ; i < m_CurrentColumns . Count ( ) ; i + + )
{
if ( ! stricmp ( columnName , m_ColumnsData [ m_CurrentColumns [ i ] ] . m_pHeader - > GetName ( ) ) )
{
return i ;
}
}
return - 1 ;
}
//-----------------------------------------------------------------------------
// Purpose: adds an item to the view
// data->GetName() is used to uniquely identify an item
// data sub items are matched against column header name to be used in the table
//-----------------------------------------------------------------------------
int ListPanel : : AddItem ( const KeyValues * item , uintp userData , bool bScrollToItem , bool bSortOnAdd )
{
FastSortListPanelItem * newitem = new FastSortListPanelItem ;
newitem - > kv = item - > MakeCopy ( ) ;
newitem - > userData = userData ;
newitem - > m_pDragData = NULL ;
newitem - > m_bImage = newitem - > kv - > GetInt ( " image " ) ! = 0 ? true : false ;
newitem - > m_nImageIndex = newitem - > kv - > GetInt ( " image " ) ;
newitem - > m_nImageIndexSelected = newitem - > kv - > GetInt ( " imageSelected " ) ;
newitem - > m_pIcon = reinterpret_cast < IImage * > ( newitem - > kv - > GetPtr ( " iconImage " ) ) ;
int itemID = m_DataItems . AddToTail ( newitem ) ;
int displayRow = m_VisibleItems . AddToTail ( itemID ) ;
newitem - > visible = true ;
// put the item in each column's sorted Tree Index
IndexItem ( itemID ) ;
if ( bSortOnAdd )
{
m_bNeedsSort = true ;
}
InvalidateLayout ( ) ;
if ( bScrollToItem )
{
// scroll to last item
m_vbar - > SetValue ( displayRow ) ;
}
return itemID ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SetUserData ( int itemID , uintp userData )
{
if ( ! m_DataItems . IsValidIndex ( itemID ) )
return ;
m_DataItems [ itemID ] - > userData = userData ;
}
//-----------------------------------------------------------------------------
// Purpose: Finds the first itemID with a matching userData
//-----------------------------------------------------------------------------
int ListPanel : : GetItemIDFromUserData ( uintp userData )
{
FOR_EACH_LL ( m_DataItems , itemID )
{
if ( m_DataItems [ itemID ] - > userData = = userData )
return itemID ;
}
// not found
return InvalidItemID ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int ListPanel : : GetItemCount ( void )
{
return m_VisibleItems . Count ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: gets the item ID of an item by name (data->GetName())
//-----------------------------------------------------------------------------
int ListPanel : : GetItem ( const char * itemName )
{
FOR_EACH_LL ( m_DataItems , i )
{
if ( ! stricmp ( m_DataItems [ i ] - > kv - > GetName ( ) , itemName ) )
{
return i ;
}
}
// failure
return - 1 ;
}
//-----------------------------------------------------------------------------
// Purpose: returns pointer to data the itemID holds
//-----------------------------------------------------------------------------
KeyValues * ListPanel : : GetItem ( int itemID )
{
if ( ! m_DataItems . IsValidIndex ( itemID ) )
return NULL ;
return m_DataItems [ itemID ] - > kv ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int ListPanel : : GetItemCurrentRow ( int itemID )
{
return m_VisibleItems . Find ( itemID ) ;
}
//-----------------------------------------------------------------------------
// Attaches drag data to a particular item
//-----------------------------------------------------------------------------
void ListPanel : : SetItemDragData ( int itemID , const KeyValues * data )
{
ListPanelItem * pItem = m_DataItems [ itemID ] ;
if ( pItem - > m_pDragData )
{
pItem - > m_pDragData - > deleteThis ( ) ;
}
pItem - > m_pDragData = data - > MakeCopy ( ) ;
}
//-----------------------------------------------------------------------------
// Attaches drag data to a particular item
//-----------------------------------------------------------------------------
void ListPanel : : OnCreateDragData ( KeyValues * msg )
{
int nCount = GetSelectedItemsCount ( ) ;
if ( nCount = = 0 )
return ;
for ( int i = 0 ; i < nCount ; + + i )
{
int nItemID = GetSelectedItem ( i ) ;
KeyValues * pDragData = m_DataItems [ nItemID ] - > m_pDragData ;
if ( pDragData )
{
KeyValues * pDragDataCopy = pDragData - > MakeCopy ( ) ;
msg - > AddSubKey ( pDragDataCopy ) ;
}
}
// Add the keys of the last item directly into the root also
int nLastItemID = GetSelectedItem ( nCount - 1 ) ;
KeyValues * pLastItemDrag = m_DataItems [ nLastItemID ] - > m_pDragData ;
if ( pLastItemDrag )
{
pLastItemDrag - > CopySubkeys ( msg ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int ListPanel : : GetItemIDFromRow ( int currentRow )
{
if ( ! m_VisibleItems . IsValidIndex ( currentRow ) )
return - 1 ;
return m_VisibleItems [ currentRow ] ;
}
int ListPanel : : FirstItem ( ) const
{
return m_DataItems . Head ( ) ;
}
int ListPanel : : NextItem ( int iItem ) const
{
return m_DataItems . Next ( iItem ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int ListPanel : : InvalidItemID ( ) const
{
return m_DataItems . InvalidIndex ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool ListPanel : : IsValidItemID ( int itemID )
{
return m_DataItems . IsValidIndex ( itemID ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
ListPanelItem * ListPanel : : GetItemData ( int itemID )
{
if ( ! m_DataItems . IsValidIndex ( itemID ) )
return NULL ;
return m_DataItems [ itemID ] ;
}
//-----------------------------------------------------------------------------
// Purpose: returns user data for itemID
//-----------------------------------------------------------------------------
uintp ListPanel : : GetItemUserData ( int itemID )
{
if ( ! m_DataItems . IsValidIndex ( itemID ) )
return 0 ;
return m_DataItems [ itemID ] - > userData ;
}
//-----------------------------------------------------------------------------
// Purpose: updates the view with any changes to the data
// Input : itemID - index to update
//-----------------------------------------------------------------------------
void ListPanel : : ApplyItemChanges ( int itemID )
{
// reindex the item and then redraw
IndexItem ( itemID ) ;
InvalidateLayout ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Adds the item into the column indexes
//-----------------------------------------------------------------------------
void ListPanel : : IndexItem ( int itemID )
{
FastSortListPanelItem * newitem = ( FastSortListPanelItem * ) m_DataItems [ itemID ] ;
// remove the item from the indexes and re-add
int maxCount = min ( m_ColumnsHistory . Count ( ) , newitem - > m_SortedTreeIndexes . Count ( ) ) ;
for ( int i = 0 ; i < maxCount ; i + + )
{
IndexRBTree_t & rbtree = m_ColumnsData [ m_ColumnsHistory [ i ] ] . m_SortedTree ;
rbtree . RemoveAt ( newitem - > m_SortedTreeIndexes [ i ] ) ;
}
// make sure it's all free
newitem - > m_SortedTreeIndexes . RemoveAll ( ) ;
// reserve one index per historical column - pad it out
newitem - > m_SortedTreeIndexes . AddMultipleToTail ( m_ColumnsHistory . Count ( ) ) ;
// set the current sorting list (since the insert will need to sort)
s_pCurrentSortingListPanel = this ;
// add the item into the RB tree for each column
for ( int i = 0 ; i < m_ColumnsHistory . Count ( ) ; i + + )
{
// skip over any removed columns
if ( m_ColumnsHistory [ i ] = = m_ColumnsData . InvalidIndex ( ) )
continue ;
column_t & column = m_ColumnsData [ m_ColumnsHistory [ i ] ] ;
IndexItem_t item ;
item . dataItem = newitem ;
item . duplicateIndex = 0 ;
IndexRBTree_t & rbtree = column . m_SortedTree ;
// setup sort state
s_pCurrentSortingListPanel = this ;
s_pCurrentSortingColumn = column . m_pHeader - > GetName ( ) ; // name of current column for sorting
s_currentSortingColumnTypeIsText = column . m_bTypeIsText ; // type of data in the column
SortFunc * sortFunc = column . m_pSortFunc ;
if ( ! sortFunc )
{
sortFunc = DefaultSortFunc ;
}
s_pSortFunc = sortFunc ;
s_bSortAscending = true ;
s_pSortFuncSecondary = NULL ;
// insert index
newitem - > m_SortedTreeIndexes [ i ] = rbtree . Insert ( item ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : RereadAllItems ( )
{
//!! need to make this more efficient
InvalidateLayout ( ) ;
}
//-----------------------------------------------------------------------------
// Cleans up allocations associated with a particular item
//-----------------------------------------------------------------------------
void ListPanel : : CleanupItem ( FastSortListPanelItem * data )
{
if ( data )
{
if ( data - > kv )
{
data - > kv - > deleteThis ( ) ;
}
if ( data - > m_pDragData )
{
data - > m_pDragData - > deleteThis ( ) ;
}
delete data ;
}
}
//-----------------------------------------------------------------------------
// Purpose: Removes an item at the specified item
//-----------------------------------------------------------------------------
void ListPanel : : RemoveItem ( int itemID )
{
# ifdef _X360
bool renavigate = false ;
if ( HasFocus ( ) )
{
for ( int i = 0 ; i < GetSelectedItemsCount ( ) ; + + i )
{
if ( itemID = = GetSelectedItem ( i ) )
{
renavigate = true ;
break ;
}
}
}
# endif
FastSortListPanelItem * data = ( FastSortListPanelItem * ) m_DataItems [ itemID ] ;
if ( ! data )
return ;
// remove from column sorted indexes
int i ;
for ( i = 0 ; i < m_ColumnsHistory . Count ( ) ; i + + )
{
if ( m_ColumnsHistory [ i ] = = m_ColumnsData . InvalidIndex ( ) )
continue ;
IndexRBTree_t & rbtree = m_ColumnsData [ m_ColumnsHistory [ i ] ] . m_SortedTree ;
rbtree . RemoveAt ( data - > m_SortedTreeIndexes [ i ] ) ;
}
// remove from selection
m_SelectedItems . FindAndRemove ( itemID ) ;
PostActionSignal ( new KeyValues ( " ItemDeselected " ) ) ;
// remove from visible items
m_VisibleItems . FindAndRemove ( itemID ) ;
// remove from data
m_DataItems . Remove ( itemID ) ;
CleanupItem ( data ) ;
InvalidateLayout ( ) ;
# ifdef _X360
if ( renavigate )
{
NavigateTo ( ) ;
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose: clears and deletes all the memory used by the data items
//-----------------------------------------------------------------------------
void ListPanel : : RemoveAll ( )
{
// remove all sort indexes
for ( int i = 0 ; i < m_ColumnsHistory . Count ( ) ; i + + )
{
m_ColumnsData [ m_ColumnsHistory [ i ] ] . m_SortedTree . RemoveAll ( ) ;
}
FOR_EACH_LL ( m_DataItems , index )
{
FastSortListPanelItem * pItem = m_DataItems [ index ] ;
CleanupItem ( pItem ) ;
}
m_DataItems . RemoveAll ( ) ;
m_VisibleItems . RemoveAll ( ) ;
ClearSelectedItems ( ) ;
InvalidateLayout ( ) ;
# ifdef _X360
if ( HasFocus ( ) )
{
NavigateTo ( ) ;
}
# endif
}
//-----------------------------------------------------------------------------
// Purpose: obselete, use RemoveAll();
//-----------------------------------------------------------------------------
void ListPanel : : DeleteAllItems ( )
{
RemoveAll ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : ResetScrollBar ( )
{
// delete and reallocate to besure the scroll bar's
// information is correct.
delete m_vbar ;
m_vbar = new ScrollBar ( this , " VertScrollBar " , true ) ;
m_vbar - > SetVisible ( false ) ;
m_vbar - > AddActionSignalTarget ( this ) ;
}
//-----------------------------------------------------------------------------
// Purpose: returns the count of selected rows
//-----------------------------------------------------------------------------
int ListPanel : : GetSelectedItemsCount ( )
{
return m_SelectedItems . Count ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: returns the selected item by selection index
// Input : selectionIndex - valid in range [0, GetNumSelectedRows)
// Output : int - itemID
//-----------------------------------------------------------------------------
int ListPanel : : GetSelectedItem ( int selectionIndex )
{
if ( m_SelectedItems . IsValidIndex ( selectionIndex ) )
return m_SelectedItems [ selectionIndex ] ;
return - 1 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int ListPanel : : GetSelectedColumn ( )
{
return m_iSelectedColumn ;
}
//-----------------------------------------------------------------------------
// Purpose: Clears all selected rows
//-----------------------------------------------------------------------------
void ListPanel : : ClearSelectedItems ( )
{
int nPrevCount = m_SelectedItems . Count ( ) ;
m_SelectedItems . RemoveAll ( ) ;
if ( nPrevCount > 0 )
{
PostActionSignal ( new KeyValues ( " ItemDeselected " ) ) ;
}
m_LastItemSelected = - 1 ;
m_iSelectedColumn = - 1 ;
}
//-----------------------------------------------------------------------------
bool ListPanel : : IsItemSelected ( int itemID )
{
return m_DataItems . IsValidIndex ( itemID ) & & m_SelectedItems . HasElement ( itemID ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : AddSelectedItem ( int itemID )
{
if ( ! m_DataItems . IsValidIndex ( itemID ) )
return ;
Assert ( ! m_SelectedItems . HasElement ( itemID ) ) ;
m_LastItemSelected = itemID ;
m_SelectedItems . AddToTail ( itemID ) ;
PostActionSignal ( new KeyValues ( " ItemSelected " ) ) ;
Repaint ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SetSingleSelectedItem ( int itemID )
{
ClearSelectedItems ( ) ;
AddSelectedItem ( itemID ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SetSelectedCell ( int itemID , int col )
{
if ( ! m_bCanSelectIndividualCells )
{
SetSingleSelectedItem ( itemID ) ;
return ;
}
// make sure it's a valid cell
if ( ! m_DataItems . IsValidIndex ( itemID ) )
return ;
if ( ! m_CurrentColumns . IsValidIndex ( col ) )
return ;
SetSingleSelectedItem ( itemID ) ;
m_iSelectedColumn = col ;
}
//-----------------------------------------------------------------------------
// Purpose: returns the data held by a specific cell
//-----------------------------------------------------------------------------
void ListPanel : : GetCellText ( int itemID , int col , wchar_t * wbuffer , int bufferSizeInBytes )
{
if ( ! wbuffer | | ! bufferSizeInBytes )
return ;
wcscpy ( wbuffer , L " " ) ;
KeyValues * itemData = GetItem ( itemID ) ;
if ( ! itemData )
{
return ;
}
// Look up column header
if ( col < 0 | | col > = m_CurrentColumns . Count ( ) )
{
return ;
}
const char * key = m_ColumnsData [ m_CurrentColumns [ col ] ] . m_pHeader - > GetName ( ) ;
if ( ! key | | ! key [ 0 ] )
{
return ;
}
char const * val = itemData - > GetString ( key , " " ) ;
if ( ! val | | ! key [ 0 ] )
return ;
const wchar_t * wval = NULL ;
if ( val [ 0 ] = = ' # ' )
{
StringIndex_t si = g_pVGuiLocalize - > FindIndex ( val + 1 ) ;
if ( si ! = INVALID_LOCALIZE_STRING_INDEX )
{
wval = g_pVGuiLocalize - > GetValueByIndex ( si ) ;
}
}
if ( ! wval )
{
wval = itemData - > GetWString ( key , L " " ) ;
}
wcsncpy ( wbuffer , wval , bufferSizeInBytes / sizeof ( wchar_t ) ) ;
wbuffer [ ( bufferSizeInBytes / sizeof ( wchar_t ) ) - 1 ] = 0 ;
}
//-----------------------------------------------------------------------------
// Purpose: returns the data held by a specific cell
//-----------------------------------------------------------------------------
IImage * ListPanel : : GetCellImage ( int itemID , int col ) //, ImagePanel *&buffer)
{
// if ( !buffer )
// return;
KeyValues * itemData = GetItem ( itemID ) ;
if ( ! itemData )
{
return NULL ;
}
// Look up column header
if ( col < 0 | | col > = m_CurrentColumns . Count ( ) )
{
return NULL ;
}
const char * key = m_ColumnsData [ m_CurrentColumns [ col ] ] . m_pHeader - > GetName ( ) ;
if ( ! key | | ! key [ 0 ] )
{
return NULL ;
}
if ( ! m_pImageList )
{
return NULL ;
}
int imageIndex = itemData - > GetInt ( key , 0 ) ;
if ( m_pImageList - > IsValidIndex ( imageIndex ) )
{
if ( imageIndex > 0 )
{
return m_pImageList - > GetImage ( imageIndex ) ;
}
}
return NULL ;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the panel to use to render a cell
//-----------------------------------------------------------------------------
Panel * ListPanel : : GetCellRenderer ( int itemID , int col )
{
Assert ( m_pTextImage ) ;
Assert ( m_pImagePanel ) ;
column_t & column = m_ColumnsData [ m_CurrentColumns [ col ] ] ;
IScheme * pScheme = scheme ( ) - > GetIScheme ( GetScheme ( ) ) ;
m_pLabel - > SetContentAlignment ( ( Label : : Alignment ) column . m_nContentAlignment ) ;
if ( column . m_bTypeIsText )
{
wchar_t tempText [ 256 ] ;
// Grab cell text
GetCellText ( itemID , col , tempText , 256 ) ;
KeyValues * item = GetItem ( itemID ) ;
m_pTextImage - > SetText ( tempText ) ;
int cw , tall ;
m_pTextImage - > GetContentSize ( cw , tall ) ;
// set cell size
Panel * header = column . m_pHeader ;
int wide = header - > GetWide ( ) ;
m_pTextImage - > SetSize ( min ( cw , wide - 5 ) , tall ) ;
m_pLabel - > SetTextImageIndex ( 0 ) ;
m_pLabel - > SetImageAtIndex ( 0 , m_pTextImage , 3 ) ;
bool selected = false ;
if ( m_SelectedItems . HasElement ( itemID ) & & ( ! m_bCanSelectIndividualCells | | col = = m_iSelectedColumn ) )
{
selected = true ;
VPANEL focus = input ( ) - > GetFocus ( ) ;
// if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
if ( HasFocus ( ) | | ( focus & & ipanel ( ) - > HasParent ( focus , GetVParent ( ) ) ) )
{
m_pLabel - > SetBgColor ( GetSchemeColor ( " ListPanel.SelectedBgColor " , pScheme ) ) ;
// selection
}
else
{
m_pLabel - > SetBgColor ( GetSchemeColor ( " ListPanel.SelectedOutOfFocusBgColor " , pScheme ) ) ;
}
if ( item - > IsEmpty ( " cellcolor " ) = = false )
{
m_pTextImage - > SetColor ( item - > GetColor ( " cellcolor " ) ) ;
}
else if ( item - > GetInt ( " disabled " , 0 ) = = 0 )
{
m_pTextImage - > SetColor ( m_SelectionFgColor ) ;
}
else
{
m_pTextImage - > SetColor ( m_DisabledSelectionFgColor ) ;
}
m_pLabel - > SetPaintBackgroundEnabled ( true ) ;
}
else
{
if ( item - > IsEmpty ( " cellcolor " ) = = false )
{
m_pTextImage - > SetColor ( item - > GetColor ( " cellcolor " ) ) ;
}
else if ( item - > GetInt ( " disabled " , 0 ) = = 0 )
{
m_pTextImage - > SetColor ( m_LabelFgColor ) ;
}
else
{
m_pTextImage - > SetColor ( m_DisabledColor ) ;
}
m_pLabel - > SetPaintBackgroundEnabled ( false ) ;
}
FastSortListPanelItem * listItem = m_DataItems [ itemID ] ;
if ( col = = 0 & &
listItem - > m_bImage & & m_pImageList )
{
IImage * pImage = NULL ;
if ( listItem - > m_pIcon )
{
pImage = listItem - > m_pIcon ;
}
else
{
int imageIndex = selected ? listItem - > m_nImageIndexSelected : listItem - > m_nImageIndex ;
if ( m_pImageList - > IsValidIndex ( imageIndex ) )
{
pImage = m_pImageList - > GetImage ( imageIndex ) ;
}
}
if ( pImage )
{
m_pLabel - > SetTextImageIndex ( 1 ) ;
m_pLabel - > SetImageAtIndex ( 0 , pImage , 0 ) ;
m_pLabel - > SetImageAtIndex ( 1 , m_pTextImage , 3 ) ;
}
}
return m_pLabel ;
}
else // if its an Image Panel
{
if ( m_SelectedItems . HasElement ( itemID ) & & ( ! m_bCanSelectIndividualCells | | col = = m_iSelectedColumn ) )
{
VPANEL focus = input ( ) - > GetFocus ( ) ;
// if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
if ( HasFocus ( ) | | ( focus & & ipanel ( ) - > HasParent ( focus , GetVParent ( ) ) ) )
{
m_pLabel - > SetBgColor ( GetSchemeColor ( " ListPanel.SelectedBgColor " , pScheme ) ) ;
// selection
}
else
{
m_pLabel - > SetBgColor ( GetSchemeColor ( " ListPanel.SelectedOutOfFocusBgColor " , pScheme ) ) ;
}
// selection
m_pLabel - > SetPaintBackgroundEnabled ( true ) ;
}
else
{
m_pLabel - > SetPaintBackgroundEnabled ( false ) ;
}
IImage * pIImage = GetCellImage ( itemID , col ) ;
m_pLabel - > SetImageAtIndex ( 0 , pIImage , 0 ) ;
return m_pLabel ;
}
}
//-----------------------------------------------------------------------------
// Purpose: relayouts out the panel after any internal changes
//-----------------------------------------------------------------------------
void ListPanel : : PerformLayout ( )
{
if ( m_CurrentColumns . Count ( ) = = 0 )
return ;
if ( m_bNeedsSort )
{
SortList ( ) ;
}
int rowsperpage = ( int ) GetRowsPerPage ( ) ;
// count the number of visible items
int visibleItemCount = m_VisibleItems . Count ( ) ;
//!! need to make it recalculate scroll positions
m_vbar - > SetVisible ( true ) ;
m_vbar - > SetEnabled ( false ) ;
m_vbar - > SetRangeWindow ( rowsperpage ) ;
m_vbar - > SetRange ( 0 , visibleItemCount ) ;
m_vbar - > SetButtonPressedScrollValue ( 1 ) ;
int wide , tall ;
GetSize ( wide , tall ) ;
m_vbar - > SetPos ( wide - ( m_vbar - > GetWide ( ) + WINDOW_BORDER_WIDTH ) , 0 ) ;
m_vbar - > SetSize ( m_vbar - > GetWide ( ) , tall - 2 ) ;
m_vbar - > InvalidateLayout ( ) ;
int buttonMaxXPos = wide - ( m_vbar - > GetWide ( ) + WINDOW_BORDER_WIDTH ) ;
int nColumns = m_CurrentColumns . Count ( ) ;
// number of bars that can be resized
int numToResize = 0 ;
if ( m_iColumnDraggerMoved ! = - 1 ) // we're resizing in response to a column dragger
{
numToResize = 1 ; // only one column will change size, the one we dragged
}
else // we're resizing in response to a window resize
{
for ( int i = 0 ; i < nColumns ; i + + )
{
if ( m_ColumnsData [ m_CurrentColumns [ i ] ] . m_bResizesWithWindow // column is resizable in response to window
& & ! m_ColumnsData [ m_CurrentColumns [ i ] ] . m_bHidden )
{
numToResize + + ;
}
}
}
int dxPerBar ; // zero on window first opening
// location of the last column resizer
int oldSizeX = 0 , oldSizeY = 0 ;
int lastColumnIndex = nColumns - 1 ;
for ( int i = nColumns - 1 ; i > = 0 ; - - i )
{
if ( ! m_ColumnsData [ m_CurrentColumns [ i ] ] . m_bHidden )
{
m_ColumnsData [ m_CurrentColumns [ i ] ] . m_pHeader - > GetPos ( oldSizeX , oldSizeY ) ;
lastColumnIndex = i ;
break ;
}
}
bool bForceShrink = false ;
if ( numToResize = = 0 )
{
// make sure we've got enough to be within minwidth
int minWidth = 0 ;
for ( int i = 0 ; i < nColumns ; i + + )
{
if ( ! m_ColumnsData [ m_CurrentColumns [ i ] ] . m_bHidden )
{
minWidth + = m_ColumnsData [ m_CurrentColumns [ i ] ] . m_iMinWidth ;
}
}
// if all the minimum widths cannot fit in the space given, then we will shrink ALL columns an equal amount
if ( minWidth > buttonMaxXPos )
{
int dx = buttonMaxXPos - minWidth ;
dxPerBar = ( int ) ( ( float ) dx / ( float ) nColumns ) ;
bForceShrink = true ;
}
else
{
dxPerBar = 0 ;
}
m_lastBarWidth = buttonMaxXPos ;
}
else if ( oldSizeX ! = 0 ) // make sure this isnt the first time we opened the window
{
int dx = buttonMaxXPos - m_lastBarWidth ; // this is how much we grew or shrank.
// see how many bars we have and now much each should grow/shrink
dxPerBar = ( int ) ( ( float ) dx / ( float ) numToResize ) ;
m_lastBarWidth = buttonMaxXPos ;
}
else // this is the first time we've opened the window, make sure all our colums fit! resize if needed
{
int startingBarWidth = 0 ;
for ( int i = 0 ; i < nColumns ; i + + )
{
if ( ! m_ColumnsData [ m_CurrentColumns [ i ] ] . m_bHidden )
{
startingBarWidth + = m_ColumnsData [ m_CurrentColumns [ i ] ] . m_pHeader - > GetWide ( ) ;
}
}
int dx = buttonMaxXPos - startingBarWidth ; // this is how much we grew or shrank.
// see how many bars we have and now much each should grow/shrink
dxPerBar = ( int ) ( ( float ) dx / ( float ) numToResize ) ;
m_lastBarWidth = buttonMaxXPos ;
}
// Make sure nothing is smaller than minwidth to start with or else we'll get into trouble below.
for ( int i = 0 ; i < nColumns ; i + + )
{
column_t & column = m_ColumnsData [ m_CurrentColumns [ i ] ] ;
Panel * header = column . m_pHeader ;
if ( header - > GetWide ( ) < column . m_iMinWidth )
header - > SetWide ( column . m_iMinWidth ) ;
}
// This was a while(1) loop and we hit an infinite loop case, so now we max out the # of times it can loop.
for ( int iLoopSanityCheck = 0 ; iLoopSanityCheck < 1000 ; iLoopSanityCheck + + )
{
// try and place headers as is - before we have to force items to be minimum width
int x = - 1 ;
int i ;
for ( i = 0 ; i < nColumns ; i + + )
{
column_t & column = m_ColumnsData [ m_CurrentColumns [ i ] ] ;
Panel * header = column . m_pHeader ;
if ( column . m_bHidden )
{
header - > SetVisible ( false ) ;
continue ;
}
header - > SetPos ( x , 0 ) ;
header - > SetVisible ( true ) ;
// if we couldn't fit this column - then we need to force items to be minimum width
if ( x + column . m_iMinWidth > = buttonMaxXPos & & ! bForceShrink )
{
break ;
}
int hWide = header - > GetWide ( ) ;
// calculate the column's width
// make it so the last column always attaches to the scroll bar
if ( i = = lastColumnIndex )
{
hWide = buttonMaxXPos - x ;
}
else if ( i = = m_iColumnDraggerMoved ) // column resizing using dragger
{
hWide + = dxPerBar ; // adjust width of column
}
else if ( m_iColumnDraggerMoved = = - 1 ) // window is resizing
{
// either this column is allowed to resize OR we are forcing it because we're shrinking all columns
if ( column . m_bResizesWithWindow | | bForceShrink )
{
Assert ( column . m_iMinWidth < = column . m_iMaxWidth ) ;
hWide + = dxPerBar ; // adjust width of column
}
}
// enforce column mins and max's - unless we're FORCING it to shrink
if ( hWide < column . m_iMinWidth & & ! bForceShrink )
{
hWide = column . m_iMinWidth ; // adjust width of column
}
else if ( hWide > column . m_iMaxWidth )
{
hWide = column . m_iMaxWidth ;
}
header - > SetSize ( hWide , m_vbar - > GetWide ( ) ) ;
x + = hWide ;
// set the resizers
Panel * sizer = column . m_pResizer ;
if ( i = = lastColumnIndex )
{
sizer - > SetVisible ( false ) ;
}
else
{
sizer - > SetVisible ( true ) ;
}
sizer - > MoveToFront ( ) ;
sizer - > SetPos ( x - 4 , 0 ) ;
sizer - > SetSize ( 8 , m_vbar - > GetWide ( ) ) ;
}
// we made it all the way through
if ( i = = nColumns )
break ;
// we do this AFTER trying first, to let as many columns as possible try and get to their
// desired width before we forcing the minimum width on them
// get the total desired width of all the columns
int totalDesiredWidth = 0 ;
for ( i = 0 ; i < nColumns ; i + + )
{
if ( ! m_ColumnsData [ m_CurrentColumns [ i ] ] . m_bHidden )
{
Panel * pHeader = m_ColumnsData [ m_CurrentColumns [ i ] ] . m_pHeader ;
totalDesiredWidth + = pHeader - > GetWide ( ) ;
}
}
// shrink from the most right column to minimum width until we can fit them all
Assert ( totalDesiredWidth > buttonMaxXPos ) ;
for ( i = nColumns - 1 ; i > = 0 ; i - - )
{
column_t & column = m_ColumnsData [ m_CurrentColumns [ i ] ] ;
if ( ! column . m_bHidden )
{
Panel * pHeader = column . m_pHeader ;
totalDesiredWidth - = pHeader - > GetWide ( ) ;
if ( totalDesiredWidth + column . m_iMinWidth < = buttonMaxXPos )
{
int newWidth = buttonMaxXPos - totalDesiredWidth ;
pHeader - > SetSize ( newWidth , m_vbar - > GetWide ( ) ) ;
break ;
}
totalDesiredWidth + = column . m_iMinWidth ;
pHeader - > SetSize ( column . m_iMinWidth , m_vbar - > GetWide ( ) ) ;
}
}
// If we don't allow this to shrink, then as we resize, it can get stuck in an infinite loop.
dxPerBar - = 5 ;
if ( dxPerBar < 0 )
dxPerBar = 0 ;
if ( i = = - 1 )
{
break ;
}
}
// setup edit mode
if ( m_hEditModePanel . Get ( ) )
{
m_iTableStartX = 0 ;
m_iTableStartY = m_iHeaderHeight + 1 ;
int nTotalRows = m_VisibleItems . Count ( ) ;
int nRowsPerPage = GetRowsPerPage ( ) ;
// find the first visible item to display
int nStartItem = 0 ;
if ( nRowsPerPage < = nTotalRows )
{
nStartItem = m_vbar - > GetValue ( ) ;
}
bool bDone = false ;
int drawcount = 0 ;
for ( int i = nStartItem ; i < nTotalRows & & ! bDone ; i + + )
{
int x = 0 ;
if ( ! m_VisibleItems . IsValidIndex ( i ) )
continue ;
int itemID = m_VisibleItems [ i ] ;
// iterate the columns
for ( int j = 0 ; j < m_CurrentColumns . Count ( ) ; j + + )
{
Panel * header = m_ColumnsData [ m_CurrentColumns [ j ] ] . m_pHeader ;
if ( ! header - > IsVisible ( ) )
continue ;
wide = header - > GetWide ( ) ;
if ( itemID = = m_iEditModeItemID & &
j = = m_iEditModeColumn )
{
m_hEditModePanel - > SetPos ( x + m_iTableStartX + 2 , ( drawcount * m_iRowHeight ) + m_iTableStartY ) ;
m_hEditModePanel - > SetSize ( wide , m_iRowHeight - 1 ) ;
bDone = true ;
}
x + = wide ;
}
drawcount + + ;
}
}
Repaint ( ) ;
m_iColumnDraggerMoved = - 1 ; // reset to invalid column
m_iHeaderHeight = m_ColumnsData [ 0 ] . m_pHeader ? m_ColumnsData [ 0 ] . m_pHeader - > GetTall ( ) : m_iHeaderHeight ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : OnSizeChanged ( int wide , int tall )
{
BaseClass : : OnSizeChanged ( wide , tall ) ;
InvalidateLayout ( ) ;
Repaint ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Renders the cells
//-----------------------------------------------------------------------------
void ListPanel : : Paint ( )
{
if ( m_bNeedsSort )
{
SortList ( ) ;
}
// draw selection areas if any
int wide , tall ;
GetSize ( wide , tall ) ;
m_iTableStartX = 0 ;
m_iTableStartY = m_iHeaderHeight + 1 ;
int nTotalRows = m_VisibleItems . Count ( ) ;
int nRowsPerPage = GetRowsPerPage ( ) ;
// find the first visible item to display
int nStartItem = 0 ;
if ( nRowsPerPage < = nTotalRows )
{
nStartItem = m_vbar - > GetValue ( ) ;
}
int vbarInset = m_vbar - > IsVisible ( ) ? m_vbar - > GetWide ( ) : 0 ;
int maxw = wide - vbarInset - 8 ;
// debug timing functions
// double startTime, endTime;
// startTime = system()->GetCurrentTime();
// iterate through and draw each cell
bool bDone = false ;
int drawcount = 0 ;
for ( int i = nStartItem ; i < nTotalRows & & ! bDone ; i + + )
{
int x = 0 ;
if ( ! m_VisibleItems . IsValidIndex ( i ) )
continue ;
int itemID = m_VisibleItems [ i ] ;
// iterate the columns
for ( int j = 0 ; j < m_CurrentColumns . Count ( ) ; j + + )
{
Panel * header = m_ColumnsData [ m_CurrentColumns [ j ] ] . m_pHeader ;
Panel * render = GetCellRenderer ( itemID , j ) ;
if ( ! header - > IsVisible ( ) )
continue ;
int hWide = header - > GetWide ( ) ;
if ( render )
{
// setup render panel
if ( render - > GetVParent ( ) ! = GetVPanel ( ) )
{
render - > SetParent ( GetVPanel ( ) ) ;
}
if ( ! render - > IsVisible ( ) )
{
render - > SetVisible ( true ) ;
}
int xpos = x + m_iTableStartX + 2 ;
render - > SetPos ( xpos , ( drawcount * m_iRowHeight ) + m_iTableStartY ) ;
int right = min ( xpos + hWide , maxw ) ;
int usew = right - xpos ;
render - > SetSize ( usew , m_iRowHeight - 1 ) ;
// mark the panel to draw immediately (since it will probably be recycled to draw other cells)
render - > Repaint ( ) ;
surface ( ) - > SolveTraverse ( render - > GetVPanel ( ) ) ;
int x0 , y0 , x1 , y1 ;
render - > GetClipRect ( x0 , y0 , x1 , y1 ) ;
if ( ( y1 - y0 ) < ( m_iRowHeight - 3 ) )
{
bDone = true ;
break ;
}
surface ( ) - > PaintTraverse ( render - > GetVPanel ( ) ) ;
}
/*
// work in progress, optimized paint for text
else
{
// just paint it ourselves
char tempText [ 256 ] ;
// Grab cell text
GetCellText ( i , j , tempText , sizeof ( tempText ) ) ;
surface ( ) - > DrawSetTextPos ( x + m_iTableStartX + 2 , ( drawcount * m_iRowHeight ) + m_iTableStartY ) ;
for ( const char * pText = tempText ; * pText ! = 0 ; pText + + )
{
surface ( ) - > DrawUnicodeChar ( ( wchar_t ) * pText ) ;
}
}
*/
x + = hWide ;
}
drawcount + + ;
}
m_pLabel - > SetVisible ( false ) ;
// if the list is empty, draw some help text
if ( m_VisibleItems . Count ( ) < 1 & & m_pEmptyListText )
{
m_pEmptyListText - > SetPos ( m_iTableStartX + 8 , m_iTableStartY + 4 ) ;
m_pEmptyListText - > SetSize ( wide - 8 , m_iRowHeight ) ;
m_pEmptyListText - > Paint ( ) ;
}
// endTime = system()->GetCurrentTime();
// ivgui()->DPrintf2("ListPanel::Paint() (%.3f sec)\n", (float)(endTime - startTime));
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : PaintBackground ( )
{
BaseClass : : PaintBackground ( ) ;
}
//-----------------------------------------------------------------------------
// Handles multiselect
//-----------------------------------------------------------------------------
void ListPanel : : HandleMultiSelection ( int itemID , int row , int column )
{
// deal with 'multiple' row selection
// convert the last item selected to a row so we can multiply select by rows NOT items
int lastSelectedRow = ( m_LastItemSelected ! = - 1 ) ? m_VisibleItems . Find ( m_LastItemSelected ) : row ;
int startRow , endRow ;
if ( row < lastSelectedRow )
{
startRow = row ;
endRow = lastSelectedRow ;
}
else
{
startRow = lastSelectedRow ;
endRow = row ;
}
// clear the selection if neither control key was down - we are going to readd ALL selected items
// in case the user changed the 'direction' of the shift add
if ( ! input ( ) - > IsKeyDown ( KEY_LCONTROL ) & & ! input ( ) - > IsKeyDown ( KEY_RCONTROL ) )
{
ClearSelectedItems ( ) ;
}
// add any items that we haven't added
for ( int i = startRow ; i < = endRow ; i + + )
{
// get the item indexes for these rows
int selectedItemID = m_VisibleItems [ i ] ;
if ( ! m_SelectedItems . HasElement ( selectedItemID ) )
{
AddSelectedItem ( selectedItemID ) ;
}
}
}
//-----------------------------------------------------------------------------
// Handles multiselect
//-----------------------------------------------------------------------------
void ListPanel : : HandleAddSelection ( int itemID , int row , int column )
{
// dealing with row selection
if ( m_SelectedItems . HasElement ( itemID ) )
{
// this row is already selected, remove
m_SelectedItems . FindAndRemove ( itemID ) ;
PostActionSignal ( new KeyValues ( " ItemDeselected " ) ) ;
m_LastItemSelected = itemID ;
}
else
{
// add the row to the selection
AddSelectedItem ( itemID ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : UpdateSelection ( MouseCode code , int x , int y , int row , int column )
{
// make sure we're clicking on a real item
if ( row < 0 | | row > = m_VisibleItems . Count ( ) )
{
ClearSelectedItems ( ) ;
return ;
}
int itemID = m_VisibleItems [ row ] ;
// if we've right-clicked on a selection, don't change the selection
if ( code = = MOUSE_RIGHT & & m_SelectedItems . HasElement ( itemID ) )
return ;
if ( m_bCanSelectIndividualCells )
{
if ( input ( ) - > IsKeyDown ( KEY_LCONTROL ) | | input ( ) - > IsKeyDown ( KEY_RCONTROL ) )
{
// we're ctrl selecting the same cell, clear it
if ( ( m_LastItemSelected = = itemID ) & & ( m_iSelectedColumn = = column ) & & ( m_SelectedItems . Count ( ) = = 1 ) )
{
ClearSelectedItems ( ) ;
}
else
{
SetSelectedCell ( itemID , column ) ;
}
}
else
{
SetSelectedCell ( itemID , column ) ;
}
return ;
}
if ( ! m_bMultiselectEnabled )
{
SetSingleSelectedItem ( itemID ) ;
return ;
}
if ( input ( ) - > IsKeyDown ( KEY_LSHIFT ) | | input ( ) - > IsKeyDown ( KEY_RSHIFT ) )
{
// check for multi-select
HandleMultiSelection ( itemID , row , column ) ;
}
else if ( input ( ) - > IsKeyDown ( KEY_LCONTROL ) | | input ( ) - > IsKeyDown ( KEY_RCONTROL ) )
{
// check for row-add select
HandleAddSelection ( itemID , row , column ) ;
}
else
{
// no CTRL or SHIFT keys
// reset the selection Start point
// if ( ( m_LastItemSelected != itemID ) || ( m_SelectedItems.Count() > 1 ) )
{
SetSingleSelectedItem ( itemID ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : OnMousePressed ( MouseCode code )
{
if ( code = = MOUSE_LEFT | | code = = MOUSE_RIGHT )
{
if ( m_VisibleItems . Count ( ) > 0 )
{
// determine where we were pressed
int x , y , row , column ;
input ( ) - > GetCursorPos ( x , y ) ;
GetCellAtPos ( x , y , row , column ) ;
UpdateSelection ( code , x , y , row , column ) ;
}
// get the key focus
RequestFocus ( ) ;
}
// check for context menu open
if ( code = = MOUSE_RIGHT )
{
if ( m_SelectedItems . Count ( ) > 0 )
{
PostActionSignal ( new KeyValues ( " OpenContextMenu " , " itemID " , m_SelectedItems [ 0 ] ) ) ;
}
else
{
// post it, but with the invalid row
PostActionSignal ( new KeyValues ( " OpenContextMenu " , " itemID " , - 1 ) ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Scrolls the list according to the mouse wheel movement
//-----------------------------------------------------------------------------
void ListPanel : : OnMouseWheeled ( int delta )
{
if ( m_hEditModePanel . Get ( ) )
{
// ignore mouse wheel in edit mode, forward right up to parent
CallParentFunction ( new KeyValues ( " MouseWheeled " , " delta " , delta ) ) ;
return ;
}
int val = m_vbar - > GetValue ( ) ;
val - = ( delta * 3 ) ;
m_vbar - > SetValue ( val ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Double-click act like the the item under the mouse was selected
// and then the enter key hit
//-----------------------------------------------------------------------------
void ListPanel : : OnMouseDoublePressed ( MouseCode code )
{
if ( code = = MOUSE_LEFT )
{
// select the item
OnMousePressed ( code ) ;
// post up an enter key being hit if anything was selected
if ( GetSelectedItemsCount ( ) > 0 & & ! m_bIgnoreDoubleClick )
{
OnKeyCodeTyped ( KEY_ENTER ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
# ifdef _X360
void ListPanel : : OnKeyCodePressed ( KeyCode code )
{
int nTotalRows = m_VisibleItems . Count ( ) ;
int nTotalColumns = m_CurrentColumns . Count ( ) ;
if ( nTotalRows = = 0 )
return ;
// calculate info for adjusting scrolling
int nStartItem = GetStartItem ( ) ;
int nRowsPerPage = ( int ) GetRowsPerPage ( ) ;
int nSelectedRow = 0 ;
if ( m_DataItems . IsValidIndex ( m_LastItemSelected ) )
{
nSelectedRow = m_VisibleItems . Find ( m_LastItemSelected ) ;
}
int nSelectedColumn = m_iSelectedColumn ;
switch ( code )
{
case KEY_XBUTTON_UP :
case KEY_XSTICK1_UP :
case KEY_XSTICK2_UP :
if ( GetItemCount ( ) < 1 | | nSelectedRow = = nStartItem )
{
ClearSelectedItems ( ) ;
BaseClass : : OnKeyCodePressed ( code ) ;
return ;
}
else
{
nSelectedRow - = 1 ;
}
break ;
case KEY_XBUTTON_DOWN :
case KEY_XSTICK1_DOWN :
case KEY_XSTICK2_DOWN :
{
int itemId = GetSelectedItem ( 0 ) ;
if ( itemId ! = - 1 & & GetItemCurrentRow ( itemId ) = = ( nTotalRows - 1 ) )
{
ClearSelectedItems ( ) ;
BaseClass : : OnKeyCodePressed ( code ) ;
return ;
}
else
{
nSelectedRow + = 1 ;
}
}
break ;
case KEY_XBUTTON_LEFT :
case KEY_XSTICK1_LEFT :
case KEY_XSTICK2_LEFT :
if ( m_bCanSelectIndividualCells & & ( GetSelectedItemsCount ( ) = = 1 ) & & ( nSelectedColumn > = 0 ) )
{
nSelectedColumn - - ;
if ( nSelectedColumn < 0 )
{
nSelectedColumn = 0 ;
}
break ;
}
break ;
case KEY_XBUTTON_RIGHT :
case KEY_XSTICK1_RIGHT :
case KEY_XSTICK2_RIGHT :
if ( m_bCanSelectIndividualCells & & ( GetSelectedItemsCount ( ) = = 1 ) & & ( nSelectedColumn > = 0 ) )
{
nSelectedColumn + + ;
if ( nSelectedColumn > = nTotalColumns )
{
nSelectedColumn = nTotalColumns - 1 ;
}
break ;
}
break ;
case KEY_XBUTTON_A :
PostActionSignal ( new KeyValues ( " ListPanelItemChosen " , " itemID " , m_SelectedItems [ 0 ] ) ) ;
break ;
default :
BaseClass : : OnKeyCodePressed ( code ) ;
break ;
}
// make sure newly selected item is a valid range
nSelectedRow = clamp ( nSelectedRow , 0 , nTotalRows - 1 ) ;
int row = m_VisibleItems [ nSelectedRow ] ;
// This will select the cell if in single select mode, or the row in multiselect mode
if ( ( row ! = m_LastItemSelected ) | | ( nSelectedColumn ! = m_iSelectedColumn ) | | ( m_SelectedItems . Count ( ) > 1 ) )
{
SetSelectedCell ( row , nSelectedColumn ) ;
}
// move the newly selected item to within the visible range
if ( nRowsPerPage < nTotalRows )
{
int nStartItem = m_vbar - > GetValue ( ) ;
if ( nSelectedRow < nStartItem )
{
// move the list back to match
m_vbar - > SetValue ( nSelectedRow ) ;
}
else if ( nSelectedRow > = nStartItem + nRowsPerPage )
{
// move list forward to match
m_vbar - > SetValue ( nSelectedRow - nRowsPerPage + 1 ) ;
}
}
// redraw
InvalidateLayout ( ) ;
}
# else
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : OnKeyCodePressed ( KeyCode code )
{
if ( m_hEditModePanel . Get ( ) )
{
// ignore arrow keys in edit mode
// forward right up to parent so that tab focus change doesn't occur
CallParentFunction ( new KeyValues ( " KeyCodePressed " , " code " , code ) ) ;
return ;
}
int nTotalRows = m_VisibleItems . Count ( ) ;
int nTotalColumns = m_CurrentColumns . Count ( ) ;
if ( nTotalRows = = 0 )
{
BaseClass : : OnKeyCodePressed ( code ) ;
return ;
}
// calculate info for adjusting scrolling
int nStartItem = GetStartItem ( ) ;
int nRowsPerPage = ( int ) GetRowsPerPage ( ) ;
int nSelectedRow = 0 ;
if ( m_DataItems . IsValidIndex ( m_LastItemSelected ) )
{
nSelectedRow = m_VisibleItems . Find ( m_LastItemSelected ) ;
}
int nSelectedColumn = m_iSelectedColumn ;
switch ( code )
{
case KEY_HOME :
nSelectedRow = 0 ;
break ;
case KEY_END :
nSelectedRow = nTotalRows - 1 ;
break ;
case KEY_PAGEUP :
if ( nSelectedRow < = nStartItem )
{
// move up a page
nSelectedRow - = ( nRowsPerPage - 1 ) ;
}
else
{
// move to the top of the current page
nSelectedRow = nStartItem ;
}
break ;
case KEY_PAGEDOWN :
if ( nSelectedRow > = ( nStartItem + nRowsPerPage - 1 ) )
{
// move down a page
nSelectedRow + = ( nRowsPerPage - 1 ) ;
}
else
{
// move to the bottom of the current page
nSelectedRow = nStartItem + ( nRowsPerPage - 1 ) ;
}
break ;
case KEY_UP :
case KEY_XBUTTON_UP :
case KEY_XSTICK1_UP :
case KEY_XSTICK2_UP :
case STEAMCONTROLLER_DPAD_UP :
if ( nTotalRows > 0 )
{
nSelectedRow - - ;
break ;
}
// fall through
case KEY_DOWN :
case KEY_XBUTTON_DOWN :
case KEY_XSTICK1_DOWN :
case KEY_XSTICK2_DOWN :
case STEAMCONTROLLER_DPAD_DOWN :
if ( nTotalRows > 0 )
{
nSelectedRow + + ;
break ;
}
// fall through
case KEY_LEFT :
case KEY_XBUTTON_LEFT :
case KEY_XSTICK1_LEFT :
case KEY_XSTICK2_LEFT :
if ( m_bCanSelectIndividualCells & & ( GetSelectedItemsCount ( ) = = 1 ) & & ( nSelectedColumn > = 0 ) )
{
nSelectedColumn - - ;
if ( nSelectedColumn < 0 )
{
nSelectedColumn = 0 ;
}
break ;
}
// fall through
case KEY_RIGHT :
case KEY_XBUTTON_RIGHT :
case KEY_XSTICK1_RIGHT :
case KEY_XSTICK2_RIGHT :
if ( m_bCanSelectIndividualCells & & ( GetSelectedItemsCount ( ) = = 1 ) & & ( nSelectedColumn > = 0 ) )
{
nSelectedColumn + + ;
if ( nSelectedColumn > = nTotalColumns )
{
nSelectedColumn = nTotalColumns - 1 ;
}
break ;
}
// fall through
default :
// chain back
BaseClass : : OnKeyCodePressed ( code ) ;
return ;
} ;
// make sure newly selected item is a valid range
nSelectedRow = clamp ( nSelectedRow , 0 , nTotalRows - 1 ) ;
int row = m_VisibleItems [ nSelectedRow ] ;
// This will select the cell if in single select mode, or the row in multiselect mode
if ( ( row ! = m_LastItemSelected ) | | ( nSelectedColumn ! = m_iSelectedColumn ) | | ( m_SelectedItems . Count ( ) > 1 ) )
{
SetSelectedCell ( row , nSelectedColumn ) ;
}
// move the newly selected item to within the visible range
if ( nRowsPerPage < nTotalRows )
{
nStartItem = m_vbar - > GetValue ( ) ;
if ( nSelectedRow < nStartItem )
{
// move the list back to match
m_vbar - > SetValue ( nSelectedRow ) ;
}
else if ( nSelectedRow > = nStartItem + nRowsPerPage )
{
// move list forward to match
m_vbar - > SetValue ( nSelectedRow - nRowsPerPage + 1 ) ;
}
}
// redraw
InvalidateLayout ( ) ;
}
# endif
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
bool ListPanel : : GetCellBounds ( int row , int col , int & x , int & y , int & wide , int & tall )
{
if ( col < 0 | | col > = m_CurrentColumns . Count ( ) )
return false ;
if ( row < 0 | | row > = m_VisibleItems . Count ( ) )
return false ;
// Is row on screen?
int startitem = GetStartItem ( ) ;
if ( row < startitem | | row > = ( startitem + GetRowsPerPage ( ) ) )
return false ;
y = m_iTableStartY ;
y + = ( row - startitem ) * m_iRowHeight ;
tall = m_iRowHeight ;
// Compute column cell
x = m_iTableStartX ;
// walk columns
int c = 0 ;
while ( c < col )
{
x + = m_ColumnsData [ m_CurrentColumns [ c ] ] . m_pHeader - > GetWide ( ) ;
c + + ;
}
wide = m_ColumnsData [ m_CurrentColumns [ c ] ] . m_pHeader - > GetWide ( ) ;
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: returns true if any found, row and column are filled out
//-----------------------------------------------------------------------------
bool ListPanel : : GetCellAtPos ( int x , int y , int & row , int & col )
{
// convert to local
ScreenToLocal ( x , y ) ;
// move to Start of table
x - = m_iTableStartX ;
y - = m_iTableStartY ;
int startitem = GetStartItem ( ) ;
// make sure it's still in valid area
if ( x > = 0 & & y > = 0 )
{
// walk the rows (for when row height is independant each row)
// NOTE: if we do height independent rows, we will need to change GetCellBounds as well
for ( row = startitem ; row < m_VisibleItems . Count ( ) ; row + + )
{
if ( y < ( ( ( row - startitem ) + 1 ) * m_iRowHeight ) )
break ;
}
// walk columns
int startx = 0 ;
for ( col = 0 ; col < m_CurrentColumns . Count ( ) ; col + + )
{
startx + = m_ColumnsData [ m_CurrentColumns [ col ] ] . m_pHeader - > GetWide ( ) ;
if ( x < startx )
break ;
}
// make sure we're not out of range
if ( ! ( row = = m_VisibleItems . Count ( ) | | col = = m_CurrentColumns . Count ( ) ) )
{
return true ;
}
}
// out-of-bounds
row = col = - 1 ;
return false ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : ApplySchemeSettings ( IScheme * pScheme )
{
// force label to apply scheme settings now so we can override it
m_pLabel - > InvalidateLayout ( true ) ;
BaseClass : : ApplySchemeSettings ( pScheme ) ;
SetBgColor ( GetSchemeColor ( " ListPanel.BgColor " , pScheme ) ) ;
SetBorder ( pScheme - > GetBorder ( " ButtonDepressedBorder " ) ) ;
m_pLabel - > SetBgColor ( GetSchemeColor ( " ListPanel.BgColor " , pScheme ) ) ;
m_LabelFgColor = GetSchemeColor ( " ListPanel.TextColor " , pScheme ) ;
m_DisabledColor = GetSchemeColor ( " ListPanel.DisabledTextColor " , m_LabelFgColor , pScheme ) ;
m_SelectionFgColor = GetSchemeColor ( " ListPanel.SelectedTextColor " , m_LabelFgColor , pScheme ) ;
m_DisabledSelectionFgColor = GetSchemeColor ( " ListPanel.DisabledSelectedTextColor " , m_LabelFgColor , pScheme ) ;
m_pEmptyListText - > SetColor ( GetSchemeColor ( " ListPanel.EmptyListInfoTextColor " , pScheme ) ) ;
SetFont ( pScheme - > GetFont ( " Default " , IsProportional ( ) ) ) ;
m_pEmptyListText - > SetFont ( pScheme - > GetFont ( " Default " , IsProportional ( ) ) ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SetSortFunc ( int col , SortFunc * func )
{
Assert ( col < m_CurrentColumns . Count ( ) ) ;
unsigned char dataColumnIndex = m_CurrentColumns [ col ] ;
if ( ! m_ColumnsData [ dataColumnIndex ] . m_bTypeIsText & & func ! = NULL )
{
m_ColumnsData [ dataColumnIndex ] . m_pHeader - > SetMouseClickEnabled ( MOUSE_LEFT , 1 ) ;
}
m_ColumnsData [ dataColumnIndex ] . m_pSortFunc = func ;
// resort this column according to new sort func
ResortColumnRBTree ( col ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SetSortColumn ( int column )
{
m_iSortColumn = column ;
}
int ListPanel : : GetSortColumn ( ) const
{
return m_iSortColumn ;
}
void ListPanel : : SetSortColumnEx ( int iPrimarySortColumn , int iSecondarySortColumn , bool bSortAscending )
{
m_iSortColumn = iPrimarySortColumn ;
m_iSortColumnSecondary = iSecondarySortColumn ;
m_bSortAscending = bSortAscending ;
}
void ListPanel : : GetSortColumnEx ( int & iPrimarySortColumn , int & iSecondarySortColumn , bool & bSortAscending ) const
{
iPrimarySortColumn = m_iSortColumn ;
iSecondarySortColumn = m_iSortColumnSecondary ;
bSortAscending = m_bSortAscending ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SortList ( void )
{
m_bNeedsSort = false ;
if ( m_VisibleItems . Count ( ) < = 1 )
{
return ;
}
// check if the last selected item is on the screen - if so, we should try to maintain it on screen
int startItem = GetStartItem ( ) ;
int rowsperpage = ( int ) GetRowsPerPage ( ) ;
int screenPosition = - 1 ;
if ( m_LastItemSelected ! = - 1 & & m_SelectedItems . Count ( ) > 0 )
{
int selectedItemRow = m_VisibleItems . Find ( m_LastItemSelected ) ;
if ( selectedItemRow > = startItem & & selectedItemRow < = ( startItem + rowsperpage ) )
{
screenPosition = selectedItemRow - startItem ;
}
}
// get the required sorting functions
s_pCurrentSortingListPanel = this ;
// setup globals for use in qsort
s_pSortFunc = FastSortFunc ;
s_bSortAscending = m_bSortAscending ;
s_pSortFuncSecondary = FastSortFunc ;
s_bSortAscendingSecondary = m_bSortAscendingSecondary ;
// walk the tree and set up the current indices
if ( m_CurrentColumns . IsValidIndex ( m_iSortColumn ) )
{
IndexRBTree_t & rbtree = m_ColumnsData [ m_CurrentColumns [ m_iSortColumn ] ] . m_SortedTree ;
unsigned int index = rbtree . FirstInorder ( ) ;
unsigned int lastIndex = rbtree . LastInorder ( ) ;
int prevDuplicateIndex = 0 ;
int sortValue = 1 ;
while ( 1 )
{
FastSortListPanelItem * dataItem = ( FastSortListPanelItem * ) rbtree [ index ] . dataItem ;
if ( dataItem - > visible )
{
// only increment the sort value if we're a different token from the previous
if ( ! prevDuplicateIndex | | prevDuplicateIndex ! = rbtree [ index ] . duplicateIndex )
{
sortValue + + ;
}
dataItem - > primarySortIndexValue = sortValue ;
prevDuplicateIndex = rbtree [ index ] . duplicateIndex ;
}
if ( index = = lastIndex )
break ;
index = rbtree . NextInorder ( index ) ;
}
}
// setup secondary indices
if ( m_CurrentColumns . IsValidIndex ( m_iSortColumnSecondary ) )
{
IndexRBTree_t & rbtree = m_ColumnsData [ m_CurrentColumns [ m_iSortColumnSecondary ] ] . m_SortedTree ;
unsigned int index = rbtree . FirstInorder ( ) ;
unsigned int lastIndex = rbtree . LastInorder ( ) ;
int sortValue = 1 ;
int prevDuplicateIndex = 0 ;
while ( 1 )
{
FastSortListPanelItem * dataItem = ( FastSortListPanelItem * ) rbtree [ index ] . dataItem ;
if ( dataItem - > visible )
{
// only increment the sort value if we're a different token from the previous
if ( ! prevDuplicateIndex | | prevDuplicateIndex ! = rbtree [ index ] . duplicateIndex )
{
sortValue + + ;
}
dataItem - > secondarySortIndexValue = sortValue ;
prevDuplicateIndex = rbtree [ index ] . duplicateIndex ;
}
if ( index = = lastIndex )
break ;
index = rbtree . NextInorder ( index ) ;
}
}
// quick sort the list
qsort ( m_VisibleItems . Base ( ) , ( size_t ) m_VisibleItems . Count ( ) , ( size_t ) sizeof ( int ) , AscendingSortFunc ) ;
if ( screenPosition ! = - 1 )
{
int selectedItemRow = m_VisibleItems . Find ( m_LastItemSelected ) ;
// if we can put the last selected item in exactly the same spot, put it there, otherwise
// we need to be at the top of the list
if ( selectedItemRow > screenPosition )
{
m_vbar - > SetValue ( selectedItemRow - screenPosition ) ;
}
else
{
m_vbar - > SetValue ( 0 ) ;
}
}
InvalidateLayout ( ) ;
Repaint ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : SetFont ( HFont font )
{
Assert ( font ) ;
if ( ! font )
return ;
m_pTextImage - > SetFont ( font ) ;
m_iRowHeight = surface ( ) - > GetFontTall ( font ) + 2 ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void ListPanel : : OnSliderMoved ( )
{
InvalidateLayout ( ) ;
Repaint ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : deltax - deltas from current position
//-----------------------------------------------------------------------------
void ListPanel : : OnColumnResized ( int col , int delta )
{
m_iColumnDraggerMoved = col ;
column_t & column = m_ColumnsData [ m_CurrentColumns [ col ] ] ;
Panel * header = column . m_pHeader ;
int wide , tall ;
header - > GetSize ( wide , tall ) ;
wide + = delta ;
// enforce minimum sizes for the header
if ( wide < column . m_iMinWidth )
{
wide = column . m_iMinWidth ;
}
// enforce maximum sizes for the header
if ( wide > column . m_iMaxWidth )
{
wide = column . m_iMaxWidth ;
}
// make sure we have enough space for the columns to our right
int panelWide , panelTall ;
GetSize ( panelWide , panelTall ) ;
int x , y ;
header - > GetPos ( x , y ) ;
int restColumnsMinWidth = 0 ;
int i ;
for ( i = col + 1 ; i < m_CurrentColumns . Count ( ) ; i + + )
{
column_t & nextCol = m_ColumnsData [ m_CurrentColumns [ i ] ] ;
restColumnsMinWidth + = nextCol . m_iMinWidth ;
}
panelWide - = ( x + restColumnsMinWidth + m_vbar - > GetWide ( ) + WINDOW_BORDER_WIDTH ) ;
if ( wide > panelWide )
{
wide = panelWide ;
}
header - > SetSize ( wide , tall ) ;
// the adjacent header will be moved automatically in PerformLayout()
header - > InvalidateLayout ( ) ;
InvalidateLayout ( ) ;
Repaint ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: sets which column we should sort with
//-----------------------------------------------------------------------------
void ListPanel : : OnSetSortColumn ( int column )
{
// if it's the primary column already, flip the sort direction
if ( m_iSortColumn = = column )
{
m_bSortAscending = ! m_bSortAscending ;
}
else
{
// switching sort columns, keep the old one as the secondary sort
m_iSortColumnSecondary = m_iSortColumn ;
m_bSortAscendingSecondary = m_bSortAscending ;
}
SetSortColumn ( column ) ;
SortList ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: sets whether the item is visible or not
//-----------------------------------------------------------------------------
void ListPanel : : SetItemVisible ( int itemID , bool state )
{
if ( ! m_DataItems . IsValidIndex ( itemID ) )
return ;
FastSortListPanelItem * data = ( FastSortListPanelItem * ) m_DataItems [ itemID ] ;
if ( data - > visible = = state )
return ;
m_bNeedsSort = true ;
data - > visible = state ;
if ( data - > visible )
{
// add back to end of list
m_VisibleItems . AddToTail ( itemID ) ;
}
else
{
// remove from selection if it is there.
if ( m_SelectedItems . HasElement ( itemID ) )
{
m_SelectedItems . FindAndRemove ( itemID ) ;
PostActionSignal ( new KeyValues ( " ItemDeselected " ) ) ;
}
// remove from data
m_VisibleItems . FindAndRemove ( itemID ) ;
InvalidateLayout ( ) ;
}
}
//-----------------------------------------------------------------------------
// Is the item visible?
//-----------------------------------------------------------------------------
bool ListPanel : : IsItemVisible ( int itemID )
{
if ( ! m_DataItems . IsValidIndex ( itemID ) )
return false ;
FastSortListPanelItem * data = ( FastSortListPanelItem * ) m_DataItems [ itemID ] ;
return data - > visible ;
}
//-----------------------------------------------------------------------------
// Purpose: sets whether the item is disabled or not (effects item color)
//-----------------------------------------------------------------------------
void ListPanel : : SetItemDisabled ( int itemID , bool state )
{
if ( ! m_DataItems . IsValidIndex ( itemID ) )
return ;
m_DataItems [ itemID ] - > kv - > SetInt ( " disabled " , state ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Calculate number of rows per page
//-----------------------------------------------------------------------------
float ListPanel : : GetRowsPerPage ( )
{
float rowsperpage = ( float ) ( GetTall ( ) - m_iHeaderHeight ) / ( float ) m_iRowHeight ;
return rowsperpage ;
}
//-----------------------------------------------------------------------------
// Purpose: Calculate the item we should Start on
//-----------------------------------------------------------------------------
int ListPanel : : GetStartItem ( )
{
// if rowsperpage < total number of rows
if ( GetRowsPerPage ( ) < ( float ) m_VisibleItems . Count ( ) )
{
return m_vbar - > GetValue ( ) ;
}
return 0 ; // otherwise Start at top
}
//-----------------------------------------------------------------------------
// Purpose: whether or not to select specific cells (off by default)
//-----------------------------------------------------------------------------
void ListPanel : : SetSelectIndividualCells ( bool state )
{
m_bCanSelectIndividualCells = state ;
}
//-----------------------------------------------------------------------------
// whether or not multiple cells/rows can be selected
//-----------------------------------------------------------------------------
void ListPanel : : SetMultiselectEnabled ( bool bState )
{
m_bMultiselectEnabled = bState ;
}
bool ListPanel : : IsMultiselectEnabled ( ) const
{
return m_bMultiselectEnabled ;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the text which is displayed when the list is empty
//-----------------------------------------------------------------------------
void ListPanel : : SetEmptyListText ( const char * text )
{
m_pEmptyListText - > SetText ( text ) ;
Repaint ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Sets the text which is displayed when the list is empty
//-----------------------------------------------------------------------------
void ListPanel : : SetEmptyListText ( const wchar_t * text )
{
m_pEmptyListText - > SetText ( text ) ;
Repaint ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: opens the content menu
//-----------------------------------------------------------------------------
void ListPanel : : OpenColumnChoiceMenu ( )
{
if ( ! m_bAllowUserAddDeleteColumns )
return ;
Menu * menu = new Menu ( this , " ContextMenu " ) ;
int x , y ;
input ( ) - > GetCursorPos ( x , y ) ;
menu - > SetPos ( x , y ) ;
// add all the column choices to the menu
for ( int i = 0 ; i < m_CurrentColumns . Count ( ) ; i + + )
{
column_t & column = m_ColumnsData [ m_CurrentColumns [ i ] ] ;
char name [ 128 ] ;
column . m_pHeader - > GetText ( name , sizeof ( name ) ) ;
int itemID = menu - > AddCheckableMenuItem ( name , new KeyValues ( " ToggleColumnVisible " , " col " , m_CurrentColumns [ i ] ) , this ) ;
menu - > SetMenuItemChecked ( itemID , ! column . m_bHidden ) ;
if ( column . m_bUnhidable )
{
menu - > SetItemEnabled ( itemID , false ) ;
}
}
menu - > SetVisible ( true ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Resizes a column
//-----------------------------------------------------------------------------
void ListPanel : : ResizeColumnToContents ( int column )
{
// iterate all the items in the column, getting the size of each
column_t & col = m_ColumnsData [ m_CurrentColumns [ column ] ] ;
if ( ! col . m_bTypeIsText )
return ;
// start with the size of the column text
int wide = 0 , minRequiredWidth = 0 , tall = 0 ;
col . m_pHeader - > GetContentSize ( minRequiredWidth , tall ) ;
// iterate every item
for ( int i = 0 ; i < m_VisibleItems . Count ( ) ; i + + )
{
if ( ! m_VisibleItems . IsValidIndex ( i ) )
continue ;
// get the cell
int itemID = m_VisibleItems [ i ] ;
// get the text
wchar_t tempText [ 256 ] ;
GetCellText ( itemID , column , tempText , 256 ) ;
m_pTextImage - > SetText ( tempText ) ;
m_pTextImage - > GetContentSize ( wide , tall ) ;
if ( wide > minRequiredWidth )
{
minRequiredWidth = wide ;
}
}
// Introduce a slight buffer between columns
minRequiredWidth + = 4 ;
// call the resize
col . m_pHeader - > GetSize ( wide , tall ) ;
OnColumnResized ( column , minRequiredWidth - wide ) ;
}
//-----------------------------------------------------------------------------
// Purpose: Changes the visibilty of a column
//-----------------------------------------------------------------------------
void ListPanel : : OnToggleColumnVisible ( int col )
{
if ( ! m_CurrentColumns . IsValidIndex ( col ) )
return ;
// toggle the state of the column
column_t & column = m_ColumnsData [ m_CurrentColumns [ col ] ] ;
SetColumnVisible ( col , column . m_bHidden ) ;
}
//-----------------------------------------------------------------------------
// Purpose: sets user settings
//-----------------------------------------------------------------------------
void ListPanel : : ApplyUserConfigSettings ( KeyValues * userConfig )
{
// Check for version mismatch, then don't load settings. (Just revert to the defaults.)
int version = userConfig - > GetInt ( " configVersion " , 1 ) ;
if ( version ! = m_nUserConfigFileVersion )
{
return ;
}
// We save/restore m_lastBarWidth because all of the column widths are saved relative to that size.
// If we don't save it, you can run into this case:
// - Window width is 500, load sizes setup relative to a 1000-width window
// - Set window size to 1000
// - In PerformLayout, it thinks the window has grown by 500 (since m_lastBarWidth is 500 and new window width is 1000)
// so it pushes out any COLUMN_RESIZEWITHWINDOW columns to their max extent and shrinks everything else to its min extent.
m_lastBarWidth = userConfig - > GetInt ( " lastBarWidth " , 0 ) ;
// read which columns are hidden
for ( int i = 0 ; i < m_CurrentColumns . Count ( ) ; i + + )
{
char name [ 64 ] ;
_snprintf ( name , sizeof ( name ) , " %d_hidden " , i ) ;
int hidden = userConfig - > GetInt ( name , - 1 ) ;
if ( hidden = = 0 )
{
SetColumnVisible ( i , true ) ;
}
else if ( hidden = = 1 )
{
SetColumnVisible ( i , false ) ;
}
_snprintf ( name , sizeof ( name ) , " %d_width " , i ) ;
int nWidth = userConfig - > GetInt ( name , - 1 ) ;
if ( nWidth > = 0 )
{
column_t & column = m_ColumnsData [ m_CurrentColumns [ i ] ] ;
column . m_pHeader - > SetWide ( nWidth ) ;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: returns user config settings for this control
//-----------------------------------------------------------------------------
void ListPanel : : GetUserConfigSettings ( KeyValues * userConfig )
{
if ( m_nUserConfigFileVersion ! = 1 )
{
userConfig - > SetInt ( " configVersion " , m_nUserConfigFileVersion ) ;
}
userConfig - > SetInt ( " lastBarWidth " , m_lastBarWidth ) ;
// save which columns are hidden
for ( int i = 0 ; i < m_CurrentColumns . Count ( ) ; i + + )
{
column_t & column = m_ColumnsData [ m_CurrentColumns [ i ] ] ;
char name [ 64 ] ;
_snprintf ( name , sizeof ( name ) , " %d_hidden " , i ) ;
userConfig - > SetInt ( name , column . m_bHidden ? 1 : 0 ) ;
_snprintf ( name , sizeof ( name ) , " %d_width " , i ) ;
userConfig - > SetInt ( name , column . m_pHeader - > GetWide ( ) ) ;
}
}
//-----------------------------------------------------------------------------
// Purpose: optimization, return true if this control has any user config settings
//-----------------------------------------------------------------------------
bool ListPanel : : HasUserConfigSettings ( )
{
return true ;
}
//-----------------------------------------------------------------------------
// Purpose: data accessor
//-----------------------------------------------------------------------------
void ListPanel : : SetAllowUserModificationOfColumns ( bool allowed )
{
m_bAllowUserAddDeleteColumns = allowed ;
}
void ListPanel : : SetIgnoreDoubleClick ( bool state )
{
m_bIgnoreDoubleClick = state ;
}
//-----------------------------------------------------------------------------
// Purpose: set up a field for editing
//-----------------------------------------------------------------------------
void ListPanel : : EnterEditMode ( int itemID , int column , vgui : : Panel * editPanel )
{
m_hEditModePanel = editPanel ;
m_iEditModeItemID = itemID ;
m_iEditModeColumn = column ;
editPanel - > SetParent ( this ) ;
editPanel - > SetVisible ( true ) ;
editPanel - > RequestFocus ( ) ;
editPanel - > MoveToFront ( ) ;
InvalidateLayout ( ) ;
}
//-----------------------------------------------------------------------------
// Purpose: leaves editing mode
//-----------------------------------------------------------------------------
void ListPanel : : LeaveEditMode ( )
{
if ( m_hEditModePanel . Get ( ) )
{
m_hEditModePanel - > SetVisible ( false ) ;
m_hEditModePanel - > SetParent ( ( Panel * ) NULL ) ;
m_hEditModePanel = NULL ;
}
}
//-----------------------------------------------------------------------------
// Purpose: returns true if we are currently in inline editing mode
//-----------------------------------------------------------------------------
bool ListPanel : : IsInEditMode ( )
{
return ( m_hEditModePanel . Get ( ) ! = NULL ) ;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
# ifdef _X360
void ListPanel : : NavigateTo ( )
{
BaseClass : : NavigateTo ( ) ;
// attempt to select the first item in the list when we get focus
if ( GetItemCount ( ) )
{
SetSingleSelectedItem ( FirstItem ( ) ) ;
}
else // if we have no items, change focus
{
if ( ! NavigateDown ( ) )
{
NavigateUp ( ) ;
}
}
}
# endif