From f56bb35301836e56582a575a75864392a0177875 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C3=B8rgen=20P=2E=20Tjern=C3=B8?= Date: Mon, 2 Dec 2013 19:31:46 -0800 Subject: Fix line endings. WHAMMY. --- mp/src/vgui2/vgui_controls/SectionedListPanel.cpp | 4338 ++++++++++----------- 1 file changed, 2169 insertions(+), 2169 deletions(-) (limited to 'mp/src/vgui2/vgui_controls/SectionedListPanel.cpp') diff --git a/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp b/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp index 120de368..65e90239 100644 --- a/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp +++ b/mp/src/vgui2/vgui_controls/SectionedListPanel.cpp @@ -1,2169 +1,2169 @@ -//========= Copyright Valve Corporation, All rights reserved. ============// -// -// Purpose: -// -// $NoKeywords: $ -//=============================================================================// - -#include - -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "utlvector.h" - -// memdbgon must be the last include file in a .cpp file!!! -#include - -using namespace vgui; - -enum -{ - BUTTON_HEIGHT_DEFAULT = 20, - BUTTON_HEIGHT_SPACER = 7, - DEFAULT_LINE_SPACING = 20, - DEFAULT_SECTION_GAP = 8, - COLUMN_DATA_INDENT = 6, - COLUMN_DATA_GAP = 2, -}; - -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 ); -} - -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(); -} - -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(); - - 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 - COLUMN_DATA_GAP); - } - else - { - SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP); - } - xpos += columnWidth; - - if (!(columnFlags & SectionedListPanel::HEADER_IMAGE)) - { - Assert(dynamic_cast(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(); - } - - ~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(GetImageAtIndex(i)); - if (textImage) - { - 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 - { - textImage->SetColor(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 + COLUMN_DATA_INDENT, wide - (COLUMN_DATA_INDENT + COLUMN_DATA_GAP)); - } - else - { - if (columnFlags & SectionedListPanel::COLUMN_CENTER) - { - int offSet = (wide / 2) - (imageWide / 2); - SetImageBounds(i, xpos + offSet, wide - offSet - COLUMN_DATA_GAP); - } - else if (columnFlags & SectionedListPanel::COLUMN_RIGHT) - { - SetImageBounds(i, xpos + wide - imageWide, wide - COLUMN_DATA_GAP); - } - else - { - SetImageBounds(i, xpos, wide - 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, 0, wide, tall); - } - - 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, tall = 0; - int wide; - 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; - } - -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 m_TextImages; - - bool m_bSelected; - bool m_bOverrideColors; - bool m_bShowColumns; -}; - -}; // 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_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 §ion = 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 §ion = 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 their are no item 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; - } - } - - // 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; - } - if (IsProportional()) - { - m_iSectionGap = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iSectionGap); - } -} - -//----------------------------------------------------------------------------- -// 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 §ion = 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 §ion = 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::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: 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; -} - -//============================================================================= -// 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 || - 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 || - 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 ) - { - 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; -} +//========= Copyright Valve Corporation, All rights reserved. ============// +// +// Purpose: +// +// $NoKeywords: $ +//=============================================================================// + +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include "utlvector.h" + +// memdbgon must be the last include file in a .cpp file!!! +#include + +using namespace vgui; + +enum +{ + BUTTON_HEIGHT_DEFAULT = 20, + BUTTON_HEIGHT_SPACER = 7, + DEFAULT_LINE_SPACING = 20, + DEFAULT_SECTION_GAP = 8, + COLUMN_DATA_INDENT = 6, + COLUMN_DATA_GAP = 2, +}; + +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 ); +} + +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(); +} + +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(); + + 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 - COLUMN_DATA_GAP); + } + else + { + SetImageBounds(i, xpos, wide - COLUMN_DATA_GAP); + } + xpos += columnWidth; + + if (!(columnFlags & SectionedListPanel::HEADER_IMAGE)) + { + Assert(dynamic_cast(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(); + } + + ~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(GetImageAtIndex(i)); + if (textImage) + { + 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 + { + textImage->SetColor(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 + COLUMN_DATA_INDENT, wide - (COLUMN_DATA_INDENT + COLUMN_DATA_GAP)); + } + else + { + if (columnFlags & SectionedListPanel::COLUMN_CENTER) + { + int offSet = (wide / 2) - (imageWide / 2); + SetImageBounds(i, xpos + offSet, wide - offSet - COLUMN_DATA_GAP); + } + else if (columnFlags & SectionedListPanel::COLUMN_RIGHT) + { + SetImageBounds(i, xpos + wide - imageWide, wide - COLUMN_DATA_GAP); + } + else + { + SetImageBounds(i, xpos, wide - 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, 0, wide, tall); + } + + 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, tall = 0; + int wide; + 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; + } + +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 m_TextImages; + + bool m_bSelected; + bool m_bOverrideColors; + bool m_bShowColumns; +}; + +}; // 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_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 §ion = 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 §ion = 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 their are no item 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; + } + } + + // 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; + } + if (IsProportional()) + { + m_iSectionGap = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iSectionGap); + } +} + +//----------------------------------------------------------------------------- +// 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 §ion = 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 §ion = 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::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: 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; +} + +//============================================================================= +// 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 || + 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 || + 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 ) + { + 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; +} -- cgit v1.2.3