hl2_src-leak-2017/src/vgui2/vgui_controls/SectionedListPanel.cpp

2272 lines
64 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose:
//
// $NoKeywords: $
//=============================================================================//
#include <stdio.h>
#include <vgui/IInput.h>
#include <vgui/IPanel.h>
#include <vgui/ILocalize.h>
#include <vgui/IScheme.h>
#include <vgui/ISurface.h>
#include <KeyValues.h>
#include <vgui/MouseCode.h>
#include <vgui_controls/SectionedListPanel.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/Controls.h>
#include <vgui_controls/Label.h>
#include <vgui_controls/ScrollBar.h>
#include <vgui_controls/TextImage.h>
#include <vgui_controls/ImageList.h>
#include "utlvector.h"
// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>
using namespace vgui;
namespace vgui
{
//-----------------------------------------------------------------------------
// Purpose: header label that separates and names each section
//-----------------------------------------------------------------------------
SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const char *name, int sectionID) : Label(parent, name, "")
{
m_pListPanel = parent;
m_iSectionID = sectionID;
SetTextImageIndex(-1);
ClearImages();
SetPaintBackgroundEnabled( false );
m_bDrawDividerBar = true;
}
SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const wchar_t *name, int sectionID) : Label(parent, "SectionHeader", "")
{
SetText(name);
SetVisible(false);
m_pListPanel = parent;
m_iSectionID = sectionID;
SetTextImageIndex(-1);
ClearImages();
m_bDrawDividerBar = true;
}
void SectionedListPanelHeader::ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
SetFgColor(GetSchemeColor("SectionedListPanel.HeaderTextColor", pScheme));
m_SectionDividerColor = GetSchemeColor("SectionedListPanel.DividerColor", pScheme);
SetBgColor(GetSchemeColor("SectionedListPanelHeader.BgColor", GetBgColor(), pScheme));
SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional()));
ClearImages();
HFont hFont = m_pListPanel->GetHeaderFont();
if ( hFont != INVALID_FONT )
{
SetFont( hFont );
}
else
{
SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional()));
}
}
void SectionedListPanelHeader::Paint()
{
BaseClass::Paint();
if ( m_bDrawDividerBar )
{
int x, y, wide, tall;
GetBounds(x, y, wide, tall);
y = (tall - 2); // draw the line under the panel
surface()->DrawSetColor(m_SectionDividerColor);
surface()->DrawFilledRect(1, y, GetWide() - 2, y + 1);
}
}
void SectionedListPanelHeader::SetColor(Color col)
{
m_SectionDividerColor = col;
SetFgColor(col);
}
void SectionedListPanelHeader::SetDividerColor(Color col )
{
m_SectionDividerColor = col;
}
void SectionedListPanelHeader::PerformLayout()
{
BaseClass::PerformLayout();
// set up the text in the header
int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
if (colCount != GetImageCount())
{
// rebuild the image list
for (int i = 0; i < colCount; i++)
{
int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
IImage *image = NULL;
if (columnFlags & SectionedListPanel::HEADER_IMAGE)
{
//!! need some kind of image reference
image = NULL;
}
else
{
TextImage *textImage = new TextImage("");
textImage->SetFont(GetFont());
HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i );
if ( INVALID_FONT != fallback )
{
textImage->SetUseFallbackFont( true, fallback );
}
textImage->SetColor(GetFgColor());
image = textImage;
}
SetImageAtIndex(i, image, 0);
}
}
for (int repeat = 0; repeat <= 1; repeat++)
{
int xpos = 0;
for (int i = 0; i < colCount; i++)
{
int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
int columnWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
int maxWidth = columnWidth;
IImage *image = GetImageAtIndex(i);
if (!image)
{
xpos += columnWidth;
continue;
}
// set the image position within the label
int contentWide, wide, tall;
image->GetContentSize(wide, tall);
contentWide = wide;
// see if we can draw over the next few column headers (if we're left-aligned)
if (!(columnFlags & SectionedListPanel::COLUMN_RIGHT))
{
for (int j = i + 1; j < colCount; j++)
{
// see if this column header has anything for a header
int iwide = 0, itall = 0;
if (GetImageAtIndex(j))
{
GetImageAtIndex(j)->GetContentSize(iwide, itall);
}
if (iwide == 0)
{
// it's a blank header, ok to draw over it
maxWidth += m_pListPanel->GetColumnWidthBySection(m_iSectionID, j);
}
}
}
if (maxWidth >= 0)
{
wide = maxWidth;
}
if (columnFlags & SectionedListPanel::COLUMN_RIGHT)
{
SetImageBounds(i, xpos + wide - contentWide, wide - SectionedListPanel::COLUMN_DATA_GAP);
}
else
{
SetImageBounds(i, xpos, wide - SectionedListPanel::COLUMN_DATA_GAP);
}
xpos += columnWidth;
if (!(columnFlags & SectionedListPanel::HEADER_IMAGE))
{
Assert(dynamic_cast<TextImage *>(image) != NULL);
TextImage *textImage = (TextImage *)image;
textImage->SetFont(GetFont());
textImage->SetText(m_pListPanel->GetColumnTextBySection(m_iSectionID, i));
textImage->ResizeImageToContentMaxWidth( maxWidth );
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Individual items in the list
//-----------------------------------------------------------------------------
class CItemButton : public Label
{
DECLARE_CLASS_SIMPLE( CItemButton, Label );
public:
CItemButton(SectionedListPanel *parent, int itemID) : Label(parent, NULL, "< item >")
{
m_pListPanel = parent;
m_iID = itemID;
m_pData = NULL;
Clear();
m_nHorizFillInset = 0;
}
~CItemButton()
{
// free all the keyvalues
if (m_pData)
{
m_pData->deleteThis();
}
// clear any section data
SetSectionID(-1);
}
void Clear()
{
m_bSelected = false;
m_bOverrideColors = false;
m_iSectionID = -1;
SetPaintBackgroundEnabled( false );
SetTextImageIndex(-1);
ClearImages();
}
int GetID()
{
return m_iID;
}
void SetID(int itemID)
{
m_iID = itemID;
}
int GetSectionID()
{
return m_iSectionID;
}
void SetSectionID(int sectionID)
{
if (sectionID != m_iSectionID)
{
// free any existing textimage list
ClearImages();
// delete any images we've created
for (int i = 0; i < m_TextImages.Count(); i++)
{
delete m_TextImages[i];
}
m_TextImages.RemoveAll();
// mark the list as needing rebuilding
InvalidateLayout();
}
m_iSectionID = sectionID;
}
void SetData(const KeyValues *data)
{
if (m_pData)
{
m_pData->deleteThis();
}
m_pData = data->MakeCopy();
InvalidateLayout();
}
KeyValues *GetData()
{
return m_pData;
}
virtual void PerformLayout()
{
// get our button text
int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
if (!m_pData || colCount < 1)
{
SetText("< unset >");
}
else
{
if (colCount != GetImageCount())
{
// rebuild the image list
for (int i = 0; i < colCount; i++)
{
int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
if (!(columnFlags & SectionedListPanel::COLUMN_IMAGE))
{
TextImage *image = new TextImage("");
m_TextImages.AddToTail(image);
image->SetFont( GetFont() );
HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i );
if ( INVALID_FONT != fallback )
{
image->SetUseFallbackFont( true, fallback );
}
SetImageAtIndex(i, image, 0);
}
}
{for ( int i = GetImageCount(); i < colCount; i++ ) // make sure we have enough image slots
{
AddImage( NULL, 0 );
}}
}
// set the text for each column
int xpos = 0;
for (int i = 0; i < colCount; i++)
{
const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i);
int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
IImage *image = NULL;
if (columnFlags & SectionedListPanel::COLUMN_IMAGE)
{
// lookup which image is being referred to
if (m_pListPanel->m_pImageList)
{
int imageIndex = m_pData->GetInt(keyname, 0);
if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex))
{
// 0 is always the blank image
if (imageIndex > 0)
{
image = m_pListPanel->m_pImageList->GetImage(imageIndex);
SetImageAtIndex(i, image, 0);
}
}
else
{
// this is mildly valid (CGamesList hits it because of the way it uses the image indices)
// Assert(!("Image index out of range for ImageList in SectionedListPanel"));
}
}
else
{
Assert(!("Images columns used in SectionedListPanel with no ImageList set"));
}
}
else
{
TextImage *textImage = dynamic_cast<TextImage *>(GetImageAtIndex(i));
if (textImage)
{
const wchar* pwszOverride = m_pData->GetWString( keyname, NULL );
if ( pwszOverride && pwszOverride[0] != '#' )
{
textImage->SetText( pwszOverride );
}
else
{
textImage->SetText(m_pData->GetString(keyname, ""));
}
textImage->ResizeImageToContentMaxWidth( maxWidth );
// set the text color based on the selection state - if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
VPANEL focus = input()->GetFocus();
if ( !m_bOverrideColors )
{
if (IsSelected() && !m_pListPanel->IsInEditMode())
{
if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
{
textImage->SetColor(m_ArmedFgColor2);
}
else
{
textImage->SetColor(m_OutOfFocusSelectedTextColor);
}
}
else if (columnFlags & SectionedListPanel::COLUMN_BRIGHT)
{
textImage->SetColor(m_ArmedFgColor1);
}
else
{
textImage->SetColor(m_FgColor2);
}
}
else
{
// custom colors
if (IsSelected() && (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))))
{
textImage->SetColor(m_ArmedFgColor2);
}
else
{
Color *clrOverride = m_pListPanel->GetColorOverrideForCell(m_iSectionID, m_iID, i);
textImage->SetColor( (clrOverride != NULL) ? *clrOverride : GetFgColor());
}
}
}
image = textImage;
}
// set the image position within the label
int imageWide = 0, tall = 0;
int wide;
if (image)
{
image->GetContentSize(imageWide, tall);
}
if (maxWidth >= 0)
{
wide = maxWidth;
}
else
{
wide = imageWide;
}
if (i == 0 && !(columnFlags & SectionedListPanel::COLUMN_IMAGE))
{
// first column has an extra indent
SetImageBounds(i, xpos + SectionedListPanel::COLUMN_DATA_INDENT, wide - (SectionedListPanel::COLUMN_DATA_INDENT + SectionedListPanel::COLUMN_DATA_GAP));
}
else
{
if (columnFlags & SectionedListPanel::COLUMN_CENTER)
{
int offSet = (wide / 2) - (imageWide / 2);
SetImageBounds(i, xpos + offSet, wide - offSet - SectionedListPanel::COLUMN_DATA_GAP);
}
else if (columnFlags & SectionedListPanel::COLUMN_RIGHT)
{
SetImageBounds(i, xpos + wide - imageWide, wide - SectionedListPanel::COLUMN_DATA_GAP);
}
else
{
SetImageBounds(i, xpos, wide - SectionedListPanel::COLUMN_DATA_GAP);
}
}
xpos += wide;
}
}
BaseClass::PerformLayout();
}
virtual void ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
m_ArmedFgColor1 = GetSchemeColor("SectionedListPanel.BrightTextColor", pScheme);
m_ArmedFgColor2 = GetSchemeColor("SectionedListPanel.SelectedTextColor", pScheme);
m_OutOfFocusSelectedTextColor = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedTextColor", pScheme);
m_ArmedBgColor = GetSchemeColor("SectionedListPanel.SelectedBgColor", pScheme);
m_FgColor2 = GetSchemeColor("SectionedListPanel.TextColor", pScheme);
m_BgColor = GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme);
m_SelectionBG2Color = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedBgColor", pScheme);
HFont hFont = m_pListPanel->GetRowFont();
if ( hFont != INVALID_FONT )
{
SetFont( hFont );
}
else
{
const char *fontName = pScheme->GetResourceString( "SectionedListPanel.Font" );
HFont font = pScheme->GetFont(fontName, IsProportional());
if ( font != INVALID_FONT )
{
SetFont( font );
}
}
ClearImages();
}
virtual void PaintBackground()
{
int wide, tall;
GetSize(wide, tall);
if (IsSelected() && !m_pListPanel->IsInEditMode())
{
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())))
{
surface()->DrawSetColor(m_ArmedBgColor);
}
else
{
surface()->DrawSetColor(m_SelectionBG2Color);
}
}
else
{
surface()->DrawSetColor(GetBgColor());
}
surface()->DrawFilledRect(0, m_nHorizFillInset, wide, tall - m_nHorizFillInset);
}
virtual void Paint()
{
BaseClass::Paint();
if ( !m_bShowColumns )
return;
// Debugging code to show column widths
int wide, tall;
GetSize(wide, tall);
surface()->DrawSetColor( 255,255,255,255 );
surface()->DrawOutlinedRect(0, 0, wide, tall);
int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
if (m_pData && colCount >= 0)
{
int xpos = 0;
for (int i = 0; i < colCount; i++)
{
const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i);
int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
IImage *image = NULL;
if (columnFlags & SectionedListPanel::COLUMN_IMAGE)
{
// lookup which image is being referred to
if (m_pListPanel->m_pImageList)
{
int imageIndex = m_pData->GetInt(keyname, 0);
if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex))
{
if (imageIndex > 0)
{
image = m_pListPanel->m_pImageList->GetImage(imageIndex);
}
}
}
}
else
{
image = GetImageAtIndex(i);
}
int imageWide = 0;
if (image)
{
image->GetContentSize(imageWide, tall);
}
if (maxWidth >= 0)
{
wide = maxWidth;
}
else
{
wide = imageWide;
}
xpos += wide;//max(maxWidth,wide);
surface()->DrawOutlinedRect( xpos, 0, xpos, GetTall() );
}
}
}
virtual void OnMousePressed(MouseCode code)
{
if ( m_pListPanel && m_pListPanel->IsClickable() && IsEnabled() )
{
if (code == MOUSE_LEFT)
{
m_pListPanel->PostActionSignal(new KeyValues("ItemLeftClick", "itemID", m_iID));
}
if (code == MOUSE_RIGHT)
{
KeyValues *msg = new KeyValues("ItemContextMenu", "itemID", m_iID);
msg->SetPtr("SubPanel", this);
m_pListPanel->PostActionSignal(msg);
}
m_pListPanel->SetSelectedItem(this);
}
}
void SetSelected(bool state)
{
if (m_bSelected != state)
{
if (state)
{
RequestFocus();
}
m_bSelected = state;
SetPaintBackgroundEnabled( state );
InvalidateLayout();
Repaint();
}
}
bool IsSelected()
{
return m_bSelected;
}
virtual void OnSetFocus()
{
InvalidateLayout(); // force the layout to be redone so we can change text color according to focus
BaseClass::OnSetFocus();
}
virtual void OnKillFocus()
{
InvalidateLayout(); // force the layout to be redone so we can change text color according to focus
BaseClass::OnSetFocus();
}
virtual void OnMouseDoublePressed(MouseCode code)
{
//=============================================================================
// HPE_BEGIN:
// [tj] Only do this if clicking is enabled.
//=============================================================================
if (m_pListPanel && m_pListPanel->IsClickable())
{
if (code == MOUSE_LEFT)
{
m_pListPanel->PostActionSignal(new KeyValues("ItemDoubleLeftClick", "itemID", m_iID));
// post up an enter key being hit
m_pListPanel->OnKeyCodeTyped(KEY_ENTER);
}
else
{
OnMousePressed(code);
}
m_pListPanel->SetSelectedItem(this);
}
//=============================================================================
// HPE_END
//=============================================================================
}
void GetCellBounds(int column, int &xpos, int &columnWide)
{
xpos = 0, columnWide = 0;
int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
for (int i = 0; i < colCount; i++)
{
int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
IImage *image = GetImageAtIndex(i);
if (!image)
continue;
// set the image position within the label
int wide, tall;
image->GetContentSize(wide, tall);
if (maxWidth >= 0)
{
wide = maxWidth;
}
if (i == column)
{
// found the cell size, bail
columnWide = wide;
return;
}
xpos += wide;
}
}
//=============================================================================
// HPE_BEGIN:
// [menglish] gets the local coordinates of a cell using the max width for every column
//=============================================================================
void GetMaxCellBounds(int column, int &xpos, int &columnWide)
{
xpos = 0, columnWide = 0;
int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
for (int i = 0; i < colCount; i++)
{
int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
if (i == column)
{
// found the cell size, bail
columnWide = maxWidth;
return;
}
xpos += maxWidth;
}
}
//=============================================================================
// HPE_END
//=============================================================================
virtual void SetOverrideColors( bool state )
{
m_bOverrideColors = state;
}
void SetShowColumns( bool bShow )
{
m_bShowColumns = bShow;
}
void SetItemBgHorizFillInset( int nHorizFillInset ){ m_nHorizFillInset = nHorizFillInset; }
private:
SectionedListPanel *m_pListPanel;
int m_iID;
int m_iSectionID;
KeyValues *m_pData;
Color m_FgColor2;
Color m_BgColor;
Color m_ArmedFgColor1;
Color m_ArmedFgColor2;
Color m_OutOfFocusSelectedTextColor;
Color m_ArmedBgColor;
Color m_SelectionBG2Color;
CUtlVector<vgui::TextImage *> m_TextImages;
bool m_bSelected;
bool m_bOverrideColors;
bool m_bShowColumns;
int m_nHorizFillInset;
};
}; // namespace vgui
DECLARE_BUILD_FACTORY( SectionedListPanel );
//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
SectionedListPanel::SectionedListPanel(vgui::Panel *parent, const char *name) : BaseClass(parent, name)
{
m_pScrollBar = new ScrollBar(this, "SectionedScrollBar", true);
m_pScrollBar->SetVisible(false);
m_pScrollBar->AddActionSignalTarget(this);
m_iEditModeItemID = 0;
m_iEditModeColumn = 0;
m_bSortNeeded = false;
m_bVerticalScrollbarEnabled = true;
m_iLineSpacing = DEFAULT_LINE_SPACING;
m_iLineGap = 0;
m_iSectionGap = DEFAULT_SECTION_GAP;
m_pImageList = NULL;
m_bDeleteImageListWhenDone = false;
m_hHeaderFont = INVALID_FONT;
m_hRowFont = INVALID_FONT;
//=============================================================================
// HPE_BEGIN:
//=============================================================================
// [tj] Default clickability to true so existing controls aren't affected.
m_clickable = true;
// [tj] draw section headers by default so existing controls aren't affected.
m_bDrawSectionHeaders = true;
//=============================================================================
// HPE_END
//=============================================================================
}
//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
SectionedListPanel::~SectionedListPanel()
{
}
//-----------------------------------------------------------------------------
// Purpose: Sorts the list
//-----------------------------------------------------------------------------
void SectionedListPanel::ReSortList()
{
m_SortedItems.RemoveAll();
int sectionStart = 0;
// layout the buttons
for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++)
{
section_t &section = m_Sections[sectionIndex];
sectionStart = m_SortedItems.Count();
// find all the items in this section
for( int i = m_Items.Head(); i != m_Items.InvalidIndex(); i = m_Items.Next( i ) )
{
if (m_Items[i]->GetSectionID() == m_Sections[sectionIndex].m_iID)
{
// insert the items sorted
if (section.m_pSortFunc)
{
int insertionPoint = sectionStart;
for (;insertionPoint < m_SortedItems.Count(); insertionPoint++)
{
if (section.m_pSortFunc(this, i, m_SortedItems[insertionPoint]->GetID()))
break;
}
if (insertionPoint == m_SortedItems.Count())
{
m_SortedItems.AddToTail(m_Items[i]);
}
else
{
m_SortedItems.InsertBefore(insertionPoint, m_Items[i]);
}
}
else
{
// just add to the end
m_SortedItems.AddToTail(m_Items[i]);
}
}
}
}
}
//-----------------------------------------------------------------------------
// Purpose: iterates through and sets up the position of all the sections and items
//-----------------------------------------------------------------------------
void SectionedListPanel::PerformLayout()
{
// lazy resort the list
if (m_bSortNeeded)
{
ReSortList();
m_bSortNeeded = false;
}
BaseClass::PerformLayout();
LayoutPanels(m_iContentHeight);
int cx, cy, cwide, ctall;
GetBounds(cx, cy, cwide, ctall);
if (m_iContentHeight > ctall && m_bVerticalScrollbarEnabled)
{
m_pScrollBar->SetVisible(true);
m_pScrollBar->MoveToFront();
m_pScrollBar->SetPos(cwide - m_pScrollBar->GetWide() - 2, 0);
m_pScrollBar->SetSize(m_pScrollBar->GetWide(), ctall - 2);
m_pScrollBar->SetRangeWindow(ctall);
m_pScrollBar->SetRange(0, m_iContentHeight);
m_pScrollBar->InvalidateLayout();
m_pScrollBar->Repaint();
// since we're just about to make the scrollbar visible, we need to re-layout
// the buttons since they depend on the scrollbar size
LayoutPanels(m_iContentHeight);
}
else
{
m_pScrollBar->SetValue(0);
bool bWasVisible = m_pScrollBar->IsVisible();
m_pScrollBar->SetVisible(false);
// When we hide the scrollbar, we need to layout the buttons because they'll have more width to work with
if ( bWasVisible )
{
LayoutPanels(m_iContentHeight);
}
}
}
//-----------------------------------------------------------------------------
// Purpose: lays out the sections and rows in the panel
//-----------------------------------------------------------------------------
void SectionedListPanel::LayoutPanels(int &contentTall)
{
int tall = GetSectionTall();
int x = 5, wide = GetWide() - 10;
int y = 5;
if (m_pScrollBar->IsVisible())
{
y -= m_pScrollBar->GetValue();
wide -= m_pScrollBar->GetWide();
}
int iStart = -1;
int iEnd = -1;
// layout the buttons
bool bFirstVisibleSection = true;
for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++)
{
section_t &section = m_Sections[sectionIndex];
iStart = -1;
iEnd = -1;
for (int i = 0; i < m_SortedItems.Count(); i++)
{
if (m_SortedItems[i]->GetSectionID() == m_Sections[sectionIndex].m_iID)
{
if (iStart == -1)
iStart = i;
iEnd = i;
}
}
// don't draw this section at all if there are no items in it
if (iStart == -1 && !section.m_bAlwaysVisible)
{
section.m_pHeader->SetVisible(false);
continue;
}
// Skip down a bit if this is not the first section to be drawn
if ( bFirstVisibleSection )
bFirstVisibleSection = false;
else
y += m_iSectionGap;
//=============================================================================
// HPE_BEGIN:
// [tj] Only draw the header if it is enabled
//=============================================================================
int nMinNextSectionY = y + section.m_iMinimumHeight;
if (m_bDrawSectionHeaders)
{
// draw the header
section.m_pHeader->SetBounds(x, y, wide, tall);
section.m_pHeader->SetVisible(true);
y += tall;
}
else
{
section.m_pHeader->SetVisible(false);
}
//=============================================================================
// HPE_END
//=============================================================================
if (iStart == -1 && section.m_bAlwaysVisible)
{
}
else
{
// arrange all the items in this section underneath
for (int i = iStart; i <= iEnd; i++)
{
CItemButton *item = m_SortedItems[i]; //items[i];
item->SetBounds(x, y, wide, m_iLineSpacing);
// setup edit mode
if (m_hEditModePanel.Get() && m_iEditModeItemID == item->GetID())
{
int cx, cwide;
item->GetCellBounds(1, cx, cwide);
m_hEditModePanel->SetBounds(cx, y, cwide, tall);
}
y += m_iLineSpacing + m_iLineGap;
}
}
// Add space, if needed to fulfill minimum requested content height
if ( y < nMinNextSectionY )
y = nMinNextSectionY;
}
// calculate height
contentTall = y;
if (m_pScrollBar->IsVisible())
{
contentTall += m_pScrollBar->GetValue();
}
}
//-----------------------------------------------------------------------------
// Purpose: Ensures that the specified item is visible in the display
//-----------------------------------------------------------------------------
void SectionedListPanel::ScrollToItem(int iItem)
{
int tall = GetSectionTall();
int itemX, itemY ;
int nCurrentValue = m_pScrollBar->GetValue();
// find out where the item is
m_Items[iItem]->GetPos(itemX, itemY);
// add in the current scrollbar position
itemY += nCurrentValue;
// compare that in the list
int cx, cy, cwide, ctall;
GetBounds(cx, cy, cwide, ctall);
if (m_iContentHeight > ctall)
{
if (itemY < nCurrentValue)
{
// scroll up
m_pScrollBar->SetValue(itemY);
}
else if (itemY > nCurrentValue + ctall - tall)
{
// scroll down
m_pScrollBar->SetValue(itemY - ctall + tall);
}
else
{
// keep the current value
}
}
else
{
// area isn't big enough, just remove the scrollbar
m_pScrollBar->SetValue(0);
}
// reset scrollbar
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: sets background color & border
//-----------------------------------------------------------------------------
void SectionedListPanel::ApplySchemeSettings(IScheme *pScheme)
{
BaseClass::ApplySchemeSettings(pScheme);
SetBgColor(GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme));
SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));
FOR_EACH_LL( m_Items, j )
{
m_Items[j]->SetShowColumns( m_bShowColumns );
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SectionedListPanel::SetHeaderFont( HFont hFont )
{
m_hHeaderFont = hFont;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
HFont SectionedListPanel::GetHeaderFont( void ) const
{
return m_hHeaderFont;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SectionedListPanel::SetRowFont( HFont hFont )
{
m_hRowFont = hFont;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
HFont SectionedListPanel::GetRowFont( void ) const
{
return m_hRowFont;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SectionedListPanel::ApplySettings(KeyValues *inResourceData)
{
BaseClass::ApplySettings(inResourceData);
m_iLineSpacing = inResourceData->GetInt("linespacing", 0);
if (!m_iLineSpacing)
{
m_iLineSpacing = DEFAULT_LINE_SPACING;
}
if (IsProportional())
{
m_iLineSpacing = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iLineSpacing);
}
m_iSectionGap = inResourceData->GetInt("sectiongap", 0);
if (!m_iSectionGap)
{
m_iSectionGap = DEFAULT_SECTION_GAP;
}
m_iLineGap = inResourceData->GetInt( "linegap", 0 );
if (IsProportional())
{
m_iSectionGap = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iSectionGap);
m_iLineGap = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iLineGap );
}
}
//-----------------------------------------------------------------------------
// Purpose: passes on proportional state to children
//-----------------------------------------------------------------------------
void SectionedListPanel::SetProportional(bool state)
{
BaseClass::SetProportional(state);
// now setup the section headers and items
int i;
for (i = 0; i < m_Sections.Count(); i++)
{
m_Sections[i].m_pHeader->SetProportional(state);
}
FOR_EACH_LL( m_Items, j )
{
m_Items[j]->SetProportional(state);
}
}
//-----------------------------------------------------------------------------
// Purpose: sets whether or not the vertical scrollbar should ever be displayed
//-----------------------------------------------------------------------------
void SectionedListPanel::SetVerticalScrollbar(bool state)
{
m_bVerticalScrollbarEnabled = state;
}
//-----------------------------------------------------------------------------
// Purpose: adds a new section
//-----------------------------------------------------------------------------
void SectionedListPanel::AddSection(int sectionID, const char *name, SectionSortFunc_t sortFunc)
{
SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID);
AddSection(sectionID, header, sortFunc);
}
//-----------------------------------------------------------------------------
// Purpose: adds a new section
//-----------------------------------------------------------------------------
void SectionedListPanel::AddSection(int sectionID, const wchar_t *name, SectionSortFunc_t sortFunc)
{
SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID);
AddSection(sectionID, header, sortFunc);
}
//-----------------------------------------------------------------------------
// Purpose: Adds a new section, given object
//-----------------------------------------------------------------------------
void SectionedListPanel::AddSection(int sectionID, SectionedListPanelHeader *header, SectionSortFunc_t sortFunc)
{
header = SETUP_PANEL( header );
int index = m_Sections.AddToTail();
m_Sections[index].m_iID = sectionID;
m_Sections[index].m_pHeader = header;
m_Sections[index].m_pSortFunc = sortFunc;
m_Sections[index].m_bAlwaysVisible = false;
m_Sections[index].m_iMinimumHeight = 0;
}
//-----------------------------------------------------------------------------
// Purpose: removes all the sections from the current panel
//-----------------------------------------------------------------------------
void SectionedListPanel::RemoveAllSections()
{
for (int i = 0; i < m_Sections.Count(); i++)
{
if (!m_Sections.IsValidIndex(i))
continue;
m_Sections[i].m_pHeader->SetVisible(false);
m_Sections[i].m_pHeader->MarkForDeletion();
}
m_Sections.RemoveAll();
m_Sections.Purge();
m_SortedItems.RemoveAll();
InvalidateLayout();
ReSortList();
}
//-----------------------------------------------------------------------------
// Purpose: adds a new column to a section
//-----------------------------------------------------------------------------
bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const char *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ )
{
wchar_t wtext[64];
wchar_t *pwtext = g_pVGuiLocalize->Find(columnText);
if (!pwtext)
{
g_pVGuiLocalize->ConvertANSIToUnicode(columnText, wtext, sizeof(wtext));
pwtext = wtext;
}
return AddColumnToSection(sectionID, columnName, pwtext, columnFlags, width, fallbackFont );
}
//-----------------------------------------------------------------------------
// Purpose: as above but with wchar_t's
//-----------------------------------------------------------------------------
bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const wchar_t *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ )
{
int index = FindSectionIndexByID(sectionID);
if (index < 0)
return false;
section_t &section = m_Sections[index];
// add the new column to the sections' list
index = section.m_Columns.AddToTail();
column_t &column = section.m_Columns[index];
Q_strncpy(column.m_szColumnName, columnName, sizeof(column.m_szColumnName));
wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t));
column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0;
column.m_iColumnFlags = columnFlags;
column.m_iWidth = width;
column.m_hFallbackFont = fallbackFont;
return true;
}
//-----------------------------------------------------------------------------
// Purpose: modifies the text in an existing column
//-----------------------------------------------------------------------------
bool SectionedListPanel::ModifyColumn(int sectionID, const char *columnName, const wchar_t *columnText)
{
int index = FindSectionIndexByID(sectionID);
if (index < 0)
return false;
section_t &section = m_Sections[index];
// find the specified column
int columnIndex;
for (columnIndex = 0; columnIndex < section.m_Columns.Count(); columnIndex++)
{
if (!stricmp(section.m_Columns[columnIndex].m_szColumnName, columnName))
break;
}
if (!section.m_Columns.IsValidIndex(columnIndex))
return false;
column_t &column = section.m_Columns[columnIndex];
// modify the text
wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t));
column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0;
section.m_pHeader->InvalidateLayout();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: adds an item to the list; returns itemID
//-----------------------------------------------------------------------------
int SectionedListPanel::AddItem(int sectionID, const KeyValues *data)
{
int itemID = GetNewItemButton();
ModifyItem(itemID, sectionID, data);
// not sorted but in list
m_SortedItems.AddToTail(m_Items[itemID]);
m_bSortNeeded = true;
return itemID;
}
//-----------------------------------------------------------------------------
// Purpose: modifies an existing item; returns false if the item does not exist
//-----------------------------------------------------------------------------
bool SectionedListPanel::ModifyItem(int itemID, int sectionID, const KeyValues *data)
{
if ( !m_Items.IsValidIndex(itemID) )
return false;
InvalidateLayout();
m_Items[itemID]->SetSectionID(sectionID);
m_Items[itemID]->SetData(data);
m_Items[itemID]->InvalidateLayout();
m_bSortNeeded = true;
return true;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SectionedListPanel::SetItemFgColor( int itemID, Color color )
{
Assert( m_Items.IsValidIndex(itemID) );
if ( !m_Items.IsValidIndex(itemID) )
return;
m_Items[itemID]->SetFgColor( color );
m_Items[itemID]->SetOverrideColors( true );
m_Items[itemID]->InvalidateLayout();
}
//=============================================================================
// HPE_BEGIN:
// [menglish] Setter for the background color similar to the foreground color
//=============================================================================
void SectionedListPanel::SetItemBgColor( int itemID, Color color )
{
Assert( m_Items.IsValidIndex(itemID) );
if ( !m_Items.IsValidIndex(itemID) )
return;
m_Items[itemID]->SetBgColor( color );
m_Items[itemID]->SetPaintBackgroundEnabled( true );
m_Items[itemID]->SetOverrideColors( true );
m_Items[itemID]->InvalidateLayout();
}
void SectionedListPanel::SetItemBgHorizFillInset( int itemID, int nInset )
{
Assert( m_Items.IsValidIndex( itemID ) );
if ( !m_Items.IsValidIndex( itemID ) )
return;
m_Items[itemID]->SetItemBgHorizFillInset( nInset );
}
void SectionedListPanel::SetItemFont( int itemID, HFont font )
{
Assert( m_Items.IsValidIndex(itemID) );
if ( !m_Items.IsValidIndex(itemID) )
return;
m_Items[itemID]->SetFont( font );
}
void SectionedListPanel::SetItemEnabled( int itemID, bool bEnabled )
{
Assert( m_Items.IsValidIndex(itemID) );
if ( !m_Items.IsValidIndex(itemID) )
return;
m_Items[itemID]->SetEnabled( bEnabled );
}
//=============================================================================
// HPE_END
//=============================================================================
//-----------------------------------------------------------------------------
// Purpose: sets the color of a section text & underline
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSectionFgColor(int sectionID, Color color)
{
if (!m_Sections.IsValidIndex(sectionID))
return;
m_Sections[sectionID].m_pHeader->SetColor(color);
}
//-----------------------------------------------------------------------------
// Purpose: added so you can change the divider color AFTER the main color.
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSectionDividerColor( int sectionID, Color color)
{
if (!m_Sections.IsValidIndex(sectionID))
return;
m_Sections[sectionID].m_pHeader->SetDividerColor(color);
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSectionDrawDividerBar( int sectionID, bool bDraw )
{
if ( !m_Sections.IsValidIndex( sectionID ) )
return;
m_Sections[sectionID].m_pHeader->DrawDividerBar( bDraw );
}
//-----------------------------------------------------------------------------
// Purpose: forces a section to always be visible
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSectionAlwaysVisible(int sectionID, bool visible)
{
if (!m_Sections.IsValidIndex(sectionID))
return;
m_Sections[sectionID].m_bAlwaysVisible = visible;
}
void SectionedListPanel::SetFontSection(int sectionID, HFont font)
{
if (!m_Sections.IsValidIndex(sectionID))
return;
m_Sections[sectionID].m_pHeader->SetFont(font);
}
void SectionedListPanel::SetSectionMinimumHeight(int sectionID, int iMinimumHeight)
{
if (!m_Sections.IsValidIndex(sectionID))
return;
m_Sections[sectionID].m_iMinimumHeight = iMinimumHeight;
InvalidateLayout();
}
//-----------------------------------------------------------------------------
// Purpose: removes an item from the list; returns false if the item does not exist or is already removed
//-----------------------------------------------------------------------------
bool SectionedListPanel::RemoveItem(int itemID)
{
if ( !m_Items.IsValidIndex(itemID) )
return false;
m_SortedItems.FindAndRemove(m_Items[itemID]);
m_bSortNeeded = true;
m_Items[itemID]->MarkForDeletion();
m_Items.Remove(itemID);
InvalidateLayout();
return true;
}
//-----------------------------------------------------------------------------
// Purpose: returns the number of columns in a section
//-----------------------------------------------------------------------------
int SectionedListPanel::GetColumnCountBySection(int sectionID)
{
int index = FindSectionIndexByID(sectionID);
if (index < 0)
return NULL;
return m_Sections[index].m_Columns.Size();
}
//-----------------------------------------------------------------------------
// Purpose: returns the name of a column by section and column index; returns NULL if there are no more columns
// valid range of columnIndex is [0, GetColumnCountBySection)
//-----------------------------------------------------------------------------
const char *SectionedListPanel::GetColumnNameBySection(int sectionID, int columnIndex)
{
int index = FindSectionIndexByID(sectionID);
if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size())
return NULL;
return m_Sections[index].m_Columns[columnIndex].m_szColumnName;
}
//-----------------------------------------------------------------------------
// Purpose: returns the text for a column by section and column index
//-----------------------------------------------------------------------------
const wchar_t *SectionedListPanel::GetColumnTextBySection(int sectionID, int columnIndex)
{
int index = FindSectionIndexByID(sectionID);
if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size())
return NULL;
return m_Sections[index].m_Columns[columnIndex].m_szColumnText;
}
//-----------------------------------------------------------------------------
// Purpose: returns the type of a column by section and column index
//-----------------------------------------------------------------------------
int SectionedListPanel::GetColumnFlagsBySection(int sectionID, int columnIndex)
{
int index = FindSectionIndexByID(sectionID);
if (index < 0)
return 0;
if (columnIndex >= m_Sections[index].m_Columns.Size())
return 0;
return m_Sections[index].m_Columns[columnIndex].m_iColumnFlags;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int SectionedListPanel::GetColumnWidthBySection(int sectionID, int columnIndex)
{
int index = FindSectionIndexByID(sectionID);
if (index < 0)
return 0;
if (columnIndex >= m_Sections[index].m_Columns.Size())
return 0;
return m_Sections[index].m_Columns[columnIndex].m_iWidth;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SectionedListPanel::SetColumnWidthBySection(int sectionID, const char *columnName, int iWidth)
{
int index = FindSectionIndexByID(sectionID);
if (index < 0)
return;
section_t &section = m_Sections[index];
// find the specified column
int columnIndex;
for (columnIndex = 0; columnIndex < section.m_Columns.Count(); columnIndex++)
{
if (!stricmp(section.m_Columns[columnIndex].m_szColumnName, columnName))
break;
}
if (!section.m_Columns.IsValidIndex(columnIndex))
return;
column_t &column = section.m_Columns[columnIndex];
column.m_iWidth = iWidth;
// make sure the header for this section reloads with the new width
section.m_pHeader->InvalidateLayout();
}
//=============================================================================
// HPE_BEGIN:
// [menglish] Gets the column index by the string identifier
//=============================================================================
int SectionedListPanel::GetColumnIndexByName(int sectionID, char* name)
{
int index = FindSectionIndexByID(sectionID);
if (index < 0)
return 0;
for ( int columnIndex = 0; columnIndex < m_Sections[index].m_Columns.Count(); ++ columnIndex)
{
if( !V_strcmp( m_Sections[index].m_Columns[columnIndex].m_szColumnName, name ) )
return columnIndex;
}
return -1;
}
//=============================================================================
// HPE_END
//=============================================================================
//-----------------------------------------------------------------------------
// Purpose: returns -1 if section not found
//-----------------------------------------------------------------------------
int SectionedListPanel::FindSectionIndexByID(int sectionID)
{
for (int i = 0; i < m_Sections.Size(); i++)
{
if (m_Sections[i].m_iID == sectionID)
{
return i;
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: Called when the scrollbar is moved
//-----------------------------------------------------------------------------
void SectionedListPanel::OnSliderMoved()
{
InvalidateLayout();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: Scrolls the list according to the mouse wheel movement
//-----------------------------------------------------------------------------
void SectionedListPanel::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;
}
// scroll the window based on the delta
int val = m_pScrollBar->GetValue();
val -= (delta * BUTTON_HEIGHT_DEFAULT * 3);
m_pScrollBar->SetValue(val);
}
//-----------------------------------------------------------------------------
// Purpose: Resets the scrollbar position on size change
//-----------------------------------------------------------------------------
void SectionedListPanel::OnSizeChanged(int wide, int tall)
{
BaseClass::OnSizeChanged(wide, tall);
m_pScrollBar->SetValue(0);
InvalidateLayout();
Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: deselects any items
//-----------------------------------------------------------------------------
void SectionedListPanel::OnMousePressed(MouseCode code)
{
//=============================================================================
// HPE_BEGIN:
// [tj] Only do this if clicking is enabled.
//=============================================================================
if (m_clickable){
ClearSelection();
}
//=============================================================================
// HPE_END
//=============================================================================}
}
//-----------------------------------------------------------------------------
// Purpose: deselects any items
//-----------------------------------------------------------------------------
void SectionedListPanel::ClearSelection( void )
{
SetSelectedItem((CItemButton *)NULL);
}
void SectionedListPanel::MoveSelectionDown( void )
{
int itemID = GetSelectedItem();
if (itemID == -1)
return;
if (!m_SortedItems.Count()) // if the list has been emptied
return;
int i;
for (i = 0; i < m_SortedItems.Count(); i++)
{
if (m_SortedItems[i]->GetID() == itemID)
break;
}
Assert(i != m_SortedItems.Count());
// we're already on the end
if (i >= m_SortedItems.Count() - 1)
return;
int newItemID = m_SortedItems[i + 1]->GetID();
SetSelectedItem(m_Items[newItemID]);
ScrollToItem(newItemID);
}
void SectionedListPanel::MoveSelectionUp( void )
{
int itemID = GetSelectedItem();
if (itemID == -1)
return;
if (!m_SortedItems.Count()) // if the list has been emptied
return;
int i;
for (i = 0; i < m_SortedItems.Count(); i++)
{
if (m_SortedItems[i]->GetID() == itemID)
break;
}
Assert(i != m_SortedItems.Count());
// we're already on the end
if (i == 0 || i >= m_SortedItems.Count() )
return;
int newItemID = m_SortedItems[i - 1]->GetID();
SetSelectedItem(m_Items[newItemID]);
ScrollToItem(newItemID);
}
void SectionedListPanel::NavigateTo( void )
{
BaseClass::NavigateTo();
if ( m_SortedItems.Count() )
{
int nItemID = m_SortedItems[ 0 ]->GetID();
SetSelectedItem( m_Items[ nItemID ] );
ScrollToItem( nItemID );
}
RequestFocus();
}
//-----------------------------------------------------------------------------
// Purpose: arrow key movement handler
//-----------------------------------------------------------------------------
void SectionedListPanel::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 buttonTall = GetSectionTall();
ButtonCode_t nButtonCode = GetBaseButtonCode( code );
if ( nButtonCode == KEY_XBUTTON_DOWN ||
nButtonCode == KEY_XSTICK1_DOWN ||
nButtonCode == KEY_XSTICK2_DOWN ||
nButtonCode == STEAMCONTROLLER_DPAD_DOWN ||
code == KEY_DOWN )
{
int itemID = GetSelectedItem();
MoveSelectionDown();
if ( itemID != GetSelectedItem() )
{
// Only eat the input if it did something
return;
}
}
else if ( nButtonCode == KEY_XBUTTON_UP ||
nButtonCode == KEY_XSTICK1_UP ||
nButtonCode == KEY_XSTICK2_UP ||
nButtonCode == STEAMCONTROLLER_DPAD_UP ||
code == KEY_UP)
{
int itemID = GetSelectedItem();
MoveSelectionUp();
if ( itemID != GetSelectedItem() )
{
// Only eat the input if it did something
return;
}
}
else if (code == KEY_PAGEDOWN)
{
// calculate info for # of rows
int cx, cy, cwide, ctall;
GetBounds(cx, cy, cwide, ctall);
int rowsperpage = ctall/buttonTall;
int itemID = GetSelectedItem();
int lastValidItem = itemID;
int secID = m_Items[itemID]->GetSectionID();
int i=0;
int row = m_SortedItems.Find(m_Items[itemID]);
while ( i < rowsperpage )
{
if ( m_SortedItems.IsValidIndex(++row) )
{
itemID = m_SortedItems[row]->GetID();
lastValidItem = itemID;
i++;
// if we switched sections, then count the section header as a row
if (m_Items[itemID]->GetSectionID() != secID)
{
secID = m_Items[itemID]->GetSectionID();
i++;
}
}
else
{
itemID = lastValidItem;
break;
}
}
SetSelectedItem(m_Items[itemID]);
ScrollToItem(itemID);
return;
}
else if (code == KEY_PAGEUP)
{
// calculate info for # of rows
int cx, cy, cwide, ctall;
GetBounds(cx, cy, cwide, ctall);
int rowsperpage = ctall/buttonTall;
int itemID = GetSelectedItem();
int lastValidItem = itemID;
int secID = m_Items[itemID]->GetSectionID();
int i=0;
int row = m_SortedItems.Find(m_Items[itemID]);
while ( i < rowsperpage )
{
if ( m_SortedItems.IsValidIndex(--row) )
{
itemID = m_SortedItems[row]->GetID();
lastValidItem = itemID;
i++;
// if we switched sections, then count the section header as a row
if (m_Items[itemID]->GetSectionID() != secID)
{
secID = m_Items[itemID]->GetSectionID();
i++;
}
}
else
{
SetSelectedItem(m_Items[lastValidItem]);
m_pScrollBar->SetValue(0);
return;
}
}
SetSelectedItem(m_Items[itemID]);
ScrollToItem(itemID);
return;
}
else if ( code == KEY_ENTER || nButtonCode == KEY_XBUTTON_A || nButtonCode == STEAMCONTROLLER_A )
{
Panel *pSelectedItem = m_hSelectedItem;
if ( pSelectedItem )
{
pSelectedItem->OnMousePressed( MOUSE_LEFT );
}
return;
}
BaseClass::OnKeyCodePressed( code );
}
//-----------------------------------------------------------------------------
// Purpose: Clears the list
//-----------------------------------------------------------------------------
void SectionedListPanel::DeleteAllItems()
{
FOR_EACH_LL( m_Items, i )
{
m_Items[i]->SetVisible(false);
m_Items[i]->Clear();
// don't delete, move to free list
int freeIndex = m_FreeItems.AddToTail();
m_FreeItems[freeIndex] = m_Items[i];
}
m_Items.RemoveAll();
m_SortedItems.RemoveAll();
m_hSelectedItem = NULL;
InvalidateLayout();
m_bSortNeeded = true;
}
//-----------------------------------------------------------------------------
// Purpose: Changes the current list selection
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSelectedItem(CItemButton *item)
{
if (m_hSelectedItem.Get() == item)
return;
// deselect the current item
if (m_hSelectedItem.Get())
{
m_hSelectedItem->SetSelected(false);
}
// set the new item
m_hSelectedItem = item;
if (m_hSelectedItem.Get())
{
m_hSelectedItem->SetSelected(true);
}
Repaint();
PostActionSignal(new KeyValues("ItemSelected", "itemID", m_hSelectedItem.Get() ? m_hSelectedItem->GetID() : -1));
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int SectionedListPanel::GetSelectedItem()
{
if (m_hSelectedItem.Get())
{
return m_hSelectedItem->GetID();
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: sets which item is currently selected
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSelectedItem(int itemID)
{
if ( m_Items.IsValidIndex(itemID) )
{
SetSelectedItem(m_Items[itemID]);
}
}
//-----------------------------------------------------------------------------
// Purpose: returns the data of a selected item
//-----------------------------------------------------------------------------
KeyValues *SectionedListPanel::GetItemData(int itemID)
{
Assert(m_Items.IsValidIndex(itemID));
if ( !m_Items.IsValidIndex(itemID) )
return NULL;
return m_Items[itemID]->GetData();
}
//-----------------------------------------------------------------------------
// Purpose: returns what section an item is in
//-----------------------------------------------------------------------------
int SectionedListPanel::GetItemSection(int itemID)
{
if ( !m_Items.IsValidIndex(itemID) )
return -1;
return m_Items[itemID]->GetSectionID();
}
//-----------------------------------------------------------------------------
// Purpose: returns true if the itemID is valid for use
//-----------------------------------------------------------------------------
bool SectionedListPanel::IsItemIDValid(int itemID)
{
return m_Items.IsValidIndex(itemID);
}
//-----------------------------------------------------------------------------
// Purpose: returns true if the itemID is valid for use
//-----------------------------------------------------------------------------
int SectionedListPanel::GetHighestItemID()
{
return m_Items.MaxElementIndex();
}
//-----------------------------------------------------------------------------
// Purpose: item iterators
//-----------------------------------------------------------------------------
int SectionedListPanel::GetItemCount()
{
return m_SortedItems.Count();
}
//-----------------------------------------------------------------------------
// Purpose: item iterators
//-----------------------------------------------------------------------------
int SectionedListPanel::GetItemIDFromRow(int row)
{
if ( !m_SortedItems.IsValidIndex(row) )
return -1;
return m_SortedItems[row]->GetID();
}
//-----------------------------------------------------------------------------
// Purpose: returns the row that this itemID occupies. -1 if the itemID is invalid
//-----------------------------------------------------------------------------
int SectionedListPanel::GetRowFromItemID(int itemID)
{
for (int i = 0; i < m_SortedItems.Count(); i++)
{
if ( m_SortedItems[i]->GetID() == itemID )
{
return i;
}
}
return -1;
}
//-----------------------------------------------------------------------------
// Purpose: gets the local coordinates of a cell
//-----------------------------------------------------------------------------
bool SectionedListPanel::GetCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall)
{
x = y = wide = tall = 0;
if ( !IsItemIDValid(itemID) )
return false;
// get the item
CItemButton *item = m_Items[itemID];
if ( !item->IsVisible() )
return false;
//!! ignores column for now
item->GetBounds(x, y, wide, tall);
item->GetCellBounds(column, x, wide);
return true;
}
//-----------------------------------------------------------------------------
// Purpose: gets the local coordinates of a section header
//-----------------------------------------------------------------------------
bool SectionedListPanel::GetSectionHeaderBounds(int sectionID, int &x, int &y, int &wide, int &tall)
{
x = y = wide = tall = 0;
int index = FindSectionIndexByID(sectionID);
if (index < 0 || !m_Sections[index].m_pHeader )
return false;
m_Sections[index].m_pHeader->GetBounds( x, y, wide, tall );
return true;
}
//=============================================================================
// HPE_BEGIN:
// [menglish] Gets the local coordinates of a cell using the max width for every column
// Gets the local coordinates of a cell
//=============================================================================
bool SectionedListPanel::GetMaxCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall)
{
x = y = wide = tall = 0;
if ( !IsItemIDValid(itemID) )
return false;
// get the item
CItemButton *item = m_Items[itemID];
if ( !item->IsVisible() )
return false;
//!! ignores column for now
item->GetBounds(x, y, wide, tall);
item->GetMaxCellBounds(column, x, wide);
return true;
}
//-----------------------------------------------------------------------------
// Purpose: gets the local coordinates of a cell
//-----------------------------------------------------------------------------
bool SectionedListPanel::GetItemBounds(int itemID, int &x, int &y, int &wide, int &tall)
{
x = y = wide = tall = 0;
if ( !IsItemIDValid(itemID) )
return false;
// get the item
CItemButton *item = m_Items[itemID];
if ( !item->IsVisible() )
return false;
//!! ignores column for now
item->GetBounds(x, y, wide, tall);
return true;
}
//=============================================================================
// HPE_END
//=============================================================================
//-----------------------------------------------------------------------------
// Purpose: forces an item to redraw
//-----------------------------------------------------------------------------
void SectionedListPanel::InvalidateItem(int itemID)
{
if ( !IsItemIDValid(itemID) )
return;
m_Items[itemID]->InvalidateLayout();
m_Items[itemID]->Repaint();
}
//-----------------------------------------------------------------------------
// Purpose: set up a field for editing
//-----------------------------------------------------------------------------
void SectionedListPanel::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 SectionedListPanel::LeaveEditMode()
{
if (m_hEditModePanel.Get())
{
InvalidateItem(m_iEditModeItemID);
m_hEditModePanel->SetVisible(false);
m_hEditModePanel->SetParent((Panel *)NULL);
m_hEditModePanel = NULL;
}
}
//-----------------------------------------------------------------------------
// Purpose: returns true if we are currently in inline editing mode
//-----------------------------------------------------------------------------
bool SectionedListPanel::IsInEditMode()
{
return (m_hEditModePanel.Get() != NULL);
}
//-----------------------------------------------------------------------------
// Purpose: list used to match indexes in image columns to image pointers
//-----------------------------------------------------------------------------
void SectionedListPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone)
{
m_bDeleteImageListWhenDone = deleteImageListWhenDone;
m_pImageList = imageList;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SectionedListPanel::OnSetFocus()
{
if (m_hSelectedItem.Get())
{
m_hSelectedItem->RequestFocus();
}
else
{
BaseClass::OnSetFocus();
}
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
int SectionedListPanel::GetSectionTall()
{
if (m_Sections.Count())
{
HFont font = m_Sections[0].m_pHeader->GetFont();
if (font != INVALID_FONT)
{
return surface()->GetFontTall(font) + BUTTON_HEIGHT_SPACER;
}
}
return BUTTON_HEIGHT_DEFAULT;
}
//-----------------------------------------------------------------------------
// Purpose: returns the size required to fully draw the contents of the panel
//-----------------------------------------------------------------------------
void SectionedListPanel::GetContentSize(int &wide, int &tall)
{
// make sure our layout is done
if (IsLayoutInvalid())
{
if (m_bSortNeeded)
{
ReSortList();
m_bSortNeeded = false;
}
LayoutPanels(m_iContentHeight);
}
wide = GetWide();
tall = m_iContentHeight;
}
//-----------------------------------------------------------------------------
// Purpose: Returns the index of a new item button
//-----------------------------------------------------------------------------
int SectionedListPanel::GetNewItemButton()
{
int itemID = m_Items.AddToTail();
if (m_FreeItems.Count())
{
// reusing an existing CItemButton
m_Items[itemID] = m_FreeItems[m_FreeItems.Head()];
m_Items[itemID]->SetID(itemID);
m_Items[itemID]->SetVisible(true);
m_FreeItems.Remove(m_FreeItems.Head());
}
else
{
// create a new CItemButton
m_Items[itemID] = SETUP_PANEL(new CItemButton(this, itemID));
m_Items[itemID]->SetShowColumns( m_bShowColumns );
}
// Gross. Le's hope this isn't the only property that doesn't get defaulted
// properly when an item is recycled.....
m_Items[itemID]->SetEnabled( true );
return itemID;
}
//-----------------------------------------------------------------------------
// Purpose: Returns fallback font to use for text image for this column
// Input : sectionID -
// columnIndex -
// Output : virtual HFont
//-----------------------------------------------------------------------------
HFont SectionedListPanel::GetColumnFallbackFontBySection( int sectionID, int columnIndex )
{
int index = FindSectionIndexByID(sectionID);
if (index < 0)
return INVALID_FONT;
if (columnIndex >= m_Sections[index].m_Columns.Size())
return INVALID_FONT;
return m_Sections[index].m_Columns[columnIndex].m_hFallbackFont;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
Color *SectionedListPanel::GetColorOverrideForCell( int sectionID, int itemID, int columnID )
{
FOR_EACH_VEC( m_ColorOverrides, i )
{
if ( ( m_ColorOverrides[i].m_SectionID == sectionID ) &&
( m_ColorOverrides[i].m_ItemID == itemID ) &&
( m_ColorOverrides[i].m_ColumnID == columnID ) )
{
return &(m_ColorOverrides[i].m_clrOverride);
}
}
return NULL;
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void SectionedListPanel::SetColorOverrideForCell( int sectionID, int itemID, int columnID, Color clrOverride )
{
// is this value already in the override list?
FOR_EACH_VEC( m_ColorOverrides, i )
{
if ( ( m_ColorOverrides[i].m_SectionID == sectionID ) &&
( m_ColorOverrides[i].m_ItemID == itemID ) &&
( m_ColorOverrides[i].m_ColumnID == columnID ) )
{
m_ColorOverrides[i].m_clrOverride = clrOverride;
return;
}
}
int iIndex = m_ColorOverrides.AddToTail();
m_ColorOverrides[iIndex].m_SectionID = sectionID;
m_ColorOverrides[iIndex].m_ItemID = itemID;
m_ColorOverrides[iIndex].m_ColumnID = columnID;
m_ColorOverrides[iIndex].m_clrOverride = clrOverride;
}