MenuWidget.cpp 14 KB

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