diff --git a/src/OptionsList.cpp b/src/OptionsList.cpp index 24c78e8e4e..4c4caf2746 100644 --- a/src/OptionsList.cpp +++ b/src/OptionsList.cpp @@ -1,749 +1,748 @@ -#include "global.h" -#include "OptionsList.h" -#include "GameState.h" -#include "RageLog.h" -#include "Course.h" -#include "SongUtil.h" -#include "StepsUtil.h" -#include "Style.h" -#include "InputEventPlus.h" -#include "CodeDetector.h" -#include "InputMapper.h" -#include "PlayerState.h" - -#define LINE(sLineName) THEME->GetMetric (m_sName,ssprintf("Line%s",sLineName.c_str())) -#define MAX_ITEMS_BEFORE_SPLIT THEME->GetMetricI(m_sName,"MaxItemsBeforeSplit") -#define ITEMS_SPLIT_WIDTH THEME->GetMetricF(m_sName,"ItemsSplitWidth") -#define DIRECT_LINES THEME->GetMetric (m_sName,"DirectLines") -#define TOP_MENUS THEME->GetMetric (m_sName,"TopMenus") - -static const RString RESET_ROW = "ResetOptions"; - -void OptionListRow::Load( OptionsList *pOptions, const RString &sType ) -{ - m_pOptions = pOptions; - ITEMS_SPACING_Y .Load(sType,"ItemsSpacingY"); - - m_Text.resize( 1 ); - m_Text[0].SetName( "Text" ); - m_Text[0].LoadFromFont( THEME->GetPathF(sType, "normal") ); - ActorUtil::LoadAllCommands( m_Text[0], sType ); - - m_Underlines.resize( 1 ); - m_Underlines[0].Load( THEME->GetPathG(sType, "underline") ); - m_Underlines[0]->SetName( "Underline" ); - ActorUtil::LoadAllCommands( *m_Underlines[0], sType ); - - m_Text[0].PlayCommand( "On" ); - m_Underlines[0]->PlayCommand( "On" ); -} - -// GetTitleForHandler -// can always use the title -// can have special speed row titles, note skin, or any SELECT_ONE - -void OptionListRow::SetFromHandler( const OptionRowHandler *pHandler ) -{ - this->FinishTweening(); - this->RemoveAllChildren(); - - if( pHandler == NULL ) - return; - - int iNum = max( pHandler->m_Def.m_vsChoices.size(), m_Text.size() )+1; - m_Text.resize( iNum, m_Text[0] ); - m_Underlines.resize( iNum, m_Underlines[0] ); - - for( unsigned i = 0; i < pHandler->m_Def.m_vsChoices.size(); ++i ) - { - // init underlines - this->AddChild( m_Underlines[i] ); - - // init text - this->AddChild( &m_Text[i] ); - } - - SetTextFromHandler( pHandler ); - - const unsigned iCnt = pHandler->m_Def.m_vsChoices.size(); - m_bItemsInTwoRows = (int) iCnt > MAX_ITEMS_BEFORE_SPLIT; - const float fWidth = ITEMS_SPLIT_WIDTH; - float fY = 0; - for( unsigned i = 0; i < iCnt; ++i ) - { - float fX = 0; - if( m_bItemsInTwoRows ) - { - if( (i % 2) == 0 ) - fX = -fWidth/2; - else - fX = +fWidth/2; - } - - // set the Y position of each item in the line - m_Text[i].SetXY( fX, fY ); - m_Underlines[i]->SetXY( fX, fY ); - - if( m_bItemsInTwoRows ) - m_Underlines[i]->PlayCommand( "SetTwoRows" ); - else - m_Underlines[i]->PlayCommand( "SetOneRow" ); - - if( !m_bItemsInTwoRows || (i % 2) == 1 || i+1 == iCnt ) - fY += ITEMS_SPACING_Y; - } - - int iExit = pHandler->m_Def.m_vsChoices.size(); - m_Text[iExit].SetText( "Exit" ); // XXX localize - m_Text[iExit].SetXY( 0, fY ); - this->AddChild( &m_Text[iExit] ); -} - -void OptionListRow::SetTextFromHandler( const OptionRowHandler *pHandler ) -{ - ASSERT( pHandler != NULL ); - for( unsigned i = 0; i < pHandler->m_Def.m_vsChoices.size(); ++i ) - { - // init text - RString sText = pHandler->GetThemedItemText( i ); - - RString sDest = pHandler->GetScreen( i ); - if( m_pOptions->m_setDirectRows.find(sDest) != m_pOptions->m_setDirectRows.end() && sDest.size() ) - { - const OptionRowHandler *pTarget = m_pOptions->m_Rows[sDest]; - if( pTarget->m_Def.m_selectType == SELECT_ONE ) - { - int iSelection = m_pOptions->GetOneSelection(sDest); - sText += ": " + pTarget->GetThemedItemText( iSelection ); - } - } - - m_Text[i].SetText( sText ); - } -} - -void OptionListRow::SetUnderlines( const vector &aSelections, const OptionRowHandler *pHandler ) -{ - for( unsigned i = 0; i < aSelections.size(); ++i ) - { - Actor *pActor = m_Underlines[i]; - - bool bSelected = aSelections[i]; - RString sDest = pHandler->GetScreen( i ); - if( sDest.size() ) - { - /* This is a submenu. Underline the row if its options have been changed - * from the default. */ - const OptionRowHandler *pTarget = m_pOptions->m_Rows[sDest]; - if( pTarget->m_Def.m_selectType == SELECT_ONE ) - { - int iSelection = m_pOptions->GetOneSelection(sDest); - const OptionRowHandler *lHandler = m_pOptions->m_Rows.find(sDest)->second; - int iDefault = lHandler->GetDefaultOption(); - if( iDefault != -1 && iSelection != iDefault ) - bSelected |= true; - } - else if( pTarget->m_Def.m_selectType == SELECT_MULTIPLE ) - { - const vector &bTargetSelections = m_pOptions->m_bSelections.find(sDest)->second; - for( unsigned j=0; jPlayCommand( bSelected?"Show":"Hide" ); - } -} - -void OptionListRow::PositionCursor( Actor *pCursor, int iSelection ) -{ - float fX = m_Text[iSelection].GetDestX(); - float fY = m_Text[iSelection].GetDestY(); - if( m_bItemsInTwoRows ) - pCursor->PlayCommand( "PositionTwoRows" ); - else - pCursor->PlayCommand( "PositionOneRow" ); - pCursor->SetXY( fX, fY ); -} - -OptionsList::OptionsList() -{ - m_iCurrentRow = 0; - m_pLinked = NULL; -} - -OptionsList::~OptionsList() -{ - FOREACHM( RString, OptionRowHandler *, m_Rows, hand ) - delete hand->second; -} - -void OptionsList::Load( RString sType, PlayerNumber pn ) -{ - TOP_MENU.Load( sType, "TopMenu" ); - - m_pn = pn; - m_bStartIsDown = false; - - m_Codes.Load( sType ); - - m_Cursor.Load( THEME->GetPathG(sType, "cursor") ); - m_Cursor->SetName( "Cursor" ); - ActorUtil::LoadAllCommands( *m_Cursor, sType ); - this->AddChild( m_Cursor ); - - vector asDirectLines; - split( DIRECT_LINES, ",", asDirectLines, true ); - FOREACH( RString, asDirectLines, s ) - m_setDirectRows.insert( *s ); - - vector setToLoad; - split( TOP_MENUS, ",", setToLoad ); - m_setTopMenus.insert( setToLoad.begin(), setToLoad.end() ); - - while( !setToLoad.empty() ) - { - RString sLineName = *setToLoad.begin(); - setToLoad.erase( setToLoad.begin() ); - - if( m_Rows.find(sLineName) != m_Rows.end() ) - continue; - - RString sRowCommands = LINE(sLineName); - Commands cmds; - ParseCommands( sRowCommands, cmds ); - - OptionRowHandler *pHand = OptionRowHandlerUtil::Make( cmds ); - if( pHand == NULL ) - RageException::Throw( "Invalid OptionRowHandler '%s' in %s::Line%s", cmds.GetOriginalCommandString().c_str(), m_sName.c_str(), sLineName.c_str() ); - - m_Rows[sLineName] = pHand; - m_asLoadedRows.push_back( sLineName ); - - for( size_t i = 0; i < pHand->m_Def.m_vsChoices.size(); ++i ) - { - RString sScreen = pHand->GetScreen(i); - if( !sScreen.empty() ) - setToLoad.push_back( sScreen ); - } - } - - for( int i = 0; i < 2; ++i ) - { - m_Row[i].SetName( "OptionsList" ); - m_Row[i].Load( this, "OptionsList" ); - ActorUtil::LoadAllCommands( m_Row[i], sType ); - this->AddChild( &m_Row[i] ); - } - - this->PlayCommand( "TweenOff" ); - this->FinishTweening(); -} - -void OptionsList::Reset() -{ - /* Import options. */ - FOREACHM( RString, OptionRowHandler *, m_Rows, hand ) - { - RString sLineName = hand->first; - ImportRow( sLineName ); - } -} - -void OptionsList::Open() -{ - this->PlayCommand( "Reset" ); - - Message msg("OptionsListOpened"); - msg.SetParam( "Player", m_pn ); - MESSAGEMAN->Broadcast( msg ); - - /* Push the initial menu. */ - ASSERT( m_asMenuStack.size() == 0 ); - Push( TOP_MENU ); - - this->FinishTweening(); - m_Row[!m_iCurrentRow].SetFromHandler( NULL ); - this->PlayCommand( "TweenOn" ); -} - -void OptionsList::Close() -{ - Message msg("OptionsListClosed"); - msg.SetParam( "Player", m_pn ); - MESSAGEMAN->Broadcast( msg ); - - m_bStartIsDown = false; - m_asMenuStack.clear(); - this->PlayCommand( "TweenOff" ); -} - -RString OptionsList::GetCurrentRow() const -{ - ASSERT( !m_asMenuStack.empty() ); // called while the menu was closed - return m_asMenuStack.back(); -} - -const OptionRowHandler *OptionsList::GetCurrentHandler() -{ - RString sCurrentRow = GetCurrentRow(); - return m_Rows[sCurrentRow]; -} - -int OptionsList::GetOneSelection( RString sRow, bool bAllowFail ) const -{ - map >::const_iterator it = m_bSelections.find(sRow); - ASSERT_M( it != m_bSelections.end(), sRow ); - const vector &bSelections = it->second; - for( unsigned i=0; i= 0 ) - { - if( iCurrentRow >= (int) pHandler->m_Def.m_vsChoices.size() ) - return; - RString sDest = pHandler->GetScreen( iCurrentRow ); - if( sDest.empty() ) - return; - - if( m_asMenuStack.size() == 1 ) - m_asMenuStack.push_back( sDest ); - else - m_asMenuStack.back() = sDest; - } - else - { - if( m_asMenuStack.size() == 1 ) - return; - - m_asMenuStack.pop_back(); - } - - SetDefaultCurrentRow(); - SwitchToCurrentRow(); - TweenOnCurrentRow( iDir > 0 ); -} - -void OptionsList::MoveItem( const RString &sRowName, int iMove ) -{ -} - -bool OptionsList::Input( const InputEventPlus &input ) -{ - Message msg(""); - if( m_Codes.InputMessage(input, msg) ) - this->HandleMessage( msg ); - - const OptionRowHandler *pHandler = GetCurrentHandler(); - - PlayerNumber pn = input.pn; - if( m_bStartIsDown ) - { - if( input.MenuI == GAME_BUTTON_LEFT || input.MenuI == GAME_BUTTON_RIGHT ) - { - if( input.type != IET_FIRST_PRESS ) - return false; - - m_bAcceptStartRelease = false; - - const RString &sCurrentRow = m_asMenuStack.back(); - vector &bSelections = m_bSelections[sCurrentRow]; - if( m_iMenuStackSelection == (int)bSelections.size() ) - return false; - - RString sDest = pHandler->GetScreen( m_iMenuStackSelection ); - if( m_setDirectRows.find(sDest) != m_setDirectRows.end() && sDest.size() ) - { - const OptionRowHandler *pTarget = m_Rows[sDest]; - vector &bTargetSelections = m_bSelections[sDest]; - - if( pTarget->m_Def.m_selectType == SELECT_ONE ) - { - int iSelection = GetOneSelection(sDest); - int iDir = (input.MenuI == GAME_BUTTON_RIGHT? +1:-1); - iSelection += iDir; - wrap( iSelection, bTargetSelections.size() ); - SelectItem( sDest, iSelection ); - - Message lMsg("OptionsListQuickChange"); - lMsg.SetParam( "Player", pn ); - lMsg.SetParam( "Direction", iDir ); - MESSAGEMAN->Broadcast( lMsg ); - } - } - return true; - } - } - - if( input.MenuI == GAME_BUTTON_LEFT ) - { - if( input.type == IET_RELEASE ) - return false; - - if( INPUTMAPPER->IsBeingPressed(GAME_BUTTON_RIGHT, pn) ) - { - if( input.type == IET_FIRST_PRESS ) - SwitchMenu( -1 ); - return true; - } - - --m_iMenuStackSelection; - wrap( m_iMenuStackSelection, pHandler->m_Def.m_vsChoices.size()+1 ); // +1 for exit row - PositionCursor(); - - Message lMsg("OptionsListLeft"); - lMsg.SetParam( "Player", input.pn ); - MESSAGEMAN->Broadcast( lMsg ); - return true; - } - else if( input.MenuI == GAME_BUTTON_RIGHT ) - { - if( input.type == IET_RELEASE ) - return false; - - if( INPUTMAPPER->IsBeingPressed(GAME_BUTTON_LEFT, pn) ) - { - if( input.type == IET_FIRST_PRESS ) - SwitchMenu( +1 ); - return true; - } - - ++m_iMenuStackSelection; - wrap( m_iMenuStackSelection, pHandler->m_Def.m_vsChoices.size()+1 ); // +1 for exit row - PositionCursor(); - - Message lMsg("OptionsListRight"); - lMsg.SetParam( "Player", input.pn ); - MESSAGEMAN->Broadcast( lMsg ); - return true; - } - else if( input.MenuI == GAME_BUTTON_START ) - { - if( input.type == IET_FIRST_PRESS ) - { - m_bStartIsDown = true; - m_bAcceptStartRelease = true; - return true; - } - if( input.type == IET_RELEASE ) - { - if( m_bAcceptStartRelease ) - Start(); - m_bStartIsDown = false; - } - - return true; - } - else if( input.MenuI == GAME_BUTTON_SELECT ) - { - if( input.type != IET_FIRST_PRESS ) - return false; -// if( input.type == IET_RELEASE ) - { - Close(); - return true; - } - return true; - } - return false; -} - -void OptionsList::SwitchToCurrentRow() -{ - m_iCurrentRow = !m_iCurrentRow; - - /* Set up the new row. */ - m_Row[m_iCurrentRow].SetFromHandler( GetCurrentHandler() ); - m_Row[m_iCurrentRow].SetUnderlines( m_bSelections[m_asMenuStack.back()], GetCurrentHandler() ); - PositionCursor(); - - Message msg("OptionsMenuChanged"); - msg.SetParam( "Player", m_pn ); - msg.SetParam( "Menu", m_asMenuStack.back() ); - MESSAGEMAN->Broadcast( msg ); -} - -/* After setting up a new row, tween it on. */ -void OptionsList::TweenOnCurrentRow( bool bForward ) -{ - OptionListRow &OldRow = m_Row[!m_iCurrentRow]; - OptionListRow &NewRow = m_Row[m_iCurrentRow]; - - /* Tween out the old row. */ - if( bForward ) - OldRow.PlayCommand( "TweenOutForward" ); - else - OldRow.PlayCommand( "TweenOutBackward" ); - - /* Tween in the old row. */ - if( bForward ) - NewRow.PlayCommand( "TweenInForward" ); - else - NewRow.PlayCommand( "TweenInBackward" ); -} - -void OptionsList::ImportRow( RString sRow ) -{ - vector aSelections[NUM_PLAYERS]; - vector vpns; - vpns.push_back( m_pn ); - OptionRowHandler *pHandler = m_Rows[sRow]; - aSelections[ m_pn ].resize( pHandler->m_Def.m_vsChoices.size() ); - pHandler->ImportOption( NULL, vpns, aSelections ); - m_bSelections[sRow] = aSelections[ m_pn ]; - - if( m_setTopMenus.find(sRow) != m_setTopMenus.end() ) - fill( m_bSelections[sRow].begin(), m_bSelections[sRow].end(), false ); -} - -void OptionsList::ExportRow( RString sRow ) -{ - if( m_setTopMenus.find(sRow) != m_setTopMenus.end() ) - return; - - vector aSelections[NUM_PLAYERS]; - aSelections[m_pn] = m_bSelections[sRow]; - - vector vpns; - vpns.push_back( m_pn ); - - m_Rows[sRow]->ExportOption( vpns, aSelections ); -} - -void OptionsList::SetDefaultCurrentRow() -{ - /* If all items on the row just point to other menus, default to 0. */ - m_iMenuStackSelection = 0; - - const RString &sCurrentRow = m_asMenuStack.back(); - const OptionRowHandler *pHandler = m_Rows.find(sCurrentRow)->second; - if( pHandler->m_Def.m_selectType == SELECT_ONE ) - { - /* One item is selected, so position the cursor on it. */ - m_iMenuStackSelection = GetOneSelection( sCurrentRow, true ); - if( m_iMenuStackSelection == -1 ) - m_iMenuStackSelection = 0; - } -} - -int OptionsList::FindScreenInHandler( const OptionRowHandler *pHandler, RString sScreen ) -{ - for( size_t i = 0; i < pHandler->m_Def.m_vsChoices.size(); ++i ) - { - if( pHandler->GetScreen(i) == sScreen ) - return i; - } - return -1; -} - -void OptionsList::Pop() -{ - if( m_asMenuStack.size() == 1 ) - { - Close(); - return; - } - - RString sLastMenu = m_asMenuStack.back(); - - m_asMenuStack.pop_back(); - - /* Choose the default option. */ - SetDefaultCurrentRow(); - - /* If the old menu exists as a target from the new menu, switch to it. */ - const OptionRowHandler *pHandler = GetCurrentHandler(); - int iIndex = FindScreenInHandler( pHandler, sLastMenu ); - if( iIndex != -1 ) - m_iMenuStackSelection = iIndex; - - SwitchToCurrentRow(); - TweenOnCurrentRow( false ); -} - -void OptionsList::Push( RString sDest ) -{ - m_asMenuStack.push_back( sDest ); - SetDefaultCurrentRow(); - SwitchToCurrentRow(); -} - -void OptionsList::SelectItem( const RString &sRowName, int iMenuItem ) -{ - const OptionRowHandler *pHandler = m_Rows[sRowName]; - vector &bSelections = m_bSelections[sRowName]; - - if( pHandler->m_Def.m_selectType == SELECT_MULTIPLE ) - { - bool bSelected = !bSelections[iMenuItem]; - bSelections[iMenuItem] = bSelected; - -// if( bSelected ) -// m_SoundToggleOn.Play(); -// else -// m_SoundToggleOff.Play(); - } - else // data.selectType != SELECT_MULTIPLE - { - fill( bSelections.begin(), bSelections.end(), false ); - bSelections[iMenuItem] = true; - } - - SelectionsChanged( sRowName ); - UpdateMenuFromSelections(); -} - -void OptionsList::SelectionsChanged( const RString &sRowName ) -{ - const OptionRowHandler *pHandler = m_Rows[sRowName]; - vector &bSelections = m_bSelections[sRowName]; - - if( pHandler->m_Def.m_bOneChoiceForAllPlayers && m_pLinked != NULL ) - { - vector &bLinkedSelections = m_pLinked->m_bSelections[sRowName]; - bLinkedSelections = bSelections; - - if( m_pLinked->IsOpened() ) - m_pLinked->UpdateMenuFromSelections(); - - m_pLinked->ExportRow( sRowName ); - } - - ExportRow( sRowName ); -} - -void OptionsList::UpdateMenuFromSelections() -{ - const vector &bCurrentSelections = m_bSelections.find(GetCurrentRow())->second; - m_Row[m_iCurrentRow].SetUnderlines( bCurrentSelections, GetCurrentHandler() ); - m_Row[m_iCurrentRow].SetTextFromHandler( GetCurrentHandler() ); -} - -bool OptionsList::Start() -{ - const OptionRowHandler *pHandler = GetCurrentHandler(); - const RString &sCurrentRow = m_asMenuStack.back(); - vector &bSelections = m_bSelections[sCurrentRow]; - if( m_iMenuStackSelection == (int)bSelections.size() ) - { - Pop(); - - Message msg("OptionsListPop"); - msg.SetParam( "Player", m_pn ); - MESSAGEMAN->Broadcast( msg ); - - return m_asMenuStack.empty(); - } - - { - RString sIconText; - GameCommand gc; - pHandler->GetIconTextAndGameCommand( m_iMenuStackSelection, sIconText, gc ); - if( gc.m_sName == RESET_ROW ) - { - GAMESTATE->m_pPlayerState[m_pn]->ResetToDefaultPlayerOptions( ModsLevel_Preferred ); - GAMESTATE->ResetToDefaultSongOptions( ModsLevel_Preferred ); - - /* Import options. */ - FOREACHM( RString, OptionRowHandler *, m_Rows, hand ) - { - ImportRow( hand->first ); - SelectionsChanged( hand->first ); - } - - UpdateMenuFromSelections(); - - Message msg("OptionsListReset"); - msg.SetParam( "Player", m_pn ); - MESSAGEMAN->Broadcast( msg ); - - return false; - } - } - - RString sDest = pHandler->GetScreen( m_iMenuStackSelection ); - if( sDest.size() ) - { - Push( sDest ); - TweenOnCurrentRow( true ); - - Message msg("OptionsListPush"); - msg.SetParam( "Player", m_pn ); - MESSAGEMAN->Broadcast( msg ); - - return false; - } - - SelectItem( GetCurrentRow(), m_iMenuStackSelection ); - - /* Move to the exit row. */ - m_iMenuStackSelection = (int)bSelections.size(); - PositionCursor(); - - Message msg("OptionsListStart"); - msg.SetParam( "Player", m_pn ); - MESSAGEMAN->Broadcast( msg ); - - return false; -} - -/* - * Copyright (c) 2006 Glenn Maynard - * All rights reserved. - * - * Permission is hereby granted, free of charge, to any person obtaining a - * copy of this software and associated documentation files (the - * "Software"), to deal in the Software without restriction, including - * without limitation the rights to use, copy, modify, merge, publish, - * distribute, and/or sell copies of the Software, and to permit persons to - * whom the Software is furnished to do so, provided that the above - * copyright notice(s) and this permission notice appear in all copies of - * the Software and that both the above copyright notice(s) and this - * permission notice appear in supporting documentation. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS - * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF - * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS - * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT - * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS - * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR - * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR - * PERFORMANCE OF THIS SOFTWARE. - */ - +#include "global.h" +#include "OptionsList.h" +#include "GameState.h" +#include "RageLog.h" +#include "Course.h" +#include "SongUtil.h" +#include "StepsUtil.h" +#include "Style.h" +#include "InputEventPlus.h" +#include "CodeDetector.h" +#include "InputMapper.h" +#include "PlayerState.h" + +#define LINE(sLineName) THEME->GetMetric (m_sName,ssprintf("Line%s",sLineName.c_str())) +#define MAX_ITEMS_BEFORE_SPLIT THEME->GetMetricI(m_sName,"MaxItemsBeforeSplit") +#define ITEMS_SPLIT_WIDTH THEME->GetMetricF(m_sName,"ItemsSplitWidth") +#define DIRECT_LINES THEME->GetMetric (m_sName,"DirectLines") +#define TOP_MENUS THEME->GetMetric (m_sName,"TopMenus") + +static const RString RESET_ROW = "ResetOptions"; + +void OptionListRow::Load( OptionsList *pOptions, const RString &sType ) +{ + m_pOptions = pOptions; + ITEMS_SPACING_Y .Load(sType,"ItemsSpacingY"); + + m_Text.resize( 1 ); + m_Text[0].SetName( "Text" ); + m_Text[0].LoadFromFont( THEME->GetPathF(sType, "normal") ); + ActorUtil::LoadAllCommands( m_Text[0], sType ); + + m_Underlines.resize( 1 ); + m_Underlines[0].Load( THEME->GetPathG(sType, "underline") ); + m_Underlines[0]->SetName( "Underline" ); + ActorUtil::LoadAllCommands( *m_Underlines[0], sType ); + + m_Text[0].PlayCommand( "On" ); + m_Underlines[0]->PlayCommand( "On" ); +} + +// GetTitleForHandler +// can always use the title +// can have special speed row titles, note skin, or any SELECT_ONE + +void OptionListRow::SetFromHandler( const OptionRowHandler *pHandler ) +{ + this->FinishTweening(); + this->RemoveAllChildren(); + + if( pHandler == NULL ) + return; + + int iNum = max( pHandler->m_Def.m_vsChoices.size(), m_Text.size() )+1; + m_Text.resize( iNum, m_Text[0] ); + m_Underlines.resize( iNum, m_Underlines[0] ); + + for( unsigned i = 0; i < pHandler->m_Def.m_vsChoices.size(); ++i ) + { + // init underlines + this->AddChild( m_Underlines[i] ); + + // init text + this->AddChild( &m_Text[i] ); + } + + SetTextFromHandler( pHandler ); + + const unsigned iCnt = pHandler->m_Def.m_vsChoices.size(); + m_bItemsInTwoRows = (int) iCnt > MAX_ITEMS_BEFORE_SPLIT; + const float fWidth = ITEMS_SPLIT_WIDTH; + float fY = 0; + for( unsigned i = 0; i < iCnt; ++i ) + { + float fX = 0; + if( m_bItemsInTwoRows ) + { + if( (i % 2) == 0 ) + fX = -fWidth/2; + else + fX = +fWidth/2; + } + + // set the Y position of each item in the line + m_Text[i].SetXY( fX, fY ); + m_Underlines[i]->SetXY( fX, fY ); + + if( m_bItemsInTwoRows ) + m_Underlines[i]->PlayCommand( "SetTwoRows" ); + else + m_Underlines[i]->PlayCommand( "SetOneRow" ); + + if( !m_bItemsInTwoRows || (i % 2) == 1 || i+1 == iCnt ) + fY += ITEMS_SPACING_Y; + } + + int iExit = pHandler->m_Def.m_vsChoices.size(); + m_Text[iExit].SetText( "Exit" ); // XXX localize + m_Text[iExit].SetXY( 0, fY ); + this->AddChild( &m_Text[iExit] ); +} + +void OptionListRow::SetTextFromHandler( const OptionRowHandler *pHandler ) +{ + ASSERT( pHandler != NULL ); + for( unsigned i = 0; i < pHandler->m_Def.m_vsChoices.size(); ++i ) + { + // init text + RString sText = pHandler->GetThemedItemText( i ); + + RString sDest = pHandler->GetScreen( i ); + if( m_pOptions->m_setDirectRows.find(sDest) != m_pOptions->m_setDirectRows.end() && sDest.size() ) + { + const OptionRowHandler *pTarget = m_pOptions->m_Rows[sDest]; + if( pTarget->m_Def.m_selectType == SELECT_ONE ) + { + int iSelection = m_pOptions->GetOneSelection(sDest); + sText += ": " + pTarget->GetThemedItemText( iSelection ); + } + } + + m_Text[i].SetText( sText ); + } +} + +void OptionListRow::SetUnderlines( const vector &aSelections, const OptionRowHandler *pHandler ) +{ + for( unsigned i = 0; i < aSelections.size(); ++i ) + { + Actor *pActor = m_Underlines[i]; + + bool bSelected = aSelections[i]; + RString sDest = pHandler->GetScreen( i ); + if( sDest.size() ) + { + /* This is a submenu. Underline the row if its options have been changed + * from the default. */ + const OptionRowHandler *pTarget = m_pOptions->m_Rows[sDest]; + if( pTarget->m_Def.m_selectType == SELECT_ONE ) + { + int iSelection = m_pOptions->GetOneSelection(sDest); + const OptionRowHandler *lHandler = m_pOptions->m_Rows.find(sDest)->second; + int iDefault = lHandler->GetDefaultOption(); + if( iDefault != -1 && iSelection != iDefault ) + bSelected |= true; + } + else if( pTarget->m_Def.m_selectType == SELECT_MULTIPLE ) + { + const vector &bTargetSelections = m_pOptions->m_bSelections.find(sDest)->second; + for( unsigned j=0; jPlayCommand( bSelected?"Show":"Hide" ); + } +} + +void OptionListRow::PositionCursor( Actor *pCursor, int iSelection ) +{ + float fX = m_Text[iSelection].GetDestX(); + float fY = m_Text[iSelection].GetDestY(); + if( m_bItemsInTwoRows ) + pCursor->PlayCommand( "PositionTwoRows" ); + else + pCursor->PlayCommand( "PositionOneRow" ); + pCursor->SetXY( fX, fY ); +} + +OptionsList::OptionsList() +{ + m_iCurrentRow = 0; + m_pLinked = NULL; +} + +OptionsList::~OptionsList() +{ + for (std::pair hand : m_Rows) + delete hand.second; +} + +void OptionsList::Load( RString sType, PlayerNumber pn ) +{ + TOP_MENU.Load( sType, "TopMenu" ); + + m_pn = pn; + m_bStartIsDown = false; + + m_Codes.Load( sType ); + + m_Cursor.Load( THEME->GetPathG(sType, "cursor") ); + m_Cursor->SetName( "Cursor" ); + ActorUtil::LoadAllCommands( *m_Cursor, sType ); + this->AddChild( m_Cursor ); + + vector asDirectLines; + split( DIRECT_LINES, ",", asDirectLines, true ); + FOREACH( RString, asDirectLines, s ) + m_setDirectRows.insert( *s ); + + vector setToLoad; + split( TOP_MENUS, ",", setToLoad ); + m_setTopMenus.insert( setToLoad.begin(), setToLoad.end() ); + + while( !setToLoad.empty() ) + { + RString sLineName = *setToLoad.begin(); + setToLoad.erase( setToLoad.begin() ); + + if( m_Rows.find(sLineName) != m_Rows.end() ) + continue; + + RString sRowCommands = LINE(sLineName); + Commands cmds; + ParseCommands( sRowCommands, cmds ); + + OptionRowHandler *pHand = OptionRowHandlerUtil::Make( cmds ); + if( pHand == NULL ) + RageException::Throw( "Invalid OptionRowHandler '%s' in %s::Line%s", cmds.GetOriginalCommandString().c_str(), m_sName.c_str(), sLineName.c_str() ); + + m_Rows[sLineName] = pHand; + m_asLoadedRows.push_back( sLineName ); + + for( size_t i = 0; i < pHand->m_Def.m_vsChoices.size(); ++i ) + { + RString sScreen = pHand->GetScreen(i); + if( !sScreen.empty() ) + setToLoad.push_back( sScreen ); + } + } + + for( int i = 0; i < 2; ++i ) + { + m_Row[i].SetName( "OptionsList" ); + m_Row[i].Load( this, "OptionsList" ); + ActorUtil::LoadAllCommands( m_Row[i], sType ); + this->AddChild( &m_Row[i] ); + } + + this->PlayCommand( "TweenOff" ); + this->FinishTweening(); +} + +void OptionsList::Reset() +{ + /* Import options. */ + for (std::pair hand : m_Rows) + { + ImportRow(hand.first); + } +} + +void OptionsList::Open() +{ + this->PlayCommand( "Reset" ); + + Message msg("OptionsListOpened"); + msg.SetParam( "Player", m_pn ); + MESSAGEMAN->Broadcast( msg ); + + /* Push the initial menu. */ + ASSERT( m_asMenuStack.size() == 0 ); + Push( TOP_MENU ); + + this->FinishTweening(); + m_Row[!m_iCurrentRow].SetFromHandler( NULL ); + this->PlayCommand( "TweenOn" ); +} + +void OptionsList::Close() +{ + Message msg("OptionsListClosed"); + msg.SetParam( "Player", m_pn ); + MESSAGEMAN->Broadcast( msg ); + + m_bStartIsDown = false; + m_asMenuStack.clear(); + this->PlayCommand( "TweenOff" ); +} + +RString OptionsList::GetCurrentRow() const +{ + ASSERT( !m_asMenuStack.empty() ); // called while the menu was closed + return m_asMenuStack.back(); +} + +const OptionRowHandler *OptionsList::GetCurrentHandler() +{ + RString sCurrentRow = GetCurrentRow(); + return m_Rows[sCurrentRow]; +} + +int OptionsList::GetOneSelection( RString sRow, bool bAllowFail ) const +{ + map >::const_iterator it = m_bSelections.find(sRow); + ASSERT_M( it != m_bSelections.end(), sRow ); + const vector &bSelections = it->second; + for( unsigned i=0; i= 0 ) + { + if( iCurrentRow >= (int) pHandler->m_Def.m_vsChoices.size() ) + return; + RString sDest = pHandler->GetScreen( iCurrentRow ); + if( sDest.empty() ) + return; + + if( m_asMenuStack.size() == 1 ) + m_asMenuStack.push_back( sDest ); + else + m_asMenuStack.back() = sDest; + } + else + { + if( m_asMenuStack.size() == 1 ) + return; + + m_asMenuStack.pop_back(); + } + + SetDefaultCurrentRow(); + SwitchToCurrentRow(); + TweenOnCurrentRow( iDir > 0 ); +} + +void OptionsList::MoveItem( const RString &sRowName, int iMove ) +{ +} + +bool OptionsList::Input( const InputEventPlus &input ) +{ + Message msg(""); + if( m_Codes.InputMessage(input, msg) ) + this->HandleMessage( msg ); + + const OptionRowHandler *pHandler = GetCurrentHandler(); + + PlayerNumber pn = input.pn; + if( m_bStartIsDown ) + { + if( input.MenuI == GAME_BUTTON_LEFT || input.MenuI == GAME_BUTTON_RIGHT ) + { + if( input.type != IET_FIRST_PRESS ) + return false; + + m_bAcceptStartRelease = false; + + const RString &sCurrentRow = m_asMenuStack.back(); + vector &bSelections = m_bSelections[sCurrentRow]; + if( m_iMenuStackSelection == (int)bSelections.size() ) + return false; + + RString sDest = pHandler->GetScreen( m_iMenuStackSelection ); + if( m_setDirectRows.find(sDest) != m_setDirectRows.end() && sDest.size() ) + { + const OptionRowHandler *pTarget = m_Rows[sDest]; + vector &bTargetSelections = m_bSelections[sDest]; + + if( pTarget->m_Def.m_selectType == SELECT_ONE ) + { + int iSelection = GetOneSelection(sDest); + int iDir = (input.MenuI == GAME_BUTTON_RIGHT? +1:-1); + iSelection += iDir; + wrap( iSelection, bTargetSelections.size() ); + SelectItem( sDest, iSelection ); + + Message lMsg("OptionsListQuickChange"); + lMsg.SetParam( "Player", pn ); + lMsg.SetParam( "Direction", iDir ); + MESSAGEMAN->Broadcast( lMsg ); + } + } + return true; + } + } + + if( input.MenuI == GAME_BUTTON_LEFT ) + { + if( input.type == IET_RELEASE ) + return false; + + if( INPUTMAPPER->IsBeingPressed(GAME_BUTTON_RIGHT, pn) ) + { + if( input.type == IET_FIRST_PRESS ) + SwitchMenu( -1 ); + return true; + } + + --m_iMenuStackSelection; + wrap( m_iMenuStackSelection, pHandler->m_Def.m_vsChoices.size()+1 ); // +1 for exit row + PositionCursor(); + + Message lMsg("OptionsListLeft"); + lMsg.SetParam( "Player", input.pn ); + MESSAGEMAN->Broadcast( lMsg ); + return true; + } + else if( input.MenuI == GAME_BUTTON_RIGHT ) + { + if( input.type == IET_RELEASE ) + return false; + + if( INPUTMAPPER->IsBeingPressed(GAME_BUTTON_LEFT, pn) ) + { + if( input.type == IET_FIRST_PRESS ) + SwitchMenu( +1 ); + return true; + } + + ++m_iMenuStackSelection; + wrap( m_iMenuStackSelection, pHandler->m_Def.m_vsChoices.size()+1 ); // +1 for exit row + PositionCursor(); + + Message lMsg("OptionsListRight"); + lMsg.SetParam( "Player", input.pn ); + MESSAGEMAN->Broadcast( lMsg ); + return true; + } + else if( input.MenuI == GAME_BUTTON_START ) + { + if( input.type == IET_FIRST_PRESS ) + { + m_bStartIsDown = true; + m_bAcceptStartRelease = true; + return true; + } + if( input.type == IET_RELEASE ) + { + if( m_bAcceptStartRelease ) + Start(); + m_bStartIsDown = false; + } + + return true; + } + else if( input.MenuI == GAME_BUTTON_SELECT ) + { + if( input.type != IET_FIRST_PRESS ) + return false; +// if( input.type == IET_RELEASE ) + { + Close(); + return true; + } + return true; + } + return false; +} + +void OptionsList::SwitchToCurrentRow() +{ + m_iCurrentRow = !m_iCurrentRow; + + /* Set up the new row. */ + m_Row[m_iCurrentRow].SetFromHandler( GetCurrentHandler() ); + m_Row[m_iCurrentRow].SetUnderlines( m_bSelections[m_asMenuStack.back()], GetCurrentHandler() ); + PositionCursor(); + + Message msg("OptionsMenuChanged"); + msg.SetParam( "Player", m_pn ); + msg.SetParam( "Menu", m_asMenuStack.back() ); + MESSAGEMAN->Broadcast( msg ); +} + +/* After setting up a new row, tween it on. */ +void OptionsList::TweenOnCurrentRow( bool bForward ) +{ + OptionListRow &OldRow = m_Row[!m_iCurrentRow]; + OptionListRow &NewRow = m_Row[m_iCurrentRow]; + + /* Tween out the old row. */ + if( bForward ) + OldRow.PlayCommand( "TweenOutForward" ); + else + OldRow.PlayCommand( "TweenOutBackward" ); + + /* Tween in the old row. */ + if( bForward ) + NewRow.PlayCommand( "TweenInForward" ); + else + NewRow.PlayCommand( "TweenInBackward" ); +} + +void OptionsList::ImportRow( RString sRow ) +{ + vector aSelections[NUM_PLAYERS]; + vector vpns; + vpns.push_back( m_pn ); + OptionRowHandler *pHandler = m_Rows[sRow]; + aSelections[ m_pn ].resize( pHandler->m_Def.m_vsChoices.size() ); + pHandler->ImportOption( NULL, vpns, aSelections ); + m_bSelections[sRow] = aSelections[ m_pn ]; + + if( m_setTopMenus.find(sRow) != m_setTopMenus.end() ) + fill( m_bSelections[sRow].begin(), m_bSelections[sRow].end(), false ); +} + +void OptionsList::ExportRow( RString sRow ) +{ + if( m_setTopMenus.find(sRow) != m_setTopMenus.end() ) + return; + + vector aSelections[NUM_PLAYERS]; + aSelections[m_pn] = m_bSelections[sRow]; + + vector vpns; + vpns.push_back( m_pn ); + + m_Rows[sRow]->ExportOption( vpns, aSelections ); +} + +void OptionsList::SetDefaultCurrentRow() +{ + /* If all items on the row just point to other menus, default to 0. */ + m_iMenuStackSelection = 0; + + const RString &sCurrentRow = m_asMenuStack.back(); + const OptionRowHandler *pHandler = m_Rows.find(sCurrentRow)->second; + if( pHandler->m_Def.m_selectType == SELECT_ONE ) + { + /* One item is selected, so position the cursor on it. */ + m_iMenuStackSelection = GetOneSelection( sCurrentRow, true ); + if( m_iMenuStackSelection == -1 ) + m_iMenuStackSelection = 0; + } +} + +int OptionsList::FindScreenInHandler( const OptionRowHandler *pHandler, RString sScreen ) +{ + for( size_t i = 0; i < pHandler->m_Def.m_vsChoices.size(); ++i ) + { + if( pHandler->GetScreen(i) == sScreen ) + return i; + } + return -1; +} + +void OptionsList::Pop() +{ + if( m_asMenuStack.size() == 1 ) + { + Close(); + return; + } + + RString sLastMenu = m_asMenuStack.back(); + + m_asMenuStack.pop_back(); + + /* Choose the default option. */ + SetDefaultCurrentRow(); + + /* If the old menu exists as a target from the new menu, switch to it. */ + const OptionRowHandler *pHandler = GetCurrentHandler(); + int iIndex = FindScreenInHandler( pHandler, sLastMenu ); + if( iIndex != -1 ) + m_iMenuStackSelection = iIndex; + + SwitchToCurrentRow(); + TweenOnCurrentRow( false ); +} + +void OptionsList::Push( RString sDest ) +{ + m_asMenuStack.push_back( sDest ); + SetDefaultCurrentRow(); + SwitchToCurrentRow(); +} + +void OptionsList::SelectItem( const RString &sRowName, int iMenuItem ) +{ + const OptionRowHandler *pHandler = m_Rows[sRowName]; + vector &bSelections = m_bSelections[sRowName]; + + if( pHandler->m_Def.m_selectType == SELECT_MULTIPLE ) + { + bool bSelected = !bSelections[iMenuItem]; + bSelections[iMenuItem] = bSelected; + +// if( bSelected ) +// m_SoundToggleOn.Play(); +// else +// m_SoundToggleOff.Play(); + } + else // data.selectType != SELECT_MULTIPLE + { + fill( bSelections.begin(), bSelections.end(), false ); + bSelections[iMenuItem] = true; + } + + SelectionsChanged( sRowName ); + UpdateMenuFromSelections(); +} + +void OptionsList::SelectionsChanged( const RString &sRowName ) +{ + const OptionRowHandler *pHandler = m_Rows[sRowName]; + vector &bSelections = m_bSelections[sRowName]; + + if( pHandler->m_Def.m_bOneChoiceForAllPlayers && m_pLinked != NULL ) + { + vector &bLinkedSelections = m_pLinked->m_bSelections[sRowName]; + bLinkedSelections = bSelections; + + if( m_pLinked->IsOpened() ) + m_pLinked->UpdateMenuFromSelections(); + + m_pLinked->ExportRow( sRowName ); + } + + ExportRow( sRowName ); +} + +void OptionsList::UpdateMenuFromSelections() +{ + const vector &bCurrentSelections = m_bSelections.find(GetCurrentRow())->second; + m_Row[m_iCurrentRow].SetUnderlines( bCurrentSelections, GetCurrentHandler() ); + m_Row[m_iCurrentRow].SetTextFromHandler( GetCurrentHandler() ); +} + +bool OptionsList::Start() +{ + const OptionRowHandler *pHandler = GetCurrentHandler(); + const RString &sCurrentRow = m_asMenuStack.back(); + vector &bSelections = m_bSelections[sCurrentRow]; + if( m_iMenuStackSelection == (int)bSelections.size() ) + { + Pop(); + + Message msg("OptionsListPop"); + msg.SetParam( "Player", m_pn ); + MESSAGEMAN->Broadcast( msg ); + + return m_asMenuStack.empty(); + } + + { + RString sIconText; + GameCommand gc; + pHandler->GetIconTextAndGameCommand( m_iMenuStackSelection, sIconText, gc ); + if( gc.m_sName == RESET_ROW ) + { + GAMESTATE->m_pPlayerState[m_pn]->ResetToDefaultPlayerOptions( ModsLevel_Preferred ); + GAMESTATE->ResetToDefaultSongOptions( ModsLevel_Preferred ); + + /* Import options. */ + for (std::pair hand : m_Rows) + { + ImportRow( hand.first ); + SelectionsChanged( hand.first ); + } + + UpdateMenuFromSelections(); + + Message msg("OptionsListReset"); + msg.SetParam( "Player", m_pn ); + MESSAGEMAN->Broadcast( msg ); + + return false; + } + } + + RString sDest = pHandler->GetScreen( m_iMenuStackSelection ); + if( sDest.size() ) + { + Push( sDest ); + TweenOnCurrentRow( true ); + + Message msg("OptionsListPush"); + msg.SetParam( "Player", m_pn ); + MESSAGEMAN->Broadcast( msg ); + + return false; + } + + SelectItem( GetCurrentRow(), m_iMenuStackSelection ); + + /* Move to the exit row. */ + m_iMenuStackSelection = (int)bSelections.size(); + PositionCursor(); + + Message msg("OptionsListStart"); + msg.SetParam( "Player", m_pn ); + MESSAGEMAN->Broadcast( msg ); + + return false; +} + +/* + * Copyright (c) 2006 Glenn Maynard + * All rights reserved. + * + * Permission is hereby granted, free of charge, to any person obtaining a + * copy of this software and associated documentation files (the + * "Software"), to deal in the Software without restriction, including + * without limitation the rights to use, copy, modify, merge, publish, + * distribute, and/or sell copies of the Software, and to permit persons to + * whom the Software is furnished to do so, provided that the above + * copyright notice(s) and this permission notice appear in all copies of + * the Software and that both the above copyright notice(s) and this + * permission notice appear in supporting documentation. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS + * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF + * THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS + * INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT + * OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR + * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR + * PERFORMANCE OF THIS SOFTWARE. + */ +