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