DOOM-3-BFG

DOOM 3 BFG Edition
Log | Files | Refs

MenuWidget.cpp (14394B)


      1 /*
      2 ===========================================================================
      3 
      4 Doom 3 BFG Edition GPL Source Code
      5 Copyright (C) 1993-2012 id Software LLC, a ZeniMax Media company. 
      6 
      7 This file is part of the Doom 3 BFG Edition GPL Source Code ("Doom 3 BFG Edition Source Code").  
      8 
      9 Doom 3 BFG Edition Source Code is free software: you can redistribute it and/or modify
     10 it under the terms of the GNU General Public License as published by
     11 the Free Software Foundation, either version 3 of the License, or
     12 (at your option) any later version.
     13 
     14 Doom 3 BFG Edition Source Code is distributed in the hope that it will be useful,
     15 but WITHOUT ANY WARRANTY; without even the implied warranty of
     16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
     17 GNU General Public License for more details.
     18 
     19 You should have received a copy of the GNU General Public License
     20 along with Doom 3 BFG Edition Source Code.  If not, see <http://www.gnu.org/licenses/>.
     21 
     22 In addition, the Doom 3 BFG Edition Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the Doom 3 BFG Edition Source Code.  If not, please request a copy in writing from id Software at the address below.
     23 
     24 If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA.
     25 
     26 ===========================================================================
     27 */
     28 
     29 #pragma hdrstop
     30 #include "../../idLib/precompiled.h"
     31 #include "../Game_local.h"
     32 
     33 /*
     34 ========================
     35 idMenuWidget::idMenuWidget
     36 ========================
     37 */
     38 idMenuWidget::idMenuWidget() :
     39 	boundSprite( NULL ),
     40 	parent( NULL ),
     41 	dataSource( NULL ),
     42 	dataSourceFieldIndex( 0 ),
     43 	focusIndex( 0 ),
     44 	widgetState( WIDGET_STATE_NORMAL ),
     45 	menuData( NULL ),
     46 	swfObj( NULL ),
     47 	handlerIsParent( false ),
     48 	refCount( 0 ),
     49 	noAutoFree( false ) {
     50 
     51 	eventActionLookup.SetNum( eventActionLookup.Max() );
     52 	for ( int i = 0; i < eventActionLookup.Num(); ++i ) {
     53 		eventActionLookup[ i ] = INVALID_ACTION_INDEX;
     54 	}
     55 }
     56 
     57 /*
     58 ========================
     59 idMenuWidget::~idMenuWidget
     60 ========================
     61 */
     62 idMenuWidget::~idMenuWidget() {
     63 	Cleanup();
     64 }
     65 
     66 void idMenuWidget::Cleanup() {
     67 	for ( int j = 0; j < observers.Num(); ++j ) {
     68 		assert( observers[j]->refCount > 0 );
     69 		observers[ j ]->Release();
     70 	}
     71 
     72 	observers.Clear();
     73 
     74 	// free all children
     75 	for ( int i = 0; i < children.Num(); ++i ) {
     76 		assert( children[i]->refCount > 0 );
     77 		children[ i ]->Release();
     78 	}
     79 
     80 	children.Clear();
     81 }
     82 
     83 /*
     84 ========================
     85 idMenuWidget::AddChild
     86 ========================
     87 */
     88 void idMenuWidget::AddChild( idMenuWidget * widget ) {
     89 	if ( !verify( children.Find( widget ) == NULL ) ) {
     90 		return;	// attempt to add a widget that was already in the list
     91 	}
     92 
     93 	if ( widget->GetParent() != NULL ) {
     94 		// take out of previous parent
     95 		widget->GetParent()->RemoveChild( widget );
     96 	}
     97 
     98 	widget->AddRef();
     99 	widget->SetParent( this );
    100 	children.Append( widget );
    101 }
    102 
    103 /*
    104 ========================
    105 idMenuWidget::RemoveAllChildren
    106 ========================
    107 */
    108 void idMenuWidget::RemoveAllChildren() {
    109 	
    110 	for ( int i = 0; i < children.Num(); ++ i ) {
    111 
    112 		assert( children[ i ]->GetParent() == this );
    113 
    114 		children[ i ]->SetParent( NULL );
    115 		children[ i ]->Release();
    116 	}
    117 
    118 	children.Clear();
    119 }
    120 
    121 /*
    122 ========================
    123 idMenuWidget::RemoveChild
    124 ========================
    125 */
    126 void idMenuWidget::RemoveChild( idMenuWidget * widget ) {
    127 	assert( widget->GetParent() == this );
    128 
    129 	children.Remove( widget );
    130 	widget->SetParent( NULL );
    131 	widget->Release();
    132 }
    133 
    134 /*
    135 ========================
    136 idMenuWidget::RemoveChild
    137 ========================
    138 */
    139 bool idMenuWidget::HasChild( idMenuWidget * widget ) {
    140 	for ( int i = 0; i < children.Num(); ++ i ) {
    141 		if ( children[ i ] == widget ) {
    142 			return true;
    143 		}
    144 	}
    145 	return false;
    146 }
    147 
    148 /*
    149 ========================
    150 idMenuWidget::ReceiveEvent
    151 
    152 Events received through this function are passed to the innermost focused widget first, and then
    153 propagates back through each widget within the focus chain.  The first widget that handles the
    154 event will stop propagation.
    155 
    156 Each widget along the way will fire off an event to its observers, whether or not it actually
    157 handles the event.
    158 
    159 Note: How the focus chain is calculated:
    160 Descend through GetFocus() calls until you reach a NULL focus.  The terminating widget is the
    161 innermost widget, while *this* widget is the outermost widget.
    162 ========================
    163 */
    164 void idMenuWidget::ReceiveEvent( const idWidgetEvent & event ) {
    165 	idStaticList< idMenuWidget *, 16 > focusChain;
    166 
    167 	int focusRunawayCounter = focusChain.Max();
    168 	idMenuWidget * focusedWidget = this;
    169 	while ( focusedWidget != NULL && --focusRunawayCounter != 0 ) {
    170 		focusChain.Append( focusedWidget );
    171 		focusedWidget = focusedWidget->GetFocus();
    172 	}
    173 
    174 	// If hitting this then more than likely you have a self-referential chain.  If that's not
    175 	// the case, then you may need to increase the size of the focusChain list.
    176 	assert( focusRunawayCounter != 0 );
    177 	for ( int focusIndex = focusChain.Num() - 1; focusIndex >= 0; --focusIndex ) {
    178 		idMenuWidget * const focusedWidget = focusChain[ focusIndex ];
    179 
    180 		if ( focusedWidget->ExecuteEvent( event ) ) {
    181 			break;	// this widget has handled the event, so stop propagation
    182 		}
    183 	}
    184 }
    185 
    186 
    187 /*
    188 ========================
    189 idMenuWidget::ExecuteEvent
    190 
    191 Handles the event directly, and doesn't pass it through the focus chain.
    192 
    193 This should only be used in very specific circumstances!  Most events should go to the focus.
    194 ========================
    195 */
    196 bool idMenuWidget::ExecuteEvent( const idWidgetEvent & event ) {
    197 	idList< idWidgetAction, TAG_IDLIB_LIST_MENU > * const actions = GetEventActions( event.type );
    198 
    199 	if ( actions != NULL ) {
    200 		for ( int actionIndex = 0; actionIndex < actions->Num(); ++actionIndex ) {
    201 			HandleAction( ( *actions )[ actionIndex ], event, this );
    202 		}
    203 	}
    204 
    205 	SendEventToObservers( event );
    206 
    207 	return actions != NULL && actions->Num() > 0;
    208 }
    209 
    210 /*
    211 ========================
    212 idMenuWidget::SendEventToObservers
    213 
    214 Sends an event to all the observers
    215 ========================
    216 */
    217 void idMenuWidget::SendEventToObservers( const idWidgetEvent & event ) {
    218 	for ( int i = 0; i < observers.Num(); ++i ) {
    219 		observers[ i ]->ObserveEvent( *this, event );
    220 	}
    221 }
    222 
    223 /*
    224 ========================
    225 idMenuWidget::RegisterEventObserver
    226 
    227 Adds an observer to our observers list
    228 ========================
    229 */
    230 void idMenuWidget::RegisterEventObserver( idMenuWidget * observer ) {
    231 	if ( !verify( observers.Find( observer ) == NULL ) ) {
    232 		return;
    233 	}
    234 
    235 	observer->AddRef();
    236 	observers.Append( observer );
    237 }
    238 
    239 /*
    240 ========================
    241 idMenuWidget::SetSpritePath
    242 ========================
    243 */
    244 void idMenuWidget::SetSpritePath( const char * arg1, const char * arg2, const char * arg3, const char * arg4, const char * arg5 ) {
    245 	const char * args[] = { arg1, arg2, arg3, arg4, arg5 };
    246 	const int numArgs = sizeof( args ) / sizeof( args[ 0 ] );
    247 	spritePath.Clear();
    248 	for ( int i = 0; i < numArgs; ++i ) {
    249 		if ( args[ i ] == NULL ) {
    250 			break;
    251 		}
    252 		spritePath.Append( args[ i ] );
    253 	}
    254 }
    255 
    256 /*
    257 ========================
    258 idMenuWidget::SetSpritePath
    259 ========================
    260 */
    261 void idMenuWidget::SetSpritePath( const idList< idStr > & spritePath_, const char * arg1, const char * arg2, const char * arg3, const char * arg4, const char * arg5 ) {
    262 	const char * args[] = { arg1, arg2, arg3, arg4, arg5 };
    263 	const int numArgs = sizeof( args ) / sizeof( args[ 0 ] );
    264 	spritePath = spritePath_;
    265 	for ( int i = 0; i < numArgs; ++i ) {
    266 		if ( args[ i ] == NULL ) {
    267 			break;
    268 		}
    269 		spritePath.Append( args[ i ] );
    270 	}
    271 }
    272 
    273 /*
    274 ========================
    275 idMenuWidget::ClearSprite
    276 ========================
    277 */
    278 void idMenuWidget::ClearSprite() {
    279 	if ( GetSprite() == NULL ) {
    280 		return;
    281 	}
    282 	GetSprite()->SetVisible( false );
    283 	boundSprite = NULL;
    284 }
    285 
    286 /*
    287 ========================
    288 idMenuWidget::GetSWFObject
    289 ========================
    290 */
    291 idSWF * idMenuWidget::GetSWFObject() {
    292 
    293 	if ( swfObj != NULL ) {
    294 		return swfObj;
    295 	}
    296 
    297 	if ( parent != NULL ) {
    298 		return parent->GetSWFObject();
    299 	}
    300 
    301 	if ( menuData != NULL ) {
    302 		return menuData->GetGUI();
    303 	}
    304 
    305 	return NULL;	
    306 }
    307 
    308 /*
    309 ========================
    310 idMenuWidget::GetMenuData
    311 ========================
    312 */
    313 idMenuHandler * idMenuWidget::GetMenuData() {
    314 	if ( parent != NULL ) {
    315 		return parent->GetMenuData();
    316 	}
    317 
    318 	return menuData;
    319 }
    320 
    321 /*
    322 ========================
    323 idMenuWidget::BindSprite
    324 
    325 Takes the sprite path strings and resolves it to an actual sprite relative to a given root.
    326 
    327 This is setup in this manner, because we can't resolve from path -> sprite immediately since
    328 SWFs aren't necessarily loaded at the time widgets are instantiated.
    329 ========================
    330 */
    331 bool idMenuWidget::BindSprite( idSWFScriptObject & root ) {
    332 
    333 	const char * args[ 6 ] = { NULL };
    334 	assert( GetSpritePath().Num() > 0 );
    335 	for ( int i = 0; i < GetSpritePath().Num(); ++i ) {
    336 		args[ i ] = GetSpritePath()[ i ].c_str();
    337 	}
    338 	boundSprite = root.GetNestedSprite( args[ 0 ], args[ 1 ], args[ 2 ], args[ 3 ], args[ 4 ], args[ 5 ] );
    339 	return boundSprite != NULL;
    340 }
    341 
    342 /*
    343 ========================
    344 idMenuWidget::Show
    345 ========================
    346 */
    347 void idMenuWidget::Show() {
    348 	if ( GetSWFObject() == NULL ) {
    349 		return;
    350 	}
    351 
    352 	if ( !BindSprite( GetSWFObject()->GetRootObject() ) ) {
    353 		return;
    354 	}
    355 
    356 	GetSprite()->SetVisible( true );
    357 	int currentFrame = GetSprite()->GetCurrentFrame();
    358 	int findFrame = GetSprite()->FindFrame( "rollOn" );
    359 	int idleFrame = GetSprite()->FindFrame( "idle" );
    360 	if ( currentFrame == findFrame || ( currentFrame > 1 && currentFrame <= idleFrame ) ) {
    361 		return;
    362 	}
    363 
    364 	GetSprite()->PlayFrame( findFrame );
    365 }
    366 
    367 /*
    368 ========================
    369 idMenuWidget::Hide
    370 ========================
    371 */
    372 void idMenuWidget::Hide() {
    373 	if ( GetSWFObject() == NULL ) {
    374 		return;
    375 	}
    376 
    377 	if ( !BindSprite( GetSWFObject()->GetRootObject() ) ) {
    378 		return;
    379 	}
    380 
    381 	int currentFrame = GetSprite()->GetCurrentFrame();
    382 	int findFrame = GetSprite()->FindFrame( "rollOff" );
    383 	if ( currentFrame >= findFrame || currentFrame == 1  ) {
    384 		return;
    385 	}
    386 
    387 	GetSprite()->PlayFrame( findFrame );
    388 }
    389 
    390 /*
    391 ========================
    392 idMenuWidget::SetDataSource
    393 ========================
    394 */
    395 void idMenuWidget::SetDataSource( idMenuDataSource * dataSource_, const int fieldIndex ) {
    396 	dataSource = dataSource_;
    397 	dataSourceFieldIndex = fieldIndex;
    398 }
    399 
    400 /*
    401 ========================
    402 idMenuWidget::SetFocusIndex
    403 ========================
    404 */
    405 void idMenuWidget::SetFocusIndex( const int index, bool skipSound ) {
    406 
    407 	if (  GetChildren().Num() == 0 ) {
    408 		return;
    409 	}
    410 
    411 	const int oldIndex = focusIndex;
    412 
    413 	assert( index >= 0 && index < GetChildren().Num() ); //&& oldIndex >= 0 && oldIndex < GetChildren().Num() );
    414 
    415 	focusIndex = index;
    416 
    417 	if ( oldIndex != focusIndex && !skipSound ) {
    418 		if ( menuData != NULL ) {
    419 			menuData->PlaySound( GUI_SOUND_FOCUS );	
    420 		}
    421 	}
    422 
    423 	idSWFParmList parms;
    424 	parms.Append( oldIndex );
    425 	parms.Append( index );
    426 
    427 	// need to mark the widget as having lost focus
    428 	if ( oldIndex != index && oldIndex >= 0 && oldIndex < GetChildren().Num() && GetChildByIndex( oldIndex ).GetState() != WIDGET_STATE_HIDDEN ) {
    429 		GetChildByIndex( oldIndex ).ReceiveEvent( idWidgetEvent( WIDGET_EVENT_FOCUS_OFF, 0, NULL, parms ) );
    430 	}
    431 
    432 	//assert( GetChildByIndex( index ).GetState() != WIDGET_STATE_HIDDEN );
    433 	GetChildByIndex( index ).ReceiveEvent( idWidgetEvent( WIDGET_EVENT_FOCUS_ON, 0, NULL, parms ) );
    434 }
    435 
    436 /*
    437 ========================
    438 idMenuWidget_Button::SetState
    439 
    440 Transitioning from the current button state to the new button state
    441 ========================
    442 */
    443 void idMenuWidget::SetState( const widgetState_t state ) {
    444 	if ( GetSprite() != NULL ) {
    445 		// FIXME: will need some more intelligence in the transitions to go from, say,
    446 		// selected_up -> up ... but this should work fine for now.
    447 		if ( state == WIDGET_STATE_HIDDEN ) {
    448 			GetSprite()->SetVisible( false );
    449 		} else {
    450 			GetSprite()->SetVisible( true );
    451 			if ( state == WIDGET_STATE_DISABLED ) {
    452 				GetSprite()->PlayFrame( "disabled" );
    453 			} else if ( state == WIDGET_STATE_SELECTING ) {
    454 				if ( widgetState == WIDGET_STATE_NORMAL ) {
    455 					GetSprite()->PlayFrame( "selecting" );	// transition from unselected to selected
    456 				} else {
    457 					GetSprite()->PlayFrame( "sel_up" );
    458 				}
    459 			} else if ( state == WIDGET_STATE_SELECTED ) {
    460 				GetSprite()->PlayFrame( "sel_up" );
    461 			} else if ( state == WIDGET_STATE_NORMAL ) {
    462 				if ( widgetState == WIDGET_STATE_SELECTING ) {
    463 					GetSprite()->PlayFrame( "unselecting" );	// transition from selected to unselected
    464 				} else if ( widgetState != WIDGET_STATE_HIDDEN && widgetState != WIDGET_STATE_NORMAL ) {
    465 					GetSprite()->PlayFrame( "out" );
    466 				} else {
    467 					GetSprite()->PlayFrame( "up" );
    468 				}
    469 			}
    470 		}
    471 
    472 		Update();
    473 	}
    474 
    475 	widgetState = state;
    476 }
    477 
    478 /*
    479 ========================
    480 idMenuWidget::HandleAction
    481 ========================
    482 */
    483 bool idMenuWidget::HandleAction( idWidgetAction & action, const idWidgetEvent & event, idMenuWidget * widget, bool forceHandled ) {
    484 
    485 	bool handled = false;
    486 	if ( GetParent() != NULL ) {
    487 		handled = GetParent()->HandleAction( action, event, widget );
    488 	} else {
    489 		
    490 		if ( forceHandled ) {
    491 			return false;
    492 		}
    493 
    494 		idMenuHandler * data = GetMenuData();
    495 		if ( data != NULL ) {
    496 			return data->HandleAction( action, event, widget, false );
    497 		}
    498 	}	
    499 
    500 	return handled;
    501 }
    502 
    503 /*
    504 ========================
    505 idMenuWidget::GetEventActions
    506 ========================
    507 */
    508 idList< idWidgetAction, TAG_IDLIB_LIST_MENU > * idMenuWidget::GetEventActions( const widgetEvent_t eventType ) {
    509 	if ( eventActionLookup[ eventType ] == INVALID_ACTION_INDEX ) {
    510 		return NULL;
    511 	}
    512 	return &eventActions[ eventActionLookup[ eventType ] ];
    513 }
    514 
    515 /*
    516 ========================
    517 idMenuWidget::AddEventAction
    518 ========================
    519 */
    520 idWidgetAction & idMenuWidget::AddEventAction( const widgetEvent_t eventType ) {
    521 	if ( eventActionLookup[ eventType ] == INVALID_ACTION_INDEX ) {
    522 		eventActionLookup[ eventType ] = eventActions.Num();
    523 		eventActions.Alloc();
    524 	}
    525 	return eventActions[ eventActionLookup[ eventType ] ].Alloc();
    526 }
    527 
    528 /*
    529 ========================
    530 idMenuWidget::ClearEventActions
    531 ========================
    532 */
    533 void idMenuWidget::ClearEventActions() {
    534 	eventActions.Clear();
    535 	eventActionLookup.Clear();
    536 	eventActionLookup.SetNum( eventActionLookup.Max() );
    537 	for ( int i = 0; i < eventActionLookup.Num(); ++i ) {
    538 		eventActionLookup[ i ] = INVALID_ACTION_INDEX;
    539 	}
    540 }
    541 
    542 
    543 
    544 
    545