//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: 
//
// $NoKeywords: $
//=============================================================================//

#include <stdio.h>

#include <vgui/IInput.h>
#include <vgui/IPanel.h>
#include <vgui/ILocalize.h>
#include <vgui/IScheme.h>
#include <vgui/ISurface.h>
#include <KeyValues.h>
#include <vgui/MouseCode.h>

#include <vgui_controls/SectionedListPanel.h>
#include <vgui_controls/Button.h>
#include <vgui_controls/Controls.h>
#include <vgui_controls/Label.h>
#include <vgui_controls/ScrollBar.h>
#include <vgui_controls/TextImage.h>
#include <vgui_controls/ImageList.h>

#include "utlvector.h"

// memdbgon must be the last include file in a .cpp file!!!
#include <tier0/memdbgon.h>

using namespace vgui;

namespace vgui
{

//-----------------------------------------------------------------------------
// Purpose: header label that separates and names each section
//-----------------------------------------------------------------------------
SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const char *name, int sectionID) : Label(parent, name, "")
{
	m_pListPanel = parent;
	m_iSectionID = sectionID;
	SetTextImageIndex(-1);
	ClearImages();
	SetPaintBackgroundEnabled( false );
	m_bDrawDividerBar = true;
}

SectionedListPanelHeader::SectionedListPanelHeader(SectionedListPanel *parent, const wchar_t *name, int sectionID) : Label(parent, "SectionHeader", "")
{
	SetText(name);	
	SetVisible(false);
	m_pListPanel = parent;
	m_iSectionID = sectionID;
	SetTextImageIndex(-1);
	ClearImages();
	m_bDrawDividerBar = true;
}

void SectionedListPanelHeader::ApplySchemeSettings(IScheme *pScheme)
{
	BaseClass::ApplySchemeSettings(pScheme);

	SetFgColor(GetSchemeColor("SectionedListPanel.HeaderTextColor", pScheme));
	m_SectionDividerColor = GetSchemeColor("SectionedListPanel.DividerColor", pScheme);
	SetBgColor(GetSchemeColor("SectionedListPanelHeader.BgColor", GetBgColor(), pScheme));
	SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional()));
	ClearImages();
	
	HFont hFont = m_pListPanel->GetHeaderFont();
	if ( hFont != INVALID_FONT )
	{
		SetFont( hFont );
	}
	else
	{
		SetFont(pScheme->GetFont("DefaultVerySmall", IsProportional()));
	}
}

void SectionedListPanelHeader::Paint()
{
	BaseClass::Paint();

	if ( m_bDrawDividerBar )
	{
		int x, y, wide, tall;
		GetBounds(x, y, wide, tall);

		y = (tall - 2);	// draw the line under the panel

		surface()->DrawSetColor(m_SectionDividerColor);
		surface()->DrawFilledRect(1, y, GetWide() - 2, y + 1);
	}
}

void SectionedListPanelHeader::SetColor(Color col)
{
	m_SectionDividerColor = col;
	SetFgColor(col);
}
void SectionedListPanelHeader::SetDividerColor(Color col )
{
	m_SectionDividerColor = col;
}

void SectionedListPanelHeader::PerformLayout()
{
	BaseClass::PerformLayout();

	// set up the text in the header
	int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
	if (colCount != GetImageCount())
	{
		// rebuild the image list
		for (int i = 0; i < colCount; i++)
		{
			int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
			IImage *image = NULL;
			if (columnFlags & SectionedListPanel::HEADER_IMAGE)
			{
				//!! need some kind of image reference
				image = NULL;
			}
			else 
			{
				TextImage *textImage = new TextImage("");
				textImage->SetFont(GetFont());
				HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i );
				if ( INVALID_FONT != fallback )
				{
					textImage->SetUseFallbackFont( true, fallback );
				}
				textImage->SetColor(GetFgColor());
				image = textImage;
			}

			SetImageAtIndex(i, image, 0);
		}
	}

	for (int repeat = 0; repeat <= 1; repeat++)
	{
		int xpos = 0;
		for (int i = 0; i < colCount; i++)
		{
			int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
			int columnWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
			int maxWidth = columnWidth;

			IImage *image = GetImageAtIndex(i);
			if (!image)
			{
				xpos += columnWidth;
				continue;
			}

			// set the image position within the label
			int contentWide, wide, tall;
			image->GetContentSize(wide, tall);
			contentWide = wide;

			// see if we can draw over the next few column headers (if we're left-aligned)
			if (!(columnFlags & SectionedListPanel::COLUMN_RIGHT))
			{
				for (int j = i + 1; j < colCount; j++)
				{
					// see if this column header has anything for a header
					int iwide = 0, itall = 0;
					if (GetImageAtIndex(j))
					{
						GetImageAtIndex(j)->GetContentSize(iwide, itall);
					}

					if (iwide == 0)
					{
						// it's a blank header, ok to draw over it
						maxWidth += m_pListPanel->GetColumnWidthBySection(m_iSectionID, j);
					}
				}
			}
			if (maxWidth >= 0)
			{
				wide = maxWidth;
			}

			if (columnFlags & SectionedListPanel::COLUMN_RIGHT)
			{
				SetImageBounds(i, xpos + wide - contentWide, wide - SectionedListPanel::COLUMN_DATA_GAP);
			}
			else
			{
				SetImageBounds(i, xpos, wide - SectionedListPanel::COLUMN_DATA_GAP);
			}
			xpos += columnWidth;

			if (!(columnFlags & SectionedListPanel::HEADER_IMAGE))
			{
				Assert(dynamic_cast<TextImage *>(image) != NULL);
				TextImage *textImage = (TextImage *)image;
				textImage->SetFont(GetFont()); 
				textImage->SetText(m_pListPanel->GetColumnTextBySection(m_iSectionID, i));
				textImage->ResizeImageToContentMaxWidth( maxWidth );
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: Individual items in the list
//-----------------------------------------------------------------------------
class CItemButton : public Label
{
	DECLARE_CLASS_SIMPLE( CItemButton, Label );

public:
	CItemButton(SectionedListPanel *parent, int itemID) : Label(parent, NULL, "< item >")
	{
		m_pListPanel = parent;
		m_iID = itemID;
		m_pData = NULL;
		Clear();
		m_nHorizFillInset = 0;
	}

	~CItemButton()
	{
		// free all the keyvalues
		if (m_pData)
		{
			m_pData->deleteThis();
		}

		// clear any section data
		SetSectionID(-1);
	}

	void Clear()
	{
		m_bSelected = false;
		m_bOverrideColors = false;
		m_iSectionID = -1;
		SetPaintBackgroundEnabled( false );
		SetTextImageIndex(-1);
		ClearImages();
	}

	int GetID()
	{
		return m_iID;
	}

	void SetID(int itemID)
	{
		m_iID = itemID;
	}

	int GetSectionID()
	{
		return m_iSectionID;
	}

	void SetSectionID(int sectionID)
	{
		if (sectionID != m_iSectionID)
		{
			// free any existing textimage list 
			ClearImages();
			// delete any images we've created
			for (int i = 0; i < m_TextImages.Count(); i++)
			{
				delete m_TextImages[i];
			}
			m_TextImages.RemoveAll();
			// mark the list as needing rebuilding
			InvalidateLayout();
		}
		m_iSectionID = sectionID;
	}

	void SetData(const KeyValues *data)
	{
		if (m_pData)
		{
			m_pData->deleteThis();
		}

		m_pData = data->MakeCopy();
		InvalidateLayout();
	}

	KeyValues *GetData()
	{
		return m_pData;
	}

	virtual void PerformLayout()
	{
		// get our button text
		int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
		if (!m_pData || colCount < 1)
		{
			SetText("< unset >");
		}
		else
		{
			if (colCount != GetImageCount())
			{
				// rebuild the image list
				for (int i = 0; i < colCount; i++)
				{
					int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
					if (!(columnFlags & SectionedListPanel::COLUMN_IMAGE))
					{
						TextImage *image = new TextImage("");
						m_TextImages.AddToTail(image);
						image->SetFont( GetFont() );
						HFont fallback = m_pListPanel->GetColumnFallbackFontBySection( m_iSectionID, i );
						if ( INVALID_FONT != fallback )
						{
							image->SetUseFallbackFont( true, fallback );
						}
						SetImageAtIndex(i, image, 0);
					}				
				}

				{for ( int i = GetImageCount(); i < colCount; i++ ) // make sure we have enough image slots
				{
					AddImage( NULL, 0 );
				}}
			}

			// set the text for each column
			int xpos = 0;
			for (int i = 0; i < colCount; i++)
			{
				const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i);

				int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
				int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);
			
				IImage *image = NULL;
				if (columnFlags & SectionedListPanel::COLUMN_IMAGE)
				{
					// lookup which image is being referred to
					if (m_pListPanel->m_pImageList)
					{
						int imageIndex = m_pData->GetInt(keyname, 0);
						if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex))
						{
							// 0 is always the blank image
							if (imageIndex > 0)
							{
								image = m_pListPanel->m_pImageList->GetImage(imageIndex);
								SetImageAtIndex(i, image, 0);
							}
						}
						else
						{
							// this is mildly valid (CGamesList hits it because of the way it uses the image indices)
							// Assert(!("Image index out of range for ImageList in SectionedListPanel"));
						}
					}
					else
					{
						Assert(!("Images columns used in SectionedListPanel with no ImageList set"));
					}
				}
				else
				{
					TextImage *textImage = dynamic_cast<TextImage *>(GetImageAtIndex(i));
					if (textImage)
					{
						const wchar* pwszOverride = m_pData->GetWString( keyname, NULL );
						if ( pwszOverride && pwszOverride[0] != '#' )
						{
							textImage->SetText( pwszOverride );
						}
						else
						{
							textImage->SetText(m_pData->GetString(keyname, ""));
						}
						textImage->ResizeImageToContentMaxWidth( maxWidth );

						// set the text color based on the selection state - if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
						VPANEL focus = input()->GetFocus();
						if ( !m_bOverrideColors )
						{
							if (IsSelected() && !m_pListPanel->IsInEditMode())
							{
								if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
								{
									textImage->SetColor(m_ArmedFgColor2);
								}
								else
								{
									textImage->SetColor(m_OutOfFocusSelectedTextColor);
								}
							}
							else if (columnFlags & SectionedListPanel::COLUMN_BRIGHT)
							{
								textImage->SetColor(m_ArmedFgColor1);
							}
							else
							{
								textImage->SetColor(m_FgColor2);
							}
						}
						else
						{
							// custom colors
							if (IsSelected() && (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent()))))
							{
								textImage->SetColor(m_ArmedFgColor2);
							}
							else
							{
								Color *clrOverride = m_pListPanel->GetColorOverrideForCell(m_iSectionID, m_iID, i);
								textImage->SetColor( (clrOverride != NULL) ? *clrOverride : GetFgColor());
							}
						}
					}
					image = textImage;
				}

				// set the image position within the label
				int imageWide = 0, tall = 0;
				int wide;
				if (image)
				{
					image->GetContentSize(imageWide, tall);
				}
				if (maxWidth >= 0)
				{
					wide = maxWidth;
				}
				else
				{
					wide = imageWide;
				}

				if (i == 0 && !(columnFlags & SectionedListPanel::COLUMN_IMAGE))
				{
					// first column has an extra indent
					SetImageBounds(i, xpos + SectionedListPanel::COLUMN_DATA_INDENT, wide - (SectionedListPanel::COLUMN_DATA_INDENT + SectionedListPanel::COLUMN_DATA_GAP));
				}
				else
				{
					if (columnFlags & SectionedListPanel::COLUMN_CENTER)
					{
						int offSet = (wide / 2) - (imageWide / 2);
						SetImageBounds(i, xpos + offSet, wide - offSet - SectionedListPanel::COLUMN_DATA_GAP);
					}
					else if (columnFlags & SectionedListPanel::COLUMN_RIGHT)
					{
						SetImageBounds(i, xpos + wide - imageWide, wide - SectionedListPanel::COLUMN_DATA_GAP);
					}
					else
					{
						SetImageBounds(i, xpos, wide - SectionedListPanel::COLUMN_DATA_GAP);
					}
				}
				xpos += wide;
			}
		}

		BaseClass::PerformLayout();
	}

	virtual void ApplySchemeSettings(IScheme *pScheme)
	{
		BaseClass::ApplySchemeSettings(pScheme);

		m_ArmedFgColor1 = GetSchemeColor("SectionedListPanel.BrightTextColor", pScheme);
		m_ArmedFgColor2 = GetSchemeColor("SectionedListPanel.SelectedTextColor", pScheme);
		m_OutOfFocusSelectedTextColor = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedTextColor", pScheme);
		m_ArmedBgColor = GetSchemeColor("SectionedListPanel.SelectedBgColor", pScheme);

		m_FgColor2 = GetSchemeColor("SectionedListPanel.TextColor", pScheme);

		m_BgColor = GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme);
		m_SelectionBG2Color = GetSchemeColor("SectionedListPanel.OutOfFocusSelectedBgColor", pScheme);


		HFont hFont = m_pListPanel->GetRowFont();
		if ( hFont != INVALID_FONT )
		{
			SetFont( hFont );
		}
		else
		{
			const char *fontName = pScheme->GetResourceString( "SectionedListPanel.Font" );
			HFont font = pScheme->GetFont(fontName, IsProportional());
			if ( font != INVALID_FONT )
			{
				SetFont( font );
			}
		}

		ClearImages();
	}

	virtual void PaintBackground()
	{
		int wide, tall;
		GetSize(wide, tall);

		if (IsSelected() && !m_pListPanel->IsInEditMode())
		{
            VPANEL focus = input()->GetFocus();
            // if one of the children of the SectionedListPanel has focus, then 'we have focus' if we're selected
            if (HasFocus() || (focus && ipanel()->HasParent(focus, GetVParent())))
            {
			    surface()->DrawSetColor(m_ArmedBgColor);
            }
            else
            {
			    surface()->DrawSetColor(m_SelectionBG2Color);
            }
		}
		else
		{
			surface()->DrawSetColor(GetBgColor());
		}
		surface()->DrawFilledRect(0, m_nHorizFillInset, wide, tall - m_nHorizFillInset);
	}

	virtual void Paint()
	{
		BaseClass::Paint();

		if ( !m_bShowColumns )
			return;

		// Debugging code to show column widths
		int wide, tall;
		GetSize(wide, tall);
		surface()->DrawSetColor( 255,255,255,255 );
		surface()->DrawOutlinedRect(0, 0, wide, tall);

		int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
		if (m_pData && colCount >= 0)
		{
			int xpos = 0;
			for (int i = 0; i < colCount; i++)
			{
				const char *keyname = m_pListPanel->GetColumnNameBySection(m_iSectionID, i);
				int columnFlags = m_pListPanel->GetColumnFlagsBySection(m_iSectionID, i);
				int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);

				IImage *image = NULL;
				if (columnFlags & SectionedListPanel::COLUMN_IMAGE)
				{
					// lookup which image is being referred to
					if (m_pListPanel->m_pImageList)
					{
						int imageIndex = m_pData->GetInt(keyname, 0);
						if (m_pListPanel->m_pImageList->IsValidIndex(imageIndex))
						{
							if (imageIndex > 0)
							{
								image = m_pListPanel->m_pImageList->GetImage(imageIndex);
							}
						}
					}
				}
				else
				{
					image = GetImageAtIndex(i);
				}

				int imageWide = 0;
				if (image)
				{
					image->GetContentSize(imageWide, tall);
				}
				if (maxWidth >= 0)
				{
					wide = maxWidth;
				}
				else
				{
					wide = imageWide;
				}

				xpos += wide;//max(maxWidth,wide);
				surface()->DrawOutlinedRect( xpos, 0, xpos, GetTall() );
			}
		}
	}

	virtual void OnMousePressed(MouseCode code)
	{
		if ( m_pListPanel && m_pListPanel->IsClickable() && IsEnabled() )
		{
			if (code == MOUSE_LEFT)
			{
				m_pListPanel->PostActionSignal(new KeyValues("ItemLeftClick", "itemID", m_iID));
			}
			if (code == MOUSE_RIGHT)
			{
				KeyValues *msg = new KeyValues("ItemContextMenu", "itemID", m_iID);
				msg->SetPtr("SubPanel", this);
				m_pListPanel->PostActionSignal(msg);
			}

			m_pListPanel->SetSelectedItem(this);
		}
	}

	void SetSelected(bool state)
	{
		if (m_bSelected != state)
		{
            if (state)
            {
                RequestFocus();
            }
			m_bSelected = state;
			SetPaintBackgroundEnabled( state );
			InvalidateLayout();
			Repaint();
		}
	}

	bool IsSelected()
	{
		return m_bSelected;
	}

    virtual void OnSetFocus()
    {
        InvalidateLayout(); // force the layout to be redone so we can change text color according to focus
        BaseClass::OnSetFocus();
    }

    virtual void OnKillFocus()
    {
        InvalidateLayout(); // force the layout to be redone so we can change text color according to focus
        BaseClass::OnSetFocus();
    }

	virtual void OnMouseDoublePressed(MouseCode code)
	{
		//=============================================================================
		// HPE_BEGIN:
		// [tj] Only do this if clicking is enabled.
		//=============================================================================
		if (m_pListPanel && m_pListPanel->IsClickable())
		{
			if (code == MOUSE_LEFT)
			{
				m_pListPanel->PostActionSignal(new KeyValues("ItemDoubleLeftClick", "itemID", m_iID));

				// post up an enter key being hit
				m_pListPanel->OnKeyCodeTyped(KEY_ENTER);
			}
			else
			{
				OnMousePressed(code);
			}

			m_pListPanel->SetSelectedItem(this);
		}
		//=============================================================================
		// HPE_END
		//=============================================================================
	}

	void GetCellBounds(int column, int &xpos, int &columnWide)
	{
		xpos = 0, columnWide = 0;
		int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
		for (int i = 0; i < colCount; i++)
		{
			int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);

			IImage *image = GetImageAtIndex(i);
			if (!image)
				continue;

			// set the image position within the label
			int wide, tall;
			image->GetContentSize(wide, tall);
			if (maxWidth >= 0)
			{
				wide = maxWidth;
			}

			if (i == column)
			{
				// found the cell size, bail
				columnWide = wide;
				return;
			}

			xpos += wide;
		}
	}

	//=============================================================================
	// HPE_BEGIN:
	// [menglish] gets the local coordinates of a cell using the max width for every column
	//=============================================================================
	 
	void GetMaxCellBounds(int column, int &xpos, int &columnWide)
	{
		xpos = 0, columnWide = 0;
		int colCount = m_pListPanel->GetColumnCountBySection(m_iSectionID);
		for (int i = 0; i < colCount; i++)
		{
			int maxWidth = m_pListPanel->GetColumnWidthBySection(m_iSectionID, i);

			if (i == column)
			{
				// found the cell size, bail
				columnWide = maxWidth;
				return;
			}

			xpos += maxWidth;
		}
	}
	 
	//=============================================================================
	// HPE_END
	//=============================================================================

	virtual void SetOverrideColors( bool state )
	{
		m_bOverrideColors = state;
	}

	void SetShowColumns( bool bShow )
	{
		m_bShowColumns = bShow;
	}

	void SetItemBgHorizFillInset( int nHorizFillInset ){ m_nHorizFillInset = nHorizFillInset; }

private:
	SectionedListPanel *m_pListPanel;
	int m_iID;
	int m_iSectionID;
	KeyValues *m_pData;
	Color m_FgColor2;
	Color m_BgColor;
	Color m_ArmedFgColor1;
	Color m_ArmedFgColor2;
	Color m_OutOfFocusSelectedTextColor;
	Color m_ArmedBgColor;
	Color m_SelectionBG2Color;
	CUtlVector<vgui::TextImage *> m_TextImages;

	bool m_bSelected;
	bool m_bOverrideColors;
	bool m_bShowColumns;
	int m_nHorizFillInset;
};

}; // namespace vgui

DECLARE_BUILD_FACTORY( SectionedListPanel );

//-----------------------------------------------------------------------------
// Purpose: Constructor
//-----------------------------------------------------------------------------
SectionedListPanel::SectionedListPanel(vgui::Panel *parent, const char *name) : BaseClass(parent, name)
{
	m_pScrollBar = new ScrollBar(this, "SectionedScrollBar", true);
	m_pScrollBar->SetVisible(false);
	m_pScrollBar->AddActionSignalTarget(this);

	m_iEditModeItemID = 0;
	m_iEditModeColumn = 0;
	m_bSortNeeded = false;
	m_bVerticalScrollbarEnabled = true;
	m_iLineSpacing = DEFAULT_LINE_SPACING;
	m_iLineGap = 0;
	m_iSectionGap = DEFAULT_SECTION_GAP;

	m_pImageList = NULL;
	m_bDeleteImageListWhenDone = false;

	m_hHeaderFont = INVALID_FONT;
	m_hRowFont = INVALID_FONT;

	//=============================================================================
	// HPE_BEGIN:	
	//=============================================================================
	// [tj] Default clickability to true so existing controls aren't affected.
	m_clickable = true;
	// [tj] draw section headers by default so existing controls aren't affected.
	m_bDrawSectionHeaders = true;
	//=============================================================================
	// HPE_END
	//=============================================================================
}

//-----------------------------------------------------------------------------
// Purpose: Destructor
//-----------------------------------------------------------------------------
SectionedListPanel::~SectionedListPanel()
{
}

//-----------------------------------------------------------------------------
// Purpose: Sorts the list
//-----------------------------------------------------------------------------
void SectionedListPanel::ReSortList()
{
    m_SortedItems.RemoveAll();

	int sectionStart = 0;
	// layout the buttons
	for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++)
	{
		section_t &section = m_Sections[sectionIndex];
		sectionStart = m_SortedItems.Count();

		// find all the items in this section
		for( int i = m_Items.Head(); i != m_Items.InvalidIndex(); i = m_Items.Next( i ) )
		{
			if (m_Items[i]->GetSectionID() == m_Sections[sectionIndex].m_iID)
			{
				// insert the items sorted
				if (section.m_pSortFunc)
				{
					int insertionPoint = sectionStart;
					for (;insertionPoint < m_SortedItems.Count(); insertionPoint++)
					{
						if (section.m_pSortFunc(this, i, m_SortedItems[insertionPoint]->GetID()))
							break;
					}

					if (insertionPoint == m_SortedItems.Count())
					{
						m_SortedItems.AddToTail(m_Items[i]);
					}
					else
					{
						m_SortedItems.InsertBefore(insertionPoint, m_Items[i]);
					}
				}
				else
				{
					// just add to the end
					m_SortedItems.AddToTail(m_Items[i]);
				}
			}
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: iterates through and sets up the position of all the sections and items
//-----------------------------------------------------------------------------
void SectionedListPanel::PerformLayout()
{
	// lazy resort the list
	if (m_bSortNeeded)
	{
		ReSortList();
		m_bSortNeeded = false;
	}

	BaseClass::PerformLayout();
	
	LayoutPanels(m_iContentHeight);

	int cx, cy, cwide, ctall;
	GetBounds(cx, cy, cwide, ctall);
	if (m_iContentHeight > ctall && m_bVerticalScrollbarEnabled)
	{
		m_pScrollBar->SetVisible(true);
		m_pScrollBar->MoveToFront();

		m_pScrollBar->SetPos(cwide - m_pScrollBar->GetWide() - 2, 0);
		m_pScrollBar->SetSize(m_pScrollBar->GetWide(), ctall - 2);

		m_pScrollBar->SetRangeWindow(ctall);

		m_pScrollBar->SetRange(0, m_iContentHeight);
		m_pScrollBar->InvalidateLayout();
		m_pScrollBar->Repaint();

		// since we're just about to make the scrollbar visible, we need to re-layout
		// the buttons since they depend on the scrollbar size
		LayoutPanels(m_iContentHeight);
	}
	else
	{
		m_pScrollBar->SetValue(0);

		bool bWasVisible = m_pScrollBar->IsVisible();
		m_pScrollBar->SetVisible(false);

		// When we hide the scrollbar, we need to layout the buttons because they'll have more width to work with
		if ( bWasVisible )
		{
			LayoutPanels(m_iContentHeight);
		}
	}
}

//-----------------------------------------------------------------------------
// Purpose: lays out the sections and rows in the panel
//-----------------------------------------------------------------------------
void SectionedListPanel::LayoutPanels(int &contentTall)
{
	int tall = GetSectionTall();
	int x = 5, wide = GetWide() - 10;
	int y = 5;
	
	if (m_pScrollBar->IsVisible())
	{
		y -= m_pScrollBar->GetValue();
		wide -= m_pScrollBar->GetWide();
	}
    
    int iStart = -1;
    int iEnd = -1;

	// layout the buttons
	bool bFirstVisibleSection = true;
	for (int sectionIndex = 0; sectionIndex < m_Sections.Size(); sectionIndex++)
	{
		section_t &section = m_Sections[sectionIndex];

		iStart = -1;
		iEnd = -1;
        for (int i = 0; i < m_SortedItems.Count(); i++)
        {
            if (m_SortedItems[i]->GetSectionID() == m_Sections[sectionIndex].m_iID)
            {
                if (iStart == -1)
                    iStart = i;
                iEnd = i;
            }
        }

		// don't draw this section at all if there are no items in it
		if (iStart == -1 && !section.m_bAlwaysVisible)
		{
			section.m_pHeader->SetVisible(false);
			continue;
		}

		// Skip down a bit if this is not the first section to be drawn
		if ( bFirstVisibleSection )
			bFirstVisibleSection = false;
		else
			y += m_iSectionGap;

		//=============================================================================
		// HPE_BEGIN:
		// [tj] Only draw the header if it is enabled
		//=============================================================================
		int nMinNextSectionY = y + section.m_iMinimumHeight;
		if (m_bDrawSectionHeaders)
		{
			// draw the header
			section.m_pHeader->SetBounds(x, y, wide, tall);
			section.m_pHeader->SetVisible(true);
			y += tall;
		}
		else
		{
			section.m_pHeader->SetVisible(false);			
		}
		//=============================================================================
		// HPE_END
		//=============================================================================

		if (iStart == -1 && section.m_bAlwaysVisible)
		{
		}
		else
		{
			// arrange all the items in this section underneath
			for (int i = iStart; i <= iEnd; i++)
			{
				CItemButton *item = m_SortedItems[i]; //items[i];
				item->SetBounds(x, y, wide, m_iLineSpacing);
			
				// setup edit mode
				if (m_hEditModePanel.Get() && m_iEditModeItemID == item->GetID())
				{
					int cx, cwide;
					item->GetCellBounds(1, cx, cwide);
					m_hEditModePanel->SetBounds(cx, y, cwide, tall);
				}

				y += m_iLineSpacing + m_iLineGap;
			}
		}

		// Add space, if needed to fulfill minimum requested content height
		if ( y < nMinNextSectionY )
			y = nMinNextSectionY;
	}

	// calculate height
	contentTall = y;
	if (m_pScrollBar->IsVisible())
	{
		contentTall += m_pScrollBar->GetValue();
	}
}

//-----------------------------------------------------------------------------
// Purpose: Ensures that the specified item is visible in the display
//-----------------------------------------------------------------------------
void SectionedListPanel::ScrollToItem(int iItem)
{
	int tall = GetSectionTall();
	int itemX, itemY ;
	int nCurrentValue = m_pScrollBar->GetValue();

	// find out where the item is
	m_Items[iItem]->GetPos(itemX, itemY);
	// add in the current scrollbar position
	itemY += nCurrentValue;

	// compare that in the list
	int cx, cy, cwide, ctall;
	GetBounds(cx, cy, cwide, ctall);
	if (m_iContentHeight > ctall)
	{
        if (itemY < nCurrentValue)
		{
			// scroll up
            m_pScrollBar->SetValue(itemY);
		}
        else if (itemY > nCurrentValue + ctall - tall)
		{
			// scroll down
            m_pScrollBar->SetValue(itemY - ctall + tall);
		}
		else
		{
			// keep the current value
		}
	}
	else
	{
		// area isn't big enough, just remove the scrollbar
		m_pScrollBar->SetValue(0);
	}

	// reset scrollbar
	Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: sets background color & border
//-----------------------------------------------------------------------------
void SectionedListPanel::ApplySchemeSettings(IScheme *pScheme)
{
	BaseClass::ApplySchemeSettings(pScheme);

	SetBgColor(GetSchemeColor("SectionedListPanel.BgColor", GetBgColor(), pScheme));
	SetBorder(pScheme->GetBorder("ButtonDepressedBorder"));

	FOR_EACH_LL( m_Items, j )
	{
		m_Items[j]->SetShowColumns( m_bShowColumns );
	}	
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void SectionedListPanel::SetHeaderFont( HFont hFont )
{
	m_hHeaderFont = hFont;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
HFont SectionedListPanel::GetHeaderFont( void ) const
{
	return m_hHeaderFont;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void SectionedListPanel::SetRowFont( HFont hFont )
{
	m_hRowFont = hFont;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
HFont SectionedListPanel::GetRowFont( void ) const
{
	return m_hRowFont;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void SectionedListPanel::ApplySettings(KeyValues *inResourceData)
{
	BaseClass::ApplySettings(inResourceData);
	m_iLineSpacing = inResourceData->GetInt("linespacing", 0);
	if (!m_iLineSpacing)
	{
		m_iLineSpacing = DEFAULT_LINE_SPACING;
	}
	if (IsProportional())
	{
		m_iLineSpacing = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iLineSpacing);
	}

	m_iSectionGap = inResourceData->GetInt("sectiongap", 0);
	if (!m_iSectionGap)
	{
		m_iSectionGap = DEFAULT_SECTION_GAP;
	}
	m_iLineGap = inResourceData->GetInt( "linegap", 0 );
	if (IsProportional())
	{
		m_iSectionGap = scheme()->GetProportionalScaledValueEx(GetScheme(), m_iSectionGap);
		m_iLineGap = scheme()->GetProportionalScaledValueEx( GetScheme(), m_iLineGap );
	}
}

//-----------------------------------------------------------------------------
// Purpose: passes on proportional state to children
//-----------------------------------------------------------------------------
void SectionedListPanel::SetProportional(bool state)
{
	BaseClass::SetProportional(state);

	// now setup the section headers and items
	int i;
	for (i = 0; i < m_Sections.Count(); i++)
	{
		m_Sections[i].m_pHeader->SetProportional(state);
	}	
	FOR_EACH_LL( m_Items, j )
	{
		m_Items[j]->SetProportional(state);
	}	
}

//-----------------------------------------------------------------------------
// Purpose: sets whether or not the vertical scrollbar should ever be displayed
//-----------------------------------------------------------------------------
void SectionedListPanel::SetVerticalScrollbar(bool state)
{
	m_bVerticalScrollbarEnabled = state;
}

//-----------------------------------------------------------------------------
// Purpose: adds a new section
//-----------------------------------------------------------------------------
void SectionedListPanel::AddSection(int sectionID, const char *name, SectionSortFunc_t sortFunc)
{
	SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID);
	AddSection(sectionID, header, sortFunc);
}

//-----------------------------------------------------------------------------
// Purpose: adds a new section
//-----------------------------------------------------------------------------
void SectionedListPanel::AddSection(int sectionID, const wchar_t *name, SectionSortFunc_t sortFunc)
{
	SectionedListPanelHeader *header = new SectionedListPanelHeader(this, name, sectionID);
	AddSection(sectionID, header, sortFunc);
}

//-----------------------------------------------------------------------------
// Purpose: Adds a new section, given object
//-----------------------------------------------------------------------------
void SectionedListPanel::AddSection(int sectionID, SectionedListPanelHeader *header, SectionSortFunc_t sortFunc)
{
	header = SETUP_PANEL( header );
	int index = m_Sections.AddToTail();
	m_Sections[index].m_iID = sectionID;
	m_Sections[index].m_pHeader = header;
	m_Sections[index].m_pSortFunc = sortFunc;
	m_Sections[index].m_bAlwaysVisible = false;
	m_Sections[index].m_iMinimumHeight = 0;
}

//-----------------------------------------------------------------------------
// Purpose: removes all the sections from the current panel
//-----------------------------------------------------------------------------
void SectionedListPanel::RemoveAllSections()
{
	for (int i = 0; i < m_Sections.Count(); i++)
	{
		if (!m_Sections.IsValidIndex(i))
			continue;

		m_Sections[i].m_pHeader->SetVisible(false);
		m_Sections[i].m_pHeader->MarkForDeletion();
	}

	m_Sections.RemoveAll();
	m_Sections.Purge();
    m_SortedItems.RemoveAll();

	InvalidateLayout();
    ReSortList();
}

//-----------------------------------------------------------------------------
// Purpose: adds a new column to a section
//-----------------------------------------------------------------------------
bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const char *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ )
{
	wchar_t wtext[64];
	wchar_t *pwtext = g_pVGuiLocalize->Find(columnText);
	if (!pwtext)
	{
		g_pVGuiLocalize->ConvertANSIToUnicode(columnText, wtext, sizeof(wtext));
		pwtext = wtext;
	}
	return AddColumnToSection(sectionID, columnName, pwtext, columnFlags, width, fallbackFont );
}

//-----------------------------------------------------------------------------
// Purpose: as above but with wchar_t's
//-----------------------------------------------------------------------------
bool SectionedListPanel::AddColumnToSection(int sectionID, const char *columnName, const wchar_t *columnText, int columnFlags, int width, HFont fallbackFont /*= INVALID_FONT*/ )
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0)
		return false;
	section_t &section = m_Sections[index];

	// add the new column to the sections' list
	index = section.m_Columns.AddToTail();
	column_t &column = section.m_Columns[index];

	Q_strncpy(column.m_szColumnName, columnName, sizeof(column.m_szColumnName));
	wcsncpy(column.m_szColumnText, columnText,  sizeof(column.m_szColumnText) / sizeof(wchar_t));
	column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0;
	column.m_iColumnFlags = columnFlags;
	column.m_iWidth = width;
	column.m_hFallbackFont = fallbackFont;
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: modifies the text in an existing column
//-----------------------------------------------------------------------------
bool SectionedListPanel::ModifyColumn(int sectionID, const char *columnName, const wchar_t *columnText)
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0)
		return false;
	section_t &section = m_Sections[index];

	// find the specified column
	int columnIndex;
	for (columnIndex = 0; columnIndex < section.m_Columns.Count(); columnIndex++)
	{
		if (!stricmp(section.m_Columns[columnIndex].m_szColumnName, columnName))
			break;
	}
	if (!section.m_Columns.IsValidIndex(columnIndex))
		return false;
	column_t &column = section.m_Columns[columnIndex];

	// modify the text
	wcsncpy(column.m_szColumnText, columnText, sizeof(column.m_szColumnText) / sizeof(wchar_t));
	column.m_szColumnText[sizeof(column.m_szColumnText) / sizeof(wchar_t) - 1] = 0;
	section.m_pHeader->InvalidateLayout();
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: adds an item to the list; returns itemID
//-----------------------------------------------------------------------------
int SectionedListPanel::AddItem(int sectionID, const KeyValues *data)
{
	int itemID = GetNewItemButton();
	ModifyItem(itemID, sectionID, data);

	// not sorted but in list
	m_SortedItems.AddToTail(m_Items[itemID]);
    m_bSortNeeded = true;

	return itemID;
}

//-----------------------------------------------------------------------------
// Purpose: modifies an existing item; returns false if the item does not exist
//-----------------------------------------------------------------------------
bool SectionedListPanel::ModifyItem(int itemID, int sectionID, const KeyValues *data)
{
	if ( !m_Items.IsValidIndex(itemID) )
		return false;

	InvalidateLayout();
	m_Items[itemID]->SetSectionID(sectionID);
	m_Items[itemID]->SetData(data);
	m_Items[itemID]->InvalidateLayout();
    m_bSortNeeded = true;
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void SectionedListPanel::SetItemFgColor( int itemID, Color color )
{
	Assert( m_Items.IsValidIndex(itemID) );
	if ( !m_Items.IsValidIndex(itemID) )
		return;

	m_Items[itemID]->SetFgColor( color );
	m_Items[itemID]->SetOverrideColors( true );
	m_Items[itemID]->InvalidateLayout();
}

//=============================================================================
// HPE_BEGIN:
// [menglish] Setter for the background color similar to the foreground color
//=============================================================================
 
void SectionedListPanel::SetItemBgColor( int itemID, Color color )
{
	Assert( m_Items.IsValidIndex(itemID) );
	if ( !m_Items.IsValidIndex(itemID) )
		return;

	m_Items[itemID]->SetBgColor( color );
	m_Items[itemID]->SetPaintBackgroundEnabled( true );
	m_Items[itemID]->SetOverrideColors( true );
	m_Items[itemID]->InvalidateLayout();
}

void SectionedListPanel::SetItemBgHorizFillInset( int itemID, int nInset )
{
	Assert( m_Items.IsValidIndex( itemID ) );
	if ( !m_Items.IsValidIndex( itemID ) )
		return;

	m_Items[itemID]->SetItemBgHorizFillInset( nInset );
}

void SectionedListPanel::SetItemFont( int itemID, HFont font )
{
	Assert( m_Items.IsValidIndex(itemID) );
	if ( !m_Items.IsValidIndex(itemID) )
		return;

	m_Items[itemID]->SetFont( font );
}

void SectionedListPanel::SetItemEnabled( int itemID, bool bEnabled )
{
	Assert( m_Items.IsValidIndex(itemID) );
	if ( !m_Items.IsValidIndex(itemID) )
		return;

	m_Items[itemID]->SetEnabled( bEnabled );
}
 
//=============================================================================
// HPE_END
//=============================================================================
//-----------------------------------------------------------------------------
// Purpose: sets the color of a section text & underline
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSectionFgColor(int sectionID, Color color)
{
	if (!m_Sections.IsValidIndex(sectionID))
		return;

	m_Sections[sectionID].m_pHeader->SetColor(color);
}
//-----------------------------------------------------------------------------
// Purpose: added so you can change the divider color AFTER the main color.
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSectionDividerColor( int sectionID, Color color)
{
	if (!m_Sections.IsValidIndex(sectionID))
		return;

	m_Sections[sectionID].m_pHeader->SetDividerColor(color);
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSectionDrawDividerBar( int sectionID, bool bDraw )
{
	if ( !m_Sections.IsValidIndex( sectionID ) )
		return;

	m_Sections[sectionID].m_pHeader->DrawDividerBar( bDraw );
}

//-----------------------------------------------------------------------------
// Purpose: forces a section to always be visible
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSectionAlwaysVisible(int sectionID, bool visible)
{
	if (!m_Sections.IsValidIndex(sectionID))
		return;

	m_Sections[sectionID].m_bAlwaysVisible = visible;
}
void SectionedListPanel::SetFontSection(int sectionID, HFont font)
{
	if (!m_Sections.IsValidIndex(sectionID))
		return;

	m_Sections[sectionID].m_pHeader->SetFont(font);
}
void SectionedListPanel::SetSectionMinimumHeight(int sectionID, int iMinimumHeight)
{
	if (!m_Sections.IsValidIndex(sectionID))
		return;

	m_Sections[sectionID].m_iMinimumHeight = iMinimumHeight;
	InvalidateLayout();
}

//-----------------------------------------------------------------------------
// Purpose: removes an item from the list; returns false if the item does not exist or is already removed
//-----------------------------------------------------------------------------
bool SectionedListPanel::RemoveItem(int itemID)
{
	if ( !m_Items.IsValidIndex(itemID) )
		return false;

	m_SortedItems.FindAndRemove(m_Items[itemID]);
    m_bSortNeeded = true;

	m_Items[itemID]->MarkForDeletion();
	m_Items.Remove(itemID);

	InvalidateLayout();
	
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: returns the number of columns in a section
//-----------------------------------------------------------------------------
int SectionedListPanel::GetColumnCountBySection(int sectionID)
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0)
		return NULL;

	return m_Sections[index].m_Columns.Size();
}

//-----------------------------------------------------------------------------
// Purpose: returns the name of a column by section and column index; returns NULL if there are no more columns
//			valid range of columnIndex is [0, GetColumnCountBySection)
//-----------------------------------------------------------------------------
const char *SectionedListPanel::GetColumnNameBySection(int sectionID, int columnIndex)
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size())
		return NULL;

	return m_Sections[index].m_Columns[columnIndex].m_szColumnName;
}

//-----------------------------------------------------------------------------
// Purpose: returns the text for a column by section and column index
//-----------------------------------------------------------------------------
const wchar_t *SectionedListPanel::GetColumnTextBySection(int sectionID, int columnIndex)
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0 || columnIndex >= m_Sections[index].m_Columns.Size())
		return NULL;
	
	return m_Sections[index].m_Columns[columnIndex].m_szColumnText;
}

//-----------------------------------------------------------------------------
// Purpose: returns the type of a column by section and column index
//-----------------------------------------------------------------------------
int SectionedListPanel::GetColumnFlagsBySection(int sectionID, int columnIndex)
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0)
		return 0;

	if (columnIndex >= m_Sections[index].m_Columns.Size())
		return 0;

	return m_Sections[index].m_Columns[columnIndex].m_iColumnFlags;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int SectionedListPanel::GetColumnWidthBySection(int sectionID, int columnIndex)
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0)
		return 0;

	if (columnIndex >= m_Sections[index].m_Columns.Size())
		return 0;

	return m_Sections[index].m_Columns[columnIndex].m_iWidth;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void SectionedListPanel::SetColumnWidthBySection(int sectionID, const char *columnName, int iWidth)
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0)
		return;
	section_t &section = m_Sections[index];

	// find the specified column
	int columnIndex;
	for (columnIndex = 0; columnIndex < section.m_Columns.Count(); columnIndex++)
	{
		if (!stricmp(section.m_Columns[columnIndex].m_szColumnName, columnName))
			break;
	}
	if (!section.m_Columns.IsValidIndex(columnIndex))
		return;

	column_t &column = section.m_Columns[columnIndex];
	column.m_iWidth = iWidth;

	// make sure the header for this section reloads with the new width
	section.m_pHeader->InvalidateLayout();
}

//=============================================================================
// HPE_BEGIN:
// [menglish] Gets the column index by the string identifier
//=============================================================================

int SectionedListPanel::GetColumnIndexByName(int sectionID, char* name)
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0)
		return 0;

	for ( int columnIndex = 0; columnIndex < m_Sections[index].m_Columns.Count(); ++ columnIndex)
	{
		if( !V_strcmp( m_Sections[index].m_Columns[columnIndex].m_szColumnName, name ) )
			return columnIndex;
	}
	
	return -1;
}
 
//=============================================================================
// HPE_END
//=============================================================================

//-----------------------------------------------------------------------------
// Purpose: returns -1 if section not found
//-----------------------------------------------------------------------------
int SectionedListPanel::FindSectionIndexByID(int sectionID)
{
	for (int i = 0; i < m_Sections.Size(); i++)
	{
		if (m_Sections[i].m_iID == sectionID)
		{
			return i;
		}
	}

	return -1;
}

//-----------------------------------------------------------------------------
// Purpose: Called when the scrollbar is moved
//-----------------------------------------------------------------------------
void SectionedListPanel::OnSliderMoved()
{
	InvalidateLayout();
	Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: Scrolls the list according to the mouse wheel movement
//-----------------------------------------------------------------------------
void SectionedListPanel::OnMouseWheeled(int delta)
{
	if (m_hEditModePanel.Get())
	{
		// ignore mouse wheel in edit mode, forward right up to parent
		CallParentFunction(new KeyValues("MouseWheeled", "delta", delta));
		return;
	}

	// scroll the window based on the delta
	int val = m_pScrollBar->GetValue();
	val -= (delta * BUTTON_HEIGHT_DEFAULT * 3);
	m_pScrollBar->SetValue(val);
}

//-----------------------------------------------------------------------------
// Purpose: Resets the scrollbar position on size change
//-----------------------------------------------------------------------------
void SectionedListPanel::OnSizeChanged(int wide, int tall)
{
	BaseClass::OnSizeChanged(wide, tall);
	m_pScrollBar->SetValue(0);
	InvalidateLayout();
	Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: deselects any items
//-----------------------------------------------------------------------------
void SectionedListPanel::OnMousePressed(MouseCode code)
{
	//=============================================================================
	// HPE_BEGIN:
	// [tj] Only do this if clicking is enabled.
	//=============================================================================
	if (m_clickable){
		ClearSelection();
	}
	//=============================================================================
	// HPE_END
	//=============================================================================}
}
//-----------------------------------------------------------------------------
// Purpose: deselects any items
//-----------------------------------------------------------------------------
void SectionedListPanel::ClearSelection( void )
{
	SetSelectedItem((CItemButton *)NULL);
}

void SectionedListPanel::MoveSelectionDown( void )
{
	int itemID = GetSelectedItem();
	if (itemID == -1)
		return;

	if (!m_SortedItems.Count()) // if the list has been emptied
		return;

	int i;
	for (i = 0; i < m_SortedItems.Count(); i++)
	{
		if (m_SortedItems[i]->GetID() == itemID)
			break;
	}

	Assert(i != m_SortedItems.Count());

	// we're already on the end
	if (i >= m_SortedItems.Count() - 1)
		return;

	int newItemID = m_SortedItems[i + 1]->GetID();
	SetSelectedItem(m_Items[newItemID]);
	ScrollToItem(newItemID);
}

void SectionedListPanel::MoveSelectionUp( void )
{
	int itemID = GetSelectedItem();
	if (itemID == -1)
		return;

	if (!m_SortedItems.Count()) // if the list has been emptied
		return;

	int i;
	for (i = 0; i < m_SortedItems.Count(); i++)
	{
		if (m_SortedItems[i]->GetID() == itemID)
			break;
	}

	Assert(i != m_SortedItems.Count());

	// we're already on the end
	if (i == 0 || i >= m_SortedItems.Count() )
		return;

	int newItemID = m_SortedItems[i - 1]->GetID();
	SetSelectedItem(m_Items[newItemID]);
	ScrollToItem(newItemID);
}

void SectionedListPanel::NavigateTo( void )
{
	BaseClass::NavigateTo();

	if ( m_SortedItems.Count() )
	{
		int nItemID = m_SortedItems[ 0 ]->GetID();
		SetSelectedItem( m_Items[ nItemID ] );
		ScrollToItem( nItemID );
	}
	
	RequestFocus();
}

//-----------------------------------------------------------------------------
// Purpose: arrow key movement handler
//-----------------------------------------------------------------------------
void SectionedListPanel::OnKeyCodePressed( KeyCode code )
{
	if (m_hEditModePanel.Get())
	{
		// ignore arrow keys in edit mode
		// forward right up to parent so that tab focus change doesn't occur
		CallParentFunction(new KeyValues("KeyCodePressed", "code", code));
		return;
	}

	int buttonTall = GetSectionTall();

	ButtonCode_t nButtonCode = GetBaseButtonCode( code );
		
	if ( nButtonCode == KEY_XBUTTON_DOWN || 
		 nButtonCode == KEY_XSTICK1_DOWN ||
		 nButtonCode == KEY_XSTICK2_DOWN ||
		 nButtonCode == STEAMCONTROLLER_DPAD_DOWN ||
		 code == KEY_DOWN )
	{
		int itemID = GetSelectedItem();
		MoveSelectionDown();
		if ( itemID != GetSelectedItem() )
		{
			// Only eat the input if it did something
			return;
		}
	}
	else if ( nButtonCode == KEY_XBUTTON_UP || 
			  nButtonCode == KEY_XSTICK1_UP ||
			  nButtonCode == KEY_XSTICK2_UP ||
			  nButtonCode == STEAMCONTROLLER_DPAD_UP ||
			  code == KEY_UP)
	{
		int itemID = GetSelectedItem();
		MoveSelectionUp();
		if ( itemID != GetSelectedItem() )
		{
			// Only eat the input if it did something
			return;
		}
	}
    else if (code == KEY_PAGEDOWN)
    {
        // calculate info for # of rows
        int cx, cy, cwide, ctall;
        GetBounds(cx, cy, cwide, ctall);

        int rowsperpage = ctall/buttonTall;

        int itemID = GetSelectedItem();
        int lastValidItem = itemID;
        int secID = m_Items[itemID]->GetSectionID();
        int i=0;
		int row = m_SortedItems.Find(m_Items[itemID]);

		while ( i < rowsperpage )
        {
			if ( m_SortedItems.IsValidIndex(++row) )
            {
				itemID = m_SortedItems[row]->GetID();
                lastValidItem = itemID;
                i++;

                // if we switched sections, then count the section header as a row
                if (m_Items[itemID]->GetSectionID() != secID)
                {
                    secID = m_Items[itemID]->GetSectionID();
                    i++;
                }
            }
			else
            {
                itemID = lastValidItem;
                break;
            }
        }
        SetSelectedItem(m_Items[itemID]);
        ScrollToItem(itemID);
		return;
    }
    else if (code == KEY_PAGEUP)
    {
        // calculate info for # of rows
        int cx, cy, cwide, ctall;
        GetBounds(cx, cy, cwide, ctall);
        int rowsperpage = ctall/buttonTall;

        int itemID = GetSelectedItem();
        int lastValidItem = itemID;
        int secID = m_Items[itemID]->GetSectionID();
        int i=0;
		int row = m_SortedItems.Find(m_Items[itemID]);
        while ( i < rowsperpage )
        {
			if ( m_SortedItems.IsValidIndex(--row) )
            {
				itemID = m_SortedItems[row]->GetID();
                lastValidItem = itemID;
                i++;

                // if we switched sections, then count the section header as a row
                if (m_Items[itemID]->GetSectionID() != secID)
                {
                    secID = m_Items[itemID]->GetSectionID();
                    i++;
                }
            }
			else
            {
                SetSelectedItem(m_Items[lastValidItem]);
                m_pScrollBar->SetValue(0);
                return;
            }
        }
        SetSelectedItem(m_Items[itemID]);
        ScrollToItem(itemID);
		return;
    }
	else if ( code == KEY_ENTER || nButtonCode == KEY_XBUTTON_A || nButtonCode == STEAMCONTROLLER_A )
	{
		Panel *pSelectedItem = m_hSelectedItem;
		if ( pSelectedItem )
		{
			pSelectedItem->OnMousePressed( MOUSE_LEFT );
		}
		return;
	}

	BaseClass::OnKeyCodePressed( code );
}


//-----------------------------------------------------------------------------
// Purpose: Clears the list
//-----------------------------------------------------------------------------
void SectionedListPanel::DeleteAllItems()
{
	FOR_EACH_LL( m_Items, i )
	{
		m_Items[i]->SetVisible(false);
		m_Items[i]->Clear();

		// don't delete, move to free list
		int freeIndex = m_FreeItems.AddToTail();
		m_FreeItems[freeIndex] = m_Items[i];
	}

	m_Items.RemoveAll();
	m_SortedItems.RemoveAll();
	m_hSelectedItem = NULL;
	InvalidateLayout();
    m_bSortNeeded = true;
}

//-----------------------------------------------------------------------------
// Purpose: Changes the current list selection
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSelectedItem(CItemButton *item)
{
	if (m_hSelectedItem.Get() == item)
		return;

	// deselect the current item
	if (m_hSelectedItem.Get())
	{
		m_hSelectedItem->SetSelected(false);
	}

	// set the new item
	m_hSelectedItem = item;
	if (m_hSelectedItem.Get())
	{
		m_hSelectedItem->SetSelected(true);
	}

	Repaint();
	PostActionSignal(new KeyValues("ItemSelected", "itemID", m_hSelectedItem.Get() ? m_hSelectedItem->GetID() : -1));
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int SectionedListPanel::GetSelectedItem()
{
	if (m_hSelectedItem.Get())
	{
		return m_hSelectedItem->GetID();
	}
	return -1;
}

//-----------------------------------------------------------------------------
// Purpose: sets which item is currently selected
//-----------------------------------------------------------------------------
void SectionedListPanel::SetSelectedItem(int itemID)
{
	if ( m_Items.IsValidIndex(itemID) )
	{
		SetSelectedItem(m_Items[itemID]);
	}
}

//-----------------------------------------------------------------------------
// Purpose: returns the data of a selected item
//-----------------------------------------------------------------------------
KeyValues *SectionedListPanel::GetItemData(int itemID)
{
	Assert(m_Items.IsValidIndex(itemID));
	if ( !m_Items.IsValidIndex(itemID) )
		return NULL;

	return m_Items[itemID]->GetData();
}

//-----------------------------------------------------------------------------
// Purpose: returns what section an item is in
//-----------------------------------------------------------------------------
int SectionedListPanel::GetItemSection(int itemID)
{
	if ( !m_Items.IsValidIndex(itemID) )
		return -1;

	return m_Items[itemID]->GetSectionID();
}

//-----------------------------------------------------------------------------
// Purpose: returns true if the itemID is valid for use
//-----------------------------------------------------------------------------
bool SectionedListPanel::IsItemIDValid(int itemID)
{
	return m_Items.IsValidIndex(itemID);
}

//-----------------------------------------------------------------------------
// Purpose: returns true if the itemID is valid for use
//-----------------------------------------------------------------------------
int SectionedListPanel::GetHighestItemID()
{
	return m_Items.MaxElementIndex();
}

//-----------------------------------------------------------------------------
// Purpose: item iterators
//-----------------------------------------------------------------------------
int SectionedListPanel::GetItemCount()
{
	return m_SortedItems.Count();
}

//-----------------------------------------------------------------------------
// Purpose: item iterators
//-----------------------------------------------------------------------------
int SectionedListPanel::GetItemIDFromRow(int row)
{
	if ( !m_SortedItems.IsValidIndex(row) )
		return -1;

	return m_SortedItems[row]->GetID();
}

//-----------------------------------------------------------------------------
// Purpose: returns the row that this itemID occupies. -1 if the itemID is invalid
//-----------------------------------------------------------------------------
int SectionedListPanel::GetRowFromItemID(int itemID)
{
	for (int i = 0; i < m_SortedItems.Count(); i++)
	{
		if ( m_SortedItems[i]->GetID() == itemID )
		{
			return i;
		}
	}

	return -1;
}

//-----------------------------------------------------------------------------
// Purpose: gets the local coordinates of a cell
//-----------------------------------------------------------------------------
bool SectionedListPanel::GetCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall)
{
	x = y = wide = tall = 0;
	if ( !IsItemIDValid(itemID) )
		return false;

	// get the item
	CItemButton *item = m_Items[itemID];

	if ( !item->IsVisible() )
		return false;

	//!! ignores column for now
	item->GetBounds(x, y, wide, tall);
	item->GetCellBounds(column, x, wide);
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: gets the local coordinates of a section header
//-----------------------------------------------------------------------------
bool SectionedListPanel::GetSectionHeaderBounds(int sectionID, int &x, int &y, int &wide, int &tall)
{
	x = y = wide = tall = 0;
	int index = FindSectionIndexByID(sectionID);
	if (index < 0 || !m_Sections[index].m_pHeader )
		return false;

	m_Sections[index].m_pHeader->GetBounds( x, y, wide, tall );
	return true;
}

//=============================================================================
// HPE_BEGIN:
// [menglish]	Gets the local coordinates of a cell using the max width for every column
//				Gets the local coordinates of a cell
//=============================================================================
 
bool SectionedListPanel::GetMaxCellBounds(int itemID, int column, int &x, int &y, int &wide, int &tall)
{
	x = y = wide = tall = 0;
	if ( !IsItemIDValid(itemID) )
		return false;

	// get the item
	CItemButton *item = m_Items[itemID];

	if ( !item->IsVisible() )
		return false;

	//!! ignores column for now
	item->GetBounds(x, y, wide, tall);
	item->GetMaxCellBounds(column, x, wide);
	return true;
}

//-----------------------------------------------------------------------------
// Purpose: gets the local coordinates of a cell
//-----------------------------------------------------------------------------
bool SectionedListPanel::GetItemBounds(int itemID, int &x, int &y, int &wide, int &tall)
{
	x = y = wide = tall = 0;
	if ( !IsItemIDValid(itemID) )
		return false;

	// get the item
	CItemButton *item = m_Items[itemID];

	if ( !item->IsVisible() )
		return false;

	//!! ignores column for now
	item->GetBounds(x, y, wide, tall);
	return true;
}
 
//=============================================================================
// HPE_END
//=============================================================================

//-----------------------------------------------------------------------------
// Purpose: forces an item to redraw
//-----------------------------------------------------------------------------
void SectionedListPanel::InvalidateItem(int itemID)
{
	if ( !IsItemIDValid(itemID) )
		return;

	m_Items[itemID]->InvalidateLayout();
	m_Items[itemID]->Repaint();
}

//-----------------------------------------------------------------------------
// Purpose: set up a field for editing
//-----------------------------------------------------------------------------
void SectionedListPanel::EnterEditMode(int itemID, int column, vgui::Panel *editPanel)
{
	m_hEditModePanel = editPanel;
	m_iEditModeItemID = itemID;
	m_iEditModeColumn = column;
	editPanel->SetParent(this);
	editPanel->SetVisible(true);
	editPanel->RequestFocus();
	editPanel->MoveToFront();
	InvalidateLayout();
}

//-----------------------------------------------------------------------------
// Purpose: leaves editing mode
//-----------------------------------------------------------------------------
void SectionedListPanel::LeaveEditMode()
{
	if (m_hEditModePanel.Get())
	{
		InvalidateItem(m_iEditModeItemID);
		m_hEditModePanel->SetVisible(false);
		m_hEditModePanel->SetParent((Panel *)NULL);
		m_hEditModePanel = NULL;
	}
}

//-----------------------------------------------------------------------------
// Purpose: returns true if we are currently in inline editing mode
//-----------------------------------------------------------------------------
bool SectionedListPanel::IsInEditMode()
{
	return (m_hEditModePanel.Get() != NULL);
}

//-----------------------------------------------------------------------------
// Purpose: list used to match indexes in image columns to image pointers
//-----------------------------------------------------------------------------
void SectionedListPanel::SetImageList(ImageList *imageList, bool deleteImageListWhenDone)
{
	m_bDeleteImageListWhenDone = deleteImageListWhenDone;
	m_pImageList = imageList;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void SectionedListPanel::OnSetFocus()
{
	if (m_hSelectedItem.Get())
	{
        m_hSelectedItem->RequestFocus();
	}
    else
	{
        BaseClass::OnSetFocus();
	}
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
int SectionedListPanel::GetSectionTall()
{
	if (m_Sections.Count())
	{
		HFont font = m_Sections[0].m_pHeader->GetFont();
		if (font != INVALID_FONT)
		{
			return surface()->GetFontTall(font) + BUTTON_HEIGHT_SPACER;
		}
	}

	return BUTTON_HEIGHT_DEFAULT;
}

//-----------------------------------------------------------------------------
// Purpose: returns the size required to fully draw the contents of the panel
//-----------------------------------------------------------------------------
void SectionedListPanel::GetContentSize(int &wide, int &tall)
{
	// make sure our layout is done
	if (IsLayoutInvalid())
	{
		if (m_bSortNeeded)
		{
			ReSortList();
			m_bSortNeeded = false;
		}
		LayoutPanels(m_iContentHeight);
	}

	wide = GetWide();
	tall = m_iContentHeight;
}

//-----------------------------------------------------------------------------
// Purpose: Returns the index of a new item button
//-----------------------------------------------------------------------------
int SectionedListPanel::GetNewItemButton()
{
	int itemID = m_Items.AddToTail();
	if (m_FreeItems.Count())
	{
		// reusing an existing CItemButton
		m_Items[itemID] = m_FreeItems[m_FreeItems.Head()];
		m_Items[itemID]->SetID(itemID);
		m_Items[itemID]->SetVisible(true);
		m_FreeItems.Remove(m_FreeItems.Head());
	}
	else
	{
		// create a new CItemButton
		m_Items[itemID] = SETUP_PANEL(new CItemButton(this, itemID));
		m_Items[itemID]->SetShowColumns( m_bShowColumns );
	}

	// Gross.  Le's hope this isn't the only property that doesn't get defaulted
	// properly when an item is recycled.....
	m_Items[itemID]->SetEnabled( true );

	return itemID;
}

//-----------------------------------------------------------------------------
// Purpose: Returns fallback font to use for text image for this column
// Input  : sectionID - 
//			columnIndex - 
// Output : virtual HFont
//-----------------------------------------------------------------------------
HFont SectionedListPanel::GetColumnFallbackFontBySection( int sectionID, int columnIndex )
{
	int index = FindSectionIndexByID(sectionID);
	if (index < 0)
		return INVALID_FONT;

	if (columnIndex >= m_Sections[index].m_Columns.Size())
		return INVALID_FONT;

	return m_Sections[index].m_Columns[columnIndex].m_hFallbackFont;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
Color *SectionedListPanel::GetColorOverrideForCell( int sectionID, int itemID, int columnID )
{
	FOR_EACH_VEC( m_ColorOverrides, i )
	{
		if ( ( m_ColorOverrides[i].m_SectionID == sectionID ) && 
			 ( m_ColorOverrides[i].m_ItemID == itemID ) && 
			 ( m_ColorOverrides[i].m_ColumnID == columnID ) )
		{
			return &(m_ColorOverrides[i].m_clrOverride);
		}
	}

	return NULL;
}

//-----------------------------------------------------------------------------
// Purpose: 
//-----------------------------------------------------------------------------
void SectionedListPanel::SetColorOverrideForCell( int sectionID, int itemID, int columnID, Color clrOverride )
{
	// is this value already in the override list?
	FOR_EACH_VEC( m_ColorOverrides, i )
	{
		if ( ( m_ColorOverrides[i].m_SectionID == sectionID ) &&
			( m_ColorOverrides[i].m_ItemID == itemID ) &&
			( m_ColorOverrides[i].m_ColumnID == columnID ) )
		{
			m_ColorOverrides[i].m_clrOverride = clrOverride;
			return;
		}
	}

	int iIndex = m_ColorOverrides.AddToTail();
	m_ColorOverrides[iIndex].m_SectionID = sectionID;
	m_ColorOverrides[iIndex].m_ItemID = itemID;
	m_ColorOverrides[iIndex].m_ColumnID = columnID;
	m_ColorOverrides[iIndex].m_clrOverride = clrOverride;
}