//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Multi-purpose menu for matchmaking dialogs, navigable with the xbox controller.
//
//=============================================================================//

#include "engine/imatchmaking.h"
#include "GameUI_Interface.h"
#include "vgui_controls/Label.h"
#include "vgui_controls/ImagePanel.h"
#include "vgui/ILocalize.h"
#include "vgui/ISurface.h"
#include "KeyValues.h"
#include "dialogmenu.h"
#include "BasePanel.h"
#include "vgui_controls/ImagePanel.h"
#include "iachievementmgr.h"			// for iachievement abstract class in CAchievementItem
#include "achievementsdialog.h"			// for helper functions used by both pc and xbox achievements

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

//-----------------------------------------------------------------------
// Base class representing a generic menu item. Supports two text labels,
// where the first label is the "action" text and the second is an optional
// description of the action.
//-----------------------------------------------------------------------
CMenuItem::CMenuItem( CDialogMenu *pParent, const char *pTitle, const char *pDescription ) 
	: BaseClass( pParent, "MenuItem" )
{
	// Quiet "parent not sized yet" spew
	SetSize( 10, 10 );

	m_pParent = pParent;

	m_bEnabled = true;
	m_nDisabledAlpha = 30;

	m_pTitle = new vgui::Label( this, "MenuItemText", pTitle );
	m_pDescription = NULL;
	if ( pDescription )
	{
		m_pDescription = new vgui::Label( this, "MenuItemDesc", pDescription );
	}
}

CMenuItem::~CMenuItem()
{
	delete m_pTitle;
	delete m_pDescription;
}

//-----------------------------------------------------------------------
// Update colors according to enabled/disabled state
//-----------------------------------------------------------------------
void CMenuItem::PerformLayout()
{
	BaseClass::PerformLayout();
}

//-----------------------------------------------------------------------
// Setup margins and calculate the total menu item size
//-----------------------------------------------------------------------
void CMenuItem::ApplySettings( KeyValues *pSettings )
{
	BaseClass::ApplySettings( pSettings );

	m_nBottomMargin = pSettings->GetInt( "bottommargin", 0 );
	m_nRightMargin	= pSettings->GetInt( "rightmargin", 0 );

	int x, y;
	m_pTitle->GetPos( x, y );
	m_pTitle->SizeToContents();

	int bgTall = y + m_pTitle->GetTall() + m_nBottomMargin;
	int textWide = m_pTitle->GetWide();

	if ( m_pDescription )
	{
		m_pDescription->SizeToContents();
		m_pDescription->GetPos( x, y );
		bgTall = y + m_pDescription->GetTall() + m_nBottomMargin;
		textWide = max( textWide, m_pDescription->GetWide() );
	}

	int bgWide = x + textWide + m_nRightMargin;

	SetSize( bgWide, bgTall );
}

//-----------------------------------------------------------------------
// Setup colors and fonts
//-----------------------------------------------------------------------
void CMenuItem::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	SetPaintBackgroundType( 2 );

	m_BgColor			= pScheme->GetColor( "MatchmakingMenuItemBackground", Color( 46, 43, 42, 255 ) );
	m_BgColorActive		= pScheme->GetColor( "MatchmakingMenuItemBackgroundActive", Color( 150, 71, 0, 255 ) );

	m_pTitle->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemTitleColor", Color( 0, 0, 0, 255 ) ) );

	if ( m_pDescription )
	{
		m_pDescription->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 0, 0, 0, 255 ) ) );
	}

		KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "MenuItem.res" );
		ApplySettings( pKeys );
}

//-----------------------------------------------------------------------
// Set an item as having input focus
//-----------------------------------------------------------------------
void CMenuItem::SetFocus( const bool bActive )
{
	if ( bActive )
	{
		SetBgColor( m_BgColorActive );
	}
	else
	{
		SetBgColor( m_BgColor );
	}
}

//-----------------------------------------------------------------------
// Set an item as having input focus
//-----------------------------------------------------------------------
void CMenuItem::SetEnabled( bool bEnabled )
{
	if ( bEnabled )
	{
		SetAlpha( 255 );
	}
	else
	{
		SetAlpha( m_nDisabledAlpha );
	}
	m_bEnabled = bEnabled;
}

//-----------------------------------------------------------------------
// Set a column as having focus
//-----------------------------------------------------------------------
void CMenuItem::SetActiveColumn( int col )
{
	// do nothing
}

//-----------------------------------------------------------------------
// Set an item as having input focus
//-----------------------------------------------------------------------
bool CMenuItem::IsEnabled()
{
	return m_bEnabled;
}

//-----------------------------------------------------------------------
// Perform any special actions when an item is "clicked"
//-----------------------------------------------------------------------
void CMenuItem::OnClick()
{
	// do nothing - derived classes implement this
}


//-----------------------------------------------------------------------
// CCommandItem
//
// Menu item that issues a command when clicked.
//-----------------------------------------------------------------------
CCommandItem::CCommandItem( CDialogMenu *pParent, const char *pTitleLabel, const char *pDescLabel, const char *pCommand ) 
	: BaseClass( pParent, pTitleLabel, pDescLabel )
{
	Q_strncpy( m_szCommand, pCommand, MAX_COMMAND_LEN );
}

CCommandItem::~CCommandItem()
{
	// do nothing
}

void CCommandItem::OnClick()
{
	GetParent()->OnCommand( m_szCommand );

	vgui::surface()->PlaySound( "UI/buttonclick.wav" );
}

void CCommandItem::SetFocus(const bool bActive )
{
	BaseClass::SetFocus( bActive );

	if ( bActive == true && m_bHasFocus == false )
	{
		vgui::surface()->PlaySound( "UI/buttonclickrelease.wav" );
	}

	m_bHasFocus = bActive;
}


//-----------------------------------------------------------------------
// CPlayerItem
//
// Menu item to display a player in the lobby.
//-----------------------------------------------------------------------
CPlayerItem::CPlayerItem( CDialogMenu *pParent, const char *pTitleLabel, int64 nId, byte bVoice, bool bReady ) 
: BaseClass( pParent, pTitleLabel, NULL, "ShowGamerCard" )
{
	m_pVoiceIcon = new vgui::Label( this, "voiceicon", "" );
	m_pReadyIcon = new vgui::Label( this, "readyicon", "" );

	m_nId			= nId;
	m_bVoice		= bVoice;
	m_bReady		= bReady;
}

CPlayerItem::~CPlayerItem()
{
	delete m_pVoiceIcon;
	delete m_pReadyIcon;
}

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

	const char *pVoice = "";

	if ( m_bVoice == 2 )
	{
		pVoice = "#TF_Icon_Voice";
	}
	else if ( m_bVoice == 1 )
	{
		pVoice = "#TF_Icon_Voice_Idle";
	}

	m_pVoiceIcon->SetText( pVoice );
	m_pReadyIcon->SetText( m_bReady ? "#TF_Icon_Ready" : "#TF_Icon_NotReady" );

	int x, y;
	m_pReadyIcon->GetPos( x, y );
	m_pReadyIcon->SetPos( GetWide() - m_pReadyIcon->GetWide() - m_nRightMargin, y );
}

void CPlayerItem::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "PlayerItem.res" );
	ApplySettings( pKeys );
}

void CPlayerItem::OnClick()
{
	BaseClass::OnClick();
}

//-----------------------------------------------------------------------
// CBrowserItem
//
// Menu item used to display session search results.
//-----------------------------------------------------------------------
CBrowserItem::CBrowserItem( CDialogMenu *pParent, const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing ) 
	: BaseClass( pParent, pHost, NULL, "SelectSession" )
{
	m_pPlayers = new vgui::Label( this, "players", pPlayers );
	m_pScenario = new vgui::Label( this, "scenario", pScenario );
	m_pPing = new vgui::Label( this, "ping", pPing );
}

CBrowserItem::~CBrowserItem()
{
	delete m_pPlayers;
	delete m_pScenario;
	delete m_pPing;
}

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

	int x, y, wide, tall;
	m_pPing->GetBounds( x, y, wide, tall );

	m_pScenario->SizeToContents();
	int sx, sy;
	m_pScenario->GetPos( sx, sy );
	m_pScenario->SetPos( x - m_pScenario->GetWide() - m_nRightMargin, sy );

	SetSize( x + wide, GetTall() );
}

void CBrowserItem::ApplySettings( KeyValues *pSettings )
{
	BaseClass::ApplySettings( pSettings );
}

void CBrowserItem::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	Color fgcolor = pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) );
	m_pPlayers->SetFgColor( fgcolor );
	m_pScenario->SetFgColor( fgcolor );

	m_pPing->SetContentAlignment( vgui::Label::a_center );

	KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "BrowserItem.res" );
	ApplySettings( pKeys );

	SetFocus( false );
}

//-----------------------------------------------------------------------
// COptionsItem
//
// Menu item used to present a list of options for the player to select
// from, such as "choose a map" or "number of rounds".
//-----------------------------------------------------------------------
COptionsItem::COptionsItem( CDialogMenu *pParent, const char *pLabel ) 
	: BaseClass( pParent, pLabel, NULL )
{
	m_nActiveOption = m_Options.InvalidIndex();
	m_nOptionsXPos = 0;
	m_nMaxOptionWidth = 0;

	m_szOptionsFont[0] = '\0';
	m_hOptionsFont = vgui::INVALID_FONT;

	m_pLeftArrow = new vgui::Label( this, "LeftArrow", "" );
	m_pRightArrow = new vgui::Label( this, "RightArrow", "" );
}

COptionsItem::~COptionsItem()
{
	m_OptionLabels.PurgeAndDeleteElements();

	delete m_pLeftArrow;
	delete m_pRightArrow;
}

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

	int optionWide = max( m_nOptionsMinWide, GetWide() - m_nOptionsXPos - m_pRightArrow->GetWide() - m_nOptionsLeftMargin );
	int optionTall = GetTall();

	for ( int i = 0; i < m_OptionLabels.Count(); ++i )
	{
		vgui::Label *pOption = m_OptionLabels[i];

		pOption->SetBounds( m_nOptionsXPos, 0, optionWide, optionTall );
	}

	int lx, ly;
	m_pLeftArrow->GetPos( lx, ly );
	m_pLeftArrow->SetPos( m_nOptionsXPos - m_nArrowGap - m_pLeftArrow->GetWide(), ly );

	int rx, ry;
	m_pRightArrow->GetPos( rx, ry );
	m_pRightArrow->SetPos( m_nOptionsXPos + optionWide + m_nArrowGap, ry );

	m_pLeftArrow->SetAlpha( 255 );
	m_pRightArrow->SetAlpha( 255 );

	if ( m_nActiveOption == 0 )
	{
		m_pLeftArrow->SetAlpha( 32 );
	}
	else if ( m_nActiveOption == m_OptionLabels.Count() - 1 )
	{
		m_pRightArrow->SetAlpha( 32 );
	}
}

void COptionsItem::ApplySettings( KeyValues *pSettings )
{
	BaseClass::ApplySettings( pSettings );

	m_nOptionsXPos			= pSettings->GetInt( "optionsxpos", 0 );
	m_nOptionsMinWide		= pSettings->GetInt( "optionsminwide", 0 );
	m_nOptionsLeftMargin	= pSettings->GetInt( "optionsleftmargin", 0 );
	m_nArrowGap				= pSettings->GetInt( "arrowgap", 0 );

	Q_strncpy( m_szOptionsFont, pSettings->GetString( "optionsfont", "Default" ), sizeof( m_szOptionsFont ) );
}

void COptionsItem::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );
	SetPaintBackgroundEnabled( false );

	m_pTitle->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemTitleColor", Color( 200, 184, 151, 255 ) ) );

	KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "OptionsItem.res" );
	ApplySettings( pKeys );

	m_hOptionsFont = pScheme->GetFont( m_szOptionsFont );
}

void COptionsItem::SetFocus( const bool bActive )
{
	if ( bActive )
	{
		for ( int i = 0; i < m_OptionLabels.Count(); ++i )
		{
			m_OptionLabels[i]->SetBgColor( m_BgColorActive );
		}
	}
	else
	{
		for ( int i = 0; i < m_OptionLabels.Count(); ++i )
		{
			m_OptionLabels[i]->SetBgColor( m_BgColor );
		}
	}
}

void COptionsItem::AddOption( const char *pLabelText, const sessionProperty_t& option )
{
	// Add a new option to this item's list of options
	m_Options.AddToTail( option );

	int idx = m_OptionLabels.AddToTail( new vgui::Label( this, "Option Value", pLabelText ) );
	vgui::Label *pOption = m_OptionLabels[idx];

	// Check for a format string
	if ( Q_stristr( pLabelText, "Fmt" ) )
	{
		wchar_t wszString[64];
		wchar_t wzNumber[8];
		wchar_t *wzFmt = g_pVGuiLocalize->Find( pLabelText );
		g_pVGuiLocalize->ConvertANSIToUnicode( option.szValue, wzNumber, sizeof( wzNumber ) );
		g_pVGuiLocalize->ConstructString( wszString, sizeof( wszString ), wzFmt, 1, wzNumber );
		pOption->SetText( wszString );
	}

	SETUP_PANEL( pOption );
	pOption->SetPaintBackgroundType( 2 );

	pOption->SetFont( m_hOptionsFont );
	pOption->SetBgColor( Color( 46, 43, 42, 255 ) );
	pOption->SetFgColor( m_pTitle ? m_pTitle->GetFgColor() : Color( 200, 184, 151, 255 ) );
	pOption->SetTextInset( m_nOptionsLeftMargin, 0 );
	pOption->SetContentAlignment( vgui::Label::a_southwest );
	pOption->SizeToContents();

	int wide = max( m_nOptionsMinWide, pOption->GetWide() );
	pOption->SetBounds( m_nOptionsXPos, 0, wide, GetTall() );
	m_nMaxOptionWidth = max( wide, m_nMaxOptionWidth );

	SetWide( m_nOptionsXPos + m_nMaxOptionWidth + m_nOptionsLeftMargin * 2 + m_nArrowGap * 2 + m_pRightArrow->GetWide() );
}

//-----------------------------------------------------------------------
// Return the session property associated with the current active option
//-----------------------------------------------------------------------
const sessionProperty_t &COptionsItem::GetActiveOption()
{
	return m_Options[m_nActiveOption];
}

//-----------------------------------------------------------------------
// Return the index of the current active option
//-----------------------------------------------------------------------
int COptionsItem::GetActiveOptionIndex()
{
	return m_nActiveOption;
}

//-----------------------------------------------------------------------
// Sets which option currently has focus
//-----------------------------------------------------------------------
void COptionsItem::SetOptionFocus( unsigned int idx )
{
	unsigned int itemCt = (unsigned int)m_OptionLabels.Count();
	if ( idx > itemCt )
		return;

	m_nActiveOption = idx;

	for ( unsigned int i = 0; i < itemCt; ++i )
	{
		vgui::Label *pLabel = m_OptionLabels[i];

		const bool bVisible = ( i == idx );
		pLabel->SetVisible( bVisible );
	}

	InvalidateLayout();
}

//-----------------------------------------------------------------------
// Move focus to the next option - does not wrap
//-----------------------------------------------------------------------
void COptionsItem::SetOptionFocusNext()
{
	if ( m_nActiveOption + 1 < m_OptionLabels.Count() )
	{
		SetOptionFocus( m_nActiveOption + 1 );
	}
	else
	{
		vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
	}
}

//-----------------------------------------------------------------------
// Move focus to the previous option - does not wrap
//-----------------------------------------------------------------------
void COptionsItem::SetOptionFocusPrev()
{
	if ( m_nActiveOption > 0 )
	{
		SetOptionFocus( m_nActiveOption - 1 );
	}
	else
	{
		vgui::surface()->PlaySound( "player/suit_denydevice.wav" );
	}
}


//-----------------------------------------------------------------------
// CAchievementItem
//
// Menu item used to present an achievement - including image, title,
// description, points and unlock date. Clicking the item opens another
// dialog with additional information about the achievement.
//-----------------------------------------------------------------------
CAchievementItem::CAchievementItem( CDialogMenu *pParent, const wchar_t *pName, const wchar_t *pDesc, uint points, bool bUnlocked, IAchievement* pSourceAchievement ) 
	: BaseClass( pParent, "", "" )
{
	// Title and description were returned as results of a system query,
	// and are therefore already localized.
	m_pTitle->SetText( pName );

	if ( IsX360() )
	{
		wchar_t buf[120];

		// Get the screen size
		int wide, tall;
		vgui::surface()->GetScreenSize(wide, tall);

		unsigned int iWrapLen;

		if ( tall <= 480 )
		{
			iWrapLen = 50;
		}
		else
		{
			iWrapLen = 65;
		}

		// let's do some wrapping on this label
		wcsncpy( buf, pDesc, sizeof(buf) / sizeof( wchar_t ) );

		if ( wcslen(buf) > iWrapLen )
		{
			int iPos = iWrapLen;

			while ( iPos > 0 && buf[iPos] != L' ' )
			{
				iPos--;
			}

			if ( iPos > 0 && buf[iPos] == L' ' )
			{
				buf[iPos] = L'\n';
			}				
		}

		m_pDescription->SetText( buf );
	}
	else
	{
		m_pDescription->SetText( pDesc );
	}

	m_pSourceAchievement = pSourceAchievement;

	m_pPercentageBarBackground = SETUP_PANEL( new vgui::ImagePanel( this, "PercentageBarBackground" ) );
	m_pPercentageBar = SETUP_PANEL( new vgui::ImagePanel( this, "PercentageBar" ) );
	m_pPercentageText = SETUP_PANEL( new vgui::Label( this, "PercentageText", "" ) );

	// Set the status icons
	m_pLockedIcon = SETUP_PANEL( new vgui::ImagePanel( this, "lockedicon" ) );
	m_pUnlockedIcon = SETUP_PANEL( new vgui::ImagePanel( this, "unlockedicon" ) );

	// Gamerscore number
	if ( IsX360() )
	{
		wchar_t *wzFormat = g_pVGuiLocalize->Find( "#GameUI_Achievement_Points" );	// "%s1G"
		wchar_t wzPoints[10];
		V_snwprintf( wzPoints, ARRAYSIZE( wzPoints ), L"%d", points );
		wchar_t wzPointsLayout[10];
		g_pVGuiLocalize->ConstructString( wzPointsLayout, sizeof( wzPointsLayout ), wzFormat, 1, wzPoints );
		m_pPoints = new vgui::Label( this, "Points", wzPointsLayout );
	}

	// Achievement image
	m_pImage = new vgui::ImagePanel( this, "icon" );
}

CAchievementItem::~CAchievementItem()
{
	delete m_pImage;
	delete m_pPoints;
	delete m_pLockedIcon;
	delete m_pUnlockedIcon;
	delete m_pPercentageBarBackground;
	delete m_pPercentageBar;
	delete m_pPercentageText;
}

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

	int x, y;

 	m_pPoints->SizeToContents();
	m_pPoints->GetPos( x, y );
 	x = GetWide() - m_pPoints->GetWide() - m_nRightMargin;
 	m_pPoints->SetPos( x, y );

}

void CAchievementItem::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	KeyValues*pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "AchievementItem.res" );	
	ApplySettings( pKeys );

	m_pImage->SetBgColor( Color( 32, 32, 32, 255 ) );
	m_pImage->SetFgColor( Color( 32, 32, 32, 255 ) );
	m_pImage->SetPaintBackgroundEnabled( true );

	m_pPoints->SetFgColor( pScheme->GetColor( "MatchmakingMenuItemDescriptionColor", Color( 64, 64, 64, 255 ) ) );

	// Set icon image
	LoadAchievementIcon( m_pImage, m_pSourceAchievement );

	// Percentage completion bar (for progressive achievements)
	UpdateProgressBar( this, m_pSourceAchievement, m_clrProgressBar );

	if ( m_pSourceAchievement && m_pSourceAchievement->IsAchieved() )
	{
		m_pLockedIcon->SetVisible( false );
		m_pUnlockedIcon->SetVisible ( true );
		m_pImage->SetVisible( true );
	}
	else
	{
		m_pLockedIcon->SetVisible( true );
		m_pUnlockedIcon->SetVisible( false );
		m_pImage->SetVisible( false );
	}
}

//-----------------------------------------------------------------------
// CSectionedItem
//
// Menu item used to display some number of data entries, which are arranged
// into columns.  Supports scrolling through columns horizontally with the 
// ability to "lock" columns so they don't scroll
//-----------------------------------------------------------------------
CSectionedItem::CSectionedItem( CDialogMenu *pParent, const char **ppEntries, int ct  ) 
	: BaseClass( pParent, "", NULL, "SelectSession" )
{
	m_bHeader = false;
	for ( int i = 0; i < ct; ++i )
	{
		AddSection( ppEntries[i], m_pParent->GetColumnAlignment( i ) );
	}
}

CSectionedItem::~CSectionedItem()
{
	ClearSections();
}

void CSectionedItem::ClearSections()
{
	for ( int i = 0; i < m_Sections.Count(); ++i )
	{
		section_s &sec = m_Sections[i];
		delete sec.pLabel;
	}
}

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

	int tall = GetTall();
	for ( int i = 0; i < m_Sections.Count(); ++i )
	{
		vgui::Label *pLabel = m_Sections[i].pLabel;
		if ( !m_bHeader )
		{
			pLabel->SetFont( m_pParent->GetColumnFont(i) );
			pLabel->SetFgColor( m_pParent->GetColumnColor(i) );
		}
		pLabel->SetBounds( m_pParent->GetColumnXPos(i), 0, m_pParent->GetColumnWide(i), tall );
		pLabel->SetTextInset( 10, m_bHeader ? 5 : m_pParent->GetColumnYPos(i) ); // only use ypos for the y-inset if we're not a header
	}
}

void CSectionedItem::ApplySettings( KeyValues *pResourceData )
{
	BaseClass::ApplySettings( pResourceData );
}

void CSectionedItem::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	KeyValues *pKeys = BasePanel()->GetConsoleControlSettings()->FindKey( "SectionedItem.res" );
	ApplySettings( pKeys );

	int iLast = m_Sections.Count() -1;
	SetWide( m_pParent->GetColumnXPos(iLast) + m_pParent->GetColumnWide(iLast) );
}

void CSectionedItem::AddSection( const char *pText, int align )
{
	section_s sec;
	sec.pLabel = new vgui::Label( this, "Section", pText );
	SETUP_PANEL( sec.pLabel );
	sec.pLabel->SetContentAlignment( (vgui::Label::Alignment)align );
	sec.pLabel->SetTextInset( 10, 0 );
	sec.pLabel->SetBgColor( Color( 209, 112, 52, 128 ) );
	m_Sections.AddToTail( sec );
}

void CSectionedItem::SetActiveColumn( int col )
{
	for ( int i = 0; i < m_Sections.Count(); ++i )
	{
		m_Sections[i].pLabel->SetPaintBackgroundEnabled( i == col );
	}
}

//--------------------------------------------------------------------------------------
// Generic menu for Xbox 360 matchmaking dialogs. Contains a list of CMenuItems arranged
// vertically. The user can navigate the list using the controller and click on any
// item. A clicked item may send a command to the dialog and the dialog responds accordingly.
//--------------------------------------------------------------------------------------
CDialogMenu::CDialogMenu() : BaseClass( NULL, "DialogMenu" )
{
	// Quiet "parent not sized yet" spew
	SetSize( 100, 100 );

	m_pParent			= NULL;
	m_pHeader			= NULL;
	m_bUseFilter		= false;
	m_bHasHeader		= false;
	m_nItemSpacing		= 0;
	m_nMinWide			= 0;
	m_nActive			= -1;
	m_nActiveColumn		= -1;
	m_nBaseRowIdx		= 0;
	m_nBaseColumnIdx	= 0;
	m_iUnlocked			= 0;
	m_nMaxVisibleItems	= 1000;	// arbitrarily large	
	m_nMaxVisibleColumns = 1000;// arbitrarily large
}

CDialogMenu::~CDialogMenu()
{
	m_MenuItems.PurgeAndDeleteElements();
	delete m_pHeader;
}

void CDialogMenu::SetParent( CBaseDialog *pParent )
{
	BaseClass::SetParent( pParent );
	m_pParent = pParent;
}

//--------------------------------------------------------------------------------------
// Set a filter to use when reading in menu item keyvalues
//--------------------------------------------------------------------------------------
void CDialogMenu::SetFilter( const char *pFilter )
{
	if ( pFilter )
	{
		Q_strncpy( m_szFilter, pFilter, sizeof( m_szFilter ) );
		m_bUseFilter = true;
	}
	else
	{
		m_bUseFilter = false;
	}
}

//--------------------------------------------------------------------------------------
// Add a new menu item to the item array
//--------------------------------------------------------------------------------------
CMenuItem *CDialogMenu::AddItemInternal( CMenuItem *pItem )
{
	int idx = m_MenuItems.AddToTail( pItem );

 	SETUP_PANEL( pItem );

	return m_MenuItems[idx];
}

//--------------------------------------------------------------------------------------
// Add a new menu item of some type that derives from CMenuItem
//--------------------------------------------------------------------------------------
CCommandItem *CDialogMenu::AddCommandItem( const char *pTitleLabel, const char *pDescLabel, const char *pCommand )
{
	return (CCommandItem*)AddItemInternal( new CCommandItem( this, pTitleLabel, pDescLabel, pCommand ) );
}

CBrowserItem *CDialogMenu::AddBrowserItem( const char *pHost, const char *pPlayers, const char *pScenario, const char *pPing )
{
	// Results are added to the menu at runtime, so the layout needs to be updated after each addition.
	CBrowserItem *pItem = (CBrowserItem*)AddItemInternal( new CBrowserItem( this, pHost, pPlayers, pScenario, pPing ) );
	PerformLayout();
	return pItem;
}

COptionsItem *CDialogMenu::AddOptionsItem( const char *pLabel )
{
	return (COptionsItem*)AddItemInternal( new COptionsItem( this, pLabel ) );
}

CAchievementItem *CDialogMenu::AddAchievementItem( const wchar_t *pName, const wchar_t *pDesc, uint points, bool bUnlocked, IAchievement* pSourceAchievement  )
{
	return (CAchievementItem*)AddItemInternal( new CAchievementItem( this, pName, pDesc, points, bUnlocked, pSourceAchievement ) );
}

CSectionedItem *CDialogMenu::AddSectionedItem( const char **ppEntries, int ct )
{
	CSectionedItem *pItem = (CSectionedItem*)AddItemInternal( new CSectionedItem( this, ppEntries, ct ) );
	PerformLayout();
	return pItem;
}

CPlayerItem *CDialogMenu::AddPlayerItem( const char *pTitleLabel, int64 nId, byte bVoice, bool bReady )
{
	// Players are added to the lobby at runtime, so the layout needs to be updated after each addition.
	CPlayerItem *pItem = (CPlayerItem*)AddItemInternal( new CPlayerItem( this, pTitleLabel, nId, bVoice, bReady ) );
	PerformLayout();
	return pItem;
}

void CDialogMenu::RemovePlayerItem( int idx )
{
	delete m_MenuItems[idx];
	m_MenuItems.Remove( idx );
	PerformLayout();
}

void CDialogMenu::ClearItems()
{
	m_MenuItems.PurgeAndDeleteElements();
	InvalidateLayout();
}

//--------------------------------------------------------------------------------------
// Set the size an position of all the menu items
//--------------------------------------------------------------------------------------
void CDialogMenu::PerformLayout()
{
	BaseClass::PerformLayout();

	// Position the menu items and set their width
	int yPos = 0;
	int wide = GetWide();

	if ( m_bHasHeader )
	{
		yPos = 40;
		m_pHeader->SetPos( 0, 0 );
		m_pHeader->SetWide( wide );
		m_pHeader->PerformLayout();
	}

	for ( int i = 0; i < m_MenuItems.Count(); ++i )
	{
		CMenuItem *pItem = m_MenuItems[i];

		pItem->SetPos( 0, yPos );
		pItem->SetWide( wide );
		pItem->SetActiveColumn( m_nActiveColumn );
		pItem->PerformLayout();

		if ( i < m_nBaseRowIdx || i > m_nBaseRowIdx + m_nMaxVisibleItems - 1 )
		{
			pItem->SetVisible( false );
		}
		else
		{
			pItem->SetVisible( true );
			yPos += pItem->GetTall() + m_nItemSpacing;
		}
	}
	
	// Reset the focus to update background colors of all menu items
	SetFocus( m_nActive );


	SetTall( yPos );
}

//--------------------------------------------------------------------------------------
// Parse the res file for menu items to build out the dialog menu.
//--------------------------------------------------------------------------------------
void CDialogMenu::ApplySettings( KeyValues *pResourceData )
{
	BaseClass::ApplySettings( pResourceData );

	m_nItemSpacing		 = pResourceData->GetInt( "itemspacing", 2 );
	m_nMinWide			 = pResourceData->GetInt( "minwide", 0 );
	m_nActiveColumn		 = pResourceData->GetInt( "activecolumn", -1 );
	m_nMaxVisibleItems	 = pResourceData->GetInt( "maxvisibleitems", 1000 ); // arbitrarily large
	m_nMaxVisibleColumns = pResourceData->GetInt( "maxvisiblecolumns", 1000 ); // arbitrarily large

	KeyValues *pColumnData = pResourceData->FindKey( "Columns" );
	if ( pColumnData )
	{
		int xPos = 0;
		int idx = 0;
		const char *ppHeader[MAX_COLUMNS];
		for ( KeyValues *pColumn = pColumnData->GetFirstSubKey(); pColumn != NULL; pColumn = pColumn->GetNextKey() )
		{
			if ( !Q_stricmp( pColumn->GetName(), "Column" ) )
			{
				columninfo_s col;
				col.bSortDown	= true;
				col.xpos		= pColumn->GetInt( "xpos", xPos );
				col.ypos		= pColumn->GetInt( "ypos", 0 );
				col.wide		= pColumn->GetInt( "wide", 0 );
				col.align		= pColumn->GetInt( "align", 3 ); // west by default
				col.bLocked		= pColumn->GetInt( "locked", 0 );
				col.hFont		= m_pScheme->GetFont( pColumn->GetString( "font", "default" ) );
				col.color		= m_pScheme->GetColor( pColumn->GetString( "fgcolor" ), Color( 0, 0, 0, 255 ) );
				
				ppHeader[idx++] = pColumn->GetString( "header", "" );

				xPos = col.xpos + col.wide;
				m_Columns.AddToTail( col );

				if ( col.bLocked )
				{
					m_nBaseColumnIdx = idx;
					m_iUnlocked = idx;
				}
			}
		}
		m_bHasHeader = true;
		m_pHeader = new CSectionedItem( this, ppHeader, idx );
		m_pHeader->m_bHeader = true;
		SETUP_PANEL( m_pHeader );

		m_pHeader->SetPaintBackgroundEnabled( false );
		vgui::HFont headerFont = m_pScheme->GetFont( pColumnData->GetString( "headerfont", "default" ) );
		Color headerColor = m_pScheme->GetColor( pColumnData->GetString( "headerfgcolor" ), Color( 0, 0, 0, 255 ) );
		for ( int i = 0; i < idx; ++i )
		{
			vgui::Label *pLabel = m_pHeader->m_Sections[i].pLabel;
			pLabel->SetFont( headerFont );
			pLabel->SetFgColor( headerColor );
			pLabel->SetPaintBackgroundEnabled( false );
		}
	}

	for ( KeyValues *pMenuData = pResourceData->GetFirstSubKey(); pMenuData != NULL; pMenuData = pMenuData->GetNextKey() )
	{
		// See if we should skip over this block
		if ( m_bUseFilter )
		{
			if ( pMenuData->GetInt( m_szFilter, 0 ) == 0 )
				continue;
		}

		// Give our parent a chance to change the properties of this item
		m_pParent->OverrideMenuItem( pMenuData );

		if ( !Q_stricmp( pMenuData->GetName(), "CommandItem" ) )
		{
			// New Command Item
			const char *label		= pMenuData->GetString( "label", "<unknown>" );
			const char *description = pMenuData->GetString( "description", NULL );
			const char *command		= pMenuData->GetString( "command", "<unknown>" );

			AddCommandItem( label, description, command );
		}
		else if ( !Q_stricmp( pMenuData->GetName(), "OptionsItem" ) )
		{
			// New Options Item
			COptionsItem *pItem = AddOptionsItem( pMenuData->GetString( "label", "<unknown>" ) );
 
			// ID and ValueType and the same for all option values
			const char *pID			= pMenuData->GetString( "id", "NULL" );
			const char *pValueType	= pMenuData->GetString( "valuetype", NULL );

			// Add all the options
			for ( KeyValues *pValue = pMenuData->GetFirstSubKey(); pValue != NULL; pValue = pValue->GetNextKey() )
			{
				if ( !Q_stricmp( pValue->GetName(), "Option" ) )
				{
					sessionProperty_t prop;
					prop.nType = SESSION_CONTEXT;
					Q_strncpy( prop.szID, pID, sizeof( prop.szID ) );
					Q_strncpy( prop.szValue, pValue->GetString( "value", "NULL" ), sizeof( prop.szValue ) );

					if ( pValueType )
					{
						// Only session properties have a type
						prop.nType = SESSION_PROPERTY;
						Q_strncpy( prop.szValueType, pValueType, sizeof( prop.szValueType ) );
					}

					const char *pLabel = pValue->GetString( "label", "<unknown>" );
					pItem->AddOption( pLabel, prop );
				}
			}

			// Add range items after the specified items
			if ( pMenuData->GetInt( "userange" ) )
			{
				// Options are an implicit range of integers
				int nStart		= pMenuData->GetInt( "rangelow" );
				int nEnd		= pMenuData->GetInt( "rangehigh" );
				int nInterval	= pMenuData->GetInt( "interval", 1 );

				// Prevent total destruction from a bad resource file
				if ( nEnd < nStart )
				{
					nEnd = nStart;
				}

				for ( int i = nStart; i <= nEnd; i += nInterval )
				{
					sessionProperty_t prop;
					prop.nType = SESSION_PROPERTY;
					Q_strncpy( prop.szID, pID, sizeof( prop.szID ) );
					Q_strncpy( prop.szValueType, pValueType, sizeof( prop.szValueType ) );
					Q_snprintf( prop.szValue, sizeof(prop.szValue), "%d", i ); 

					pItem->AddOption( prop.szValue, prop );
				}
			}
  
			// Set the default active option
  			int active = pMenuData->GetInt( "activeoption", 0 );
  			pItem->SetOptionFocus( active );

			// Notify our parent that each option has been set to its current setting
			KeyValues *kv = new KeyValues( "MenuItemChanged", "item", GetItemCount() - 1 );
			PostActionSignal( kv );
		}
	}

	// Calculate the final menu size according to the widest menu item
	int wide = m_nMinWide;
	for ( int i = 0; i < m_MenuItems.Count(); ++i )
	{
		wide = max( wide, m_MenuItems[i]->GetWide() );
	}
	SetWide( wide );
}

//--------------------------------------------------------------------------------------
// Cache off the scheme
//--------------------------------------------------------------------------------------
void CDialogMenu::ApplySchemeSettings( vgui::IScheme *pScheme )
{
	BaseClass::ApplySchemeSettings( pScheme );

	m_pScheme = pScheme;
}

//--------------------------------------------------------------------------------------
// Give focus (highlights) a particular menu item by index
//--------------------------------------------------------------------------------------
void CDialogMenu::SetFocus( int idx )
{
	int itemCt = (unsigned int)m_MenuItems.Count();
	if ( idx >= itemCt )
		return;

	for ( int i = 0; i < itemCt; ++i )
	{
		m_MenuItems[i]->SetFocus( i == idx );
	}
	m_nActive = idx;

	if ( m_nActive >= 0 && m_nActive < m_nBaseRowIdx )
	{
		m_nBaseRowIdx = m_nActive;
	}
	else if ( m_nActive > m_nBaseRowIdx + m_nMaxVisibleItems - 1 )
	{
		m_nBaseRowIdx = m_nActive - ( m_nMaxVisibleItems - 1 );
	}
}

//--------------------------------------------------------------------------------------
// Sort the menu items according to the selected column
//--------------------------------------------------------------------------------------
void CDialogMenu::SortMenuItems()
{
	if ( !m_bHasHeader )
		return;

	// Simple bubble sort
	char szBufferOne[32];
	char szBufferTwo[32];
	bool bSortDown = GetColumnSortType( m_nActiveColumn );
	for ( int i = 1; i <= m_MenuItems.Count(); ++i )
	{
		for ( int j = 0; j < m_MenuItems.Count() - i; ++j )
		{
			((CSectionedItem*)m_MenuItems[j])->m_Sections[m_nActiveColumn].pLabel->GetText( szBufferOne, sizeof( szBufferOne ) );
			((CSectionedItem*)m_MenuItems[j+1])->m_Sections[m_nActiveColumn].pLabel->GetText( szBufferTwo, sizeof( szBufferTwo ) );

			int diff = Q_stricmp( szBufferOne, szBufferTwo );
			bool bSwap = bSortDown ? diff > 0 : diff < 0;
			if ( bSwap )
			{
				CMenuItem *pTemp = m_MenuItems[j+1];
				m_MenuItems[j+1] = m_MenuItems[j];
				m_MenuItems[j] = pTemp;

				m_pParent->SwapMenuItems( j, j+1 );
			}
		}
	}
	InvalidateLayout();
}

//--------------------------------------------------------------------------------------
// Move item focus to the next item in the menu - supports wrapping
//--------------------------------------------------------------------------------------
void CDialogMenu::SetFocusNext()
{
	if ( m_MenuItems.Count() )
	{
		int iNewIndex = ( m_nActive + 1 ) % m_MenuItems.Count();

		int i = 0;
		bool bSet = false;
		while ( i < m_MenuItems.Count() )
		{
			if ( m_MenuItems[iNewIndex]->IsEnabled() )
			{
				SetFocus( iNewIndex );
				bSet = true;
				break;
			}

			iNewIndex = ( iNewIndex + 1 ) % m_MenuItems.Count();
			i++;
		}

		InvalidateLayout();
	}
}

//--------------------------------------------------------------------------------------
// Move item focus to the previous item in the menu - supports wrapping
//--------------------------------------------------------------------------------------
void CDialogMenu::SetFocusPrev()
{
	if ( m_MenuItems.Count() )
	{
		int iNewIndex = m_nActive - 1;
		if ( iNewIndex < 0 )
			iNewIndex = m_MenuItems.Count() - 1;

		int i = 0;
		bool bSet = false;
		while ( i < m_MenuItems.Count() )
		{
			if ( m_MenuItems[iNewIndex]->IsEnabled() )
			{
				SetFocus( iNewIndex );
				bSet = true;
				break;
			}

			if ( --iNewIndex < 0 )
				iNewIndex = m_MenuItems.Count() - 1;

			i++;
		}

		InvalidateLayout();
	}
}

//--------------------------------------------------------------------------------------
// For OptionsItems: Move focus to the next option in the menu item - does not wrap
//--------------------------------------------------------------------------------------
void CDialogMenu::SetOptionFocusNext()
{
	COptionsItem *pItem = dynamic_cast< COptionsItem* >( GetItem( m_nActive ) );
	if ( pItem )
	{
		pItem->SetOptionFocusNext();

		KeyValues *kv = new KeyValues( "MenuItemChanged", "item", m_nActive );
		PostActionSignal( kv );
	}
}

//--------------------------------------------------------------------------------------
// For OptionsItems: Move focus to the previous option in the menu item - does not wrap
//--------------------------------------------------------------------------------------
void CDialogMenu::SetOptionFocusPrev()
{
	COptionsItem *pItem = dynamic_cast< COptionsItem* >( GetItem( m_nActive ) );
	if ( pItem )
	{
		pItem->SetOptionFocusPrev();

		KeyValues *kv = new KeyValues( "MenuItemChanged", "item", m_nActive );
		PostActionSignal( kv );
	}
}

//--------------------------------------------------------------------------------------
// For OptionsItems: Update the base index for the columns
//--------------------------------------------------------------------------------------
void CDialogMenu::UpdateBaseColumnIndex()
{
	if ( m_iUnlocked + m_nActiveColumn - m_nBaseColumnIdx >= m_nMaxVisibleColumns )
	{
		m_nBaseColumnIdx = m_iUnlocked + m_nActiveColumn - m_nMaxVisibleColumns + 1;
	}
	else if ( m_nActiveColumn - m_nBaseColumnIdx < 0 )
	{
		m_nBaseColumnIdx = m_nActiveColumn;
	}
}

//--------------------------------------------------------------------------------------
// For menus with sectioned columns - move focus to the next column
//--------------------------------------------------------------------------------------
void CDialogMenu::SetColumnFocusNext()
{
	if ( m_nActiveColumn == -1 )
		return;

	if ( m_Columns.Count() )
	{
		int iNewColumn = m_nActiveColumn + 1;
		if ( iNewColumn >= m_Columns.Count() )
			return;

		m_nActiveColumn = iNewColumn;	
		UpdateBaseColumnIndex();
		InvalidateLayout();
	}
}

//--------------------------------------------------------------------------------------
// For menus with sectioned columns - move focus to the next column
//--------------------------------------------------------------------------------------
void CDialogMenu::SetColumnFocusPrev()
{
	if ( m_nActiveColumn == -1 )
		return;

	if ( m_Columns.Count() )
	{
		int iNewColumn = m_nActiveColumn - 1;
		if ( iNewColumn < 0 || m_Columns[iNewColumn].bLocked )
			return;

		m_nActiveColumn = iNewColumn;
		UpdateBaseColumnIndex();
		InvalidateLayout();
	}
}

//--------------------------------------------------------------------------------------
// For OptionsItems: Lets the dialog find out which option is currently selected
//--------------------------------------------------------------------------------------
int	CDialogMenu::GetActiveOptionIndex( int nMenuItemIdx )
{
	int retval = -1;
	COptionsItem *pItem = dynamic_cast< COptionsItem* >( GetItem( nMenuItemIdx ) );
	if ( pItem )
	{
		retval = pItem->GetActiveOptionIndex();
	}
	return retval;
}

//-----------------------------------------------------------------------
// Return the index of the current active menu item
//-----------------------------------------------------------------------
int CDialogMenu::GetActiveItemIndex()
{
	return m_nActive;
}

//-----------------------------------------------------------------------
// Return the index of the current active menu column
//-----------------------------------------------------------------------
int CDialogMenu::GetActiveColumnIndex()
{
	return m_nActiveColumn;
}

//-----------------------------------------------------------------------
// Return the number of menu items
//-----------------------------------------------------------------------
int CDialogMenu::GetItemCount()
{
	return m_MenuItems.Count();
}

//-----------------------------------------------------------------------
// Return the number of visible menu items
//-----------------------------------------------------------------------
int CDialogMenu::GetVisibleItemCount()
{
	return min( GetItemCount(), m_nMaxVisibleItems );
}

//-----------------------------------------------------------------------
// Return the number of visible menu columns
//-----------------------------------------------------------------------
int CDialogMenu::GetVisibleColumnCount()
{
	return m_nMaxVisibleColumns;
}

//-----------------------------------------------------------------------
// Return the index of the first unlocked column
//-----------------------------------------------------------------------
int CDialogMenu::GetFirstUnlockedColumnIndex()
{
	return m_iUnlocked;
}

//-----------------------------------------------------------------------
// Return the first visible index in the menu
//-----------------------------------------------------------------------
int CDialogMenu::GetBaseRowIndex()
{
	return m_nBaseRowIdx;
}

//-----------------------------------------------------------------------
// Set the first visible index in the menu
//-----------------------------------------------------------------------
void CDialogMenu::SetBaseRowIndex( int idx )
{
	m_nBaseRowIdx = idx;
}

//-----------------------------------------------------------------------
// Return the specified menu item
//-----------------------------------------------------------------------
CMenuItem *CDialogMenu::GetItem( int idx )
{
	if ( m_MenuItems.IsValidIndex( idx ) )
	{
		return m_MenuItems[idx];
	}
	return NULL;
}

//-----------------------------------------------------------------------
// Return the specified column xpos
//-----------------------------------------------------------------------
int	CDialogMenu::GetColumnXPos( int idx )
{
	// Compensate for scrolling offsets
	columninfo_s &col = m_Columns[idx];
	
	int xpos;
	if ( col.bLocked )
	{
		xpos = m_Columns[idx].xpos;
	}
	else
	{
		int trueIdx = m_iUnlocked + idx - m_nBaseColumnIdx;
		if ( trueIdx < m_iUnlocked )
		{
			// Put it offscreen
			xpos = -100 - col.wide;
		}
		else
		{
			xpos = m_Columns[trueIdx].xpos;
		}
	}
	return xpos;
}

//-----------------------------------------------------------------------
// Return the specified column ypos
//-----------------------------------------------------------------------
int	CDialogMenu::GetColumnYPos( int idx )
{
	return m_Columns[idx].ypos;
}

//-----------------------------------------------------------------------
// Return the specified column width
//-----------------------------------------------------------------------
int	CDialogMenu::GetColumnWide( int idx )
{
	return m_Columns[idx].wide;
}

//-----------------------------------------------------------------------
// Return the specified column alignment
//-----------------------------------------------------------------------
int CDialogMenu::GetColumnAlignment( int idx )
{
	return m_Columns[idx].align;
}

//-----------------------------------------------------------------------
// Return the specified column font
//-----------------------------------------------------------------------
HFont CDialogMenu::GetColumnFont( int idx )
{
	return m_Columns[idx].hFont;
}

//-----------------------------------------------------------------------
// Return the specified column fgcolor
//-----------------------------------------------------------------------
Color CDialogMenu::GetColumnColor( int idx )
{
	return m_Columns[idx].color;
}

//-----------------------------------------------------------------------
// Return the specified column fgcolor
//-----------------------------------------------------------------------
bool CDialogMenu::GetColumnSortType( int idx )
{
	bool bSortDown = m_Columns[idx].bSortDown;
	m_Columns[idx].bSortDown = !bSortDown;
	return bSortDown;
}

//--------------------------------------------------------------------------------------
// Receive the command from a clicked menu item and forwards it to the parent dialog
//--------------------------------------------------------------------------------------
void CDialogMenu::OnCommand( const char *pCommand )
{
	GetParent()->OnCommand( pCommand );
}

//--------------------------------------------------------------------------------------
// Update the menu state according to controller input.
// Returns whether or not the keycode was handled by the menu.
//--------------------------------------------------------------------------------------
bool CDialogMenu::HandleKeyCode( vgui::KeyCode code )
{
	switch( code )
	{
	case KEY_XBUTTON_DOWN:
	case KEY_XSTICK1_DOWN:
	case STEAMCONTROLLER_DPAD_DOWN:
		SetFocusNext();
		break;

	case KEY_XBUTTON_UP:
	case KEY_XSTICK1_UP:
	case STEAMCONTROLLER_DPAD_UP:
		SetFocusPrev();
		break;

	case KEY_XBUTTON_LEFT:
	case KEY_XSTICK1_LEFT:
	case STEAMCONTROLLER_DPAD_LEFT:
		SetOptionFocusPrev();
		SetColumnFocusPrev();
		break;

	case KEY_XBUTTON_RIGHT:
	case KEY_XSTICK1_RIGHT:
	case STEAMCONTROLLER_DPAD_RIGHT:
		SetOptionFocusNext();
		SetColumnFocusNext();
		break;

	case KEY_XBUTTON_A:
	case STEAMCONTROLLER_A:
		if ( m_MenuItems.Count() && m_nActive >= 0 )
		{
			m_MenuItems[m_nActive]->OnClick();
		}
		break;

	case KEY_XBUTTON_Y:
	case STEAMCONTROLLER_Y:
		SortMenuItems();
		break;

	default:
		// Not handled
		return false;
	}

	return true;
}