tabctrl.cpp 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program 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. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. /***********************************************************************************************
  19. *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : Combat *
  23. * *
  24. * $Archive:: /Commando/Code/wwui/tabctrl.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 9/19/01 11:30a $*
  29. * *
  30. * $Revision:: 17 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. // Disable warning about exception handling not being enabled. It's used as part of STL - in a part of STL we don't use.
  36. #pragma warning(disable : 4530)
  37. #include "tabctrl.h"
  38. #include "assetmgr.h"
  39. #include "refcount.h"
  40. #include "mousemgr.h"
  41. #include "dialogmgr.h"
  42. #include "dialogbase.h"
  43. #include "stylemgr.h"
  44. #include "texture.h"
  45. #include "ww3d.h"
  46. #include "font3d.h"
  47. #include "childdialog.h"
  48. ////////////////////////////////////////////////////////////////
  49. // Local constants
  50. ////////////////////////////////////////////////////////////////
  51. static const RectClass BarUVs (1, 1, 26, 255);
  52. static const RectClass SelUVs (28, 1, 254, 69);
  53. static const RectClass UnfocusUVs (28, 236, 42, 250);
  54. static const RectClass FocusUVs (43, 236, 57, 250);
  55. static const RectClass BarTopUVs (1, 1, 26, 79);
  56. static const RectClass BarBottomUVs (1, 169, 26, 255);
  57. static const RectClass BarTileUVs (1, 80, 26, 168);
  58. static const float BUBBLE_OFFSET_X = 23;
  59. static const float BUBBLE_OFFSET_Y = 25;
  60. static const float BAR_OFFSET = 25;
  61. static const float TEXT_OFFSET = 60;
  62. static const float SEL_TEXT_OFFSET = 75;
  63. static const float TEXT_RADIUS = 40;
  64. static const float ANGLE_MIN = 115;
  65. static const float ANGLE_MAX = 245;
  66. static const float BLINK_DELAY = 500;
  67. static const float PAGE_AREA_OFFSET = 245;
  68. static const float PIXELS_PER_SEC = 500;
  69. ////////////////////////////////////////////////////////////////
  70. //
  71. // TabCtrlClass
  72. //
  73. ////////////////////////////////////////////////////////////////
  74. TabCtrlClass::TabCtrlClass (void) :
  75. CurrTabIndex (0),
  76. NextBlinkTime (0),
  77. SelectorPos (0),
  78. SelRect (0, 0, 0, 0),
  79. ScaleX (0),
  80. ScaleY (0),
  81. IsBubbleDisplayed (false)
  82. {
  83. //
  84. // Determine what scale to use
  85. //
  86. ScaleX = Render2DClass::Get_Screen_Resolution().Width () / 800;
  87. ScaleY = Render2DClass::Get_Screen_Resolution().Height () / 600;
  88. //
  89. // Assign the font to the text renderers
  90. //
  91. StyleMgrClass::Assign_Font (&TextRenderer, StyleMgrClass::FONT_HEADER);
  92. StyleMgrClass::Assign_Font (&GlowRenderer, StyleMgrClass::FONT_HEADER);
  93. StyleMgrClass::Assign_Font (&HilightRenderer, StyleMgrClass::FONT_BIG_HEADER);
  94. StyleMgrClass::Assign_Font (&HilightGlowRenderer, StyleMgrClass::FONT_BIG_HEADER);
  95. //
  96. // Configure the renderers
  97. //
  98. StyleMgrClass::Configure_Renderer (&ControlRenderer);
  99. //
  100. // Load the texture for the control
  101. //
  102. TextureClass *texture = WW3DAssetManager::Get_Instance ()->Get_Texture ("IF_MENUPARTS9.TGA", TextureClass::MIP_LEVELS_1);
  103. ControlRenderer.Set_Texture (texture);
  104. REF_PTR_RELEASE (texture);
  105. return ;
  106. }
  107. ////////////////////////////////////////////////////////////////
  108. //
  109. // ~TabCtrlClass
  110. //
  111. ////////////////////////////////////////////////////////////////
  112. TabCtrlClass::~TabCtrlClass (void)
  113. {
  114. Free_Tabs ();
  115. return ;
  116. }
  117. ////////////////////////////////////////////////////////////////
  118. //
  119. // Create_Text_Renderer
  120. //
  121. ////////////////////////////////////////////////////////////////
  122. void
  123. TabCtrlClass::Create_Text_Renderer (void)
  124. {
  125. //
  126. // Reset the renderers
  127. //
  128. GlowRenderer.Reset ();
  129. TextRenderer.Reset ();
  130. HilightRenderer.Reset ();
  131. HilightGlowRenderer.Reset ();
  132. float text_left = ClientRect.Left + (TEXT_OFFSET * ScaleX);
  133. float height = ClientRect.Height ();
  134. float tab_height = height / max (TabList.Count (), 1);
  135. //
  136. // Setup some variables we can use to simulate a semi-circle for the text
  137. //
  138. float angle_min = DEG_TO_RADF (ANGLE_MIN);
  139. float angle_max = DEG_TO_RADF (ANGLE_MAX);
  140. float radius = TEXT_RADIUS * ScaleX;
  141. //
  142. // Render the tab names
  143. //
  144. float y_pos = ClientRect.Top;
  145. for (int index = 0; index < TabList.Count (); index ++) {
  146. //
  147. // Calculate a "semi-circular" x-offset for the text
  148. //
  149. float angle = angle_min + ((angle_max - angle_min) * ((float)index / float(TabList.Count()-1)));
  150. float x_offset = WWMath::Cos (angle) * radius;
  151. //
  152. // Calculate what rectangle to render to
  153. //
  154. RectClass text_rect;
  155. text_rect.Left = (int)(text_left + (radius + x_offset));
  156. text_rect.Top = (int)y_pos;
  157. text_rect.Right = (int)ClientRect.Right;
  158. text_rect.Bottom = int(y_pos + tab_height);
  159. //
  160. // Get the title from the child dialog
  161. //
  162. WideStringClass title;
  163. TabList[index]->Get_Title (&title);
  164. //
  165. // Is this the selected tab?
  166. //
  167. if (index == CurrTabIndex) {
  168. //
  169. // Move the hilighted text in a bit
  170. //
  171. text_rect.Left = SelRect.Left + (SEL_TEXT_OFFSET * ScaleX);
  172. //
  173. // Render the hilighted text and its glow
  174. //
  175. HilightRenderer.Build_Sentence (title);
  176. HilightGlowRenderer.Build_Sentence (title);
  177. //StyleMgrClass::Render_Text (title, &HilightRenderer, text_rect, true, true);
  178. StyleMgrClass::Render_Text (title, &HilightRenderer,
  179. StyleMgrClass::Get_Tab_Text_Color (),
  180. StyleMgrClass::Get_Text_Shadow_Color (),
  181. text_rect, true, true);
  182. /*HilightRenderer.Set_Clipping_Rect (text_rect);
  183. //
  184. // Get the extents of the text we will be drawing
  185. //
  186. Vector2 text_extent = HilightRenderer.Get_Text_Extents (title);
  187. //
  188. // Assume left justification
  189. //
  190. int x_pos = text_rect.Left + 1;
  191. int y_pos = int(text_rect.Top + (text_rect.Height () / 2) - (text_extent.Y / 2));
  192. //
  193. // Construct the textures needed to render the text
  194. //
  195. HilightRenderer.Build_Sentence (title);
  196. HilightRenderer.Set_Location (Vector2 (x_pos - 1, y_pos - 1));
  197. HilightRenderer.Draw_Sentence (RGB_TO_INT32 (255, 255, 255));
  198. HilightRenderer.Set_Location (Vector2 (x_pos + 1, y_pos + 1));
  199. HilightRenderer.Draw_Sentence (RGB_TO_INT32 (0, 0, 0));
  200. //
  201. // Render the text
  202. //
  203. HilightRenderer.Set_Location (Vector2 (x_pos, y_pos));
  204. HilightRenderer.Draw_Sentence (RGB_TO_INT32 (147, 155, 153));*/
  205. StyleMgrClass::Render_Glow (title, &HilightGlowRenderer, text_rect, 7, 7,
  206. StyleMgrClass::Get_Tab_Glow_Color ());
  207. } else {
  208. //
  209. // Render the text and its glow
  210. //
  211. TextRenderer.Build_Sentence (title);
  212. GlowRenderer.Build_Sentence (title);
  213. StyleMgrClass::Render_Text (title, &TextRenderer,
  214. StyleMgrClass::Get_Tab_Text_Color (),
  215. StyleMgrClass::Get_Text_Shadow_Color (),
  216. text_rect, true, true);
  217. /*TextRenderer.Set_Clipping_Rect (text_rect);
  218. //
  219. // Get the extents of the text we will be drawing
  220. //
  221. Vector2 text_extent = TextRenderer.Get_Text_Extents (title);
  222. //
  223. // Assume left justification
  224. //
  225. int x_pos = text_rect.Left + 1;
  226. int y_pos = int(text_rect.Top + (text_rect.Height () / 2) - (text_extent.Y / 2));
  227. //
  228. // Construct the textures needed to render the text
  229. //
  230. TextRenderer.Build_Sentence (title);
  231. TextRenderer.Set_Location (Vector2 (x_pos - 1, y_pos - 1));
  232. TextRenderer.Draw_Sentence (RGB_TO_INT32 (255, 255, 255));
  233. TextRenderer.Set_Location (Vector2 (x_pos + 1, y_pos + 1));
  234. TextRenderer.Draw_Sentence (RGB_TO_INT32 (0, 0, 0));
  235. //
  236. // Render the text
  237. //
  238. TextRenderer.Set_Location (Vector2 (x_pos, y_pos));
  239. TextRenderer.Draw_Sentence (RGB_TO_INT32 (147, 155, 153));*/
  240. StyleMgrClass::Render_Glow (title, &GlowRenderer, text_rect, 4, 4,
  241. StyleMgrClass::Get_Tab_Glow_Color ());
  242. }
  243. //
  244. // Move down to the next tab
  245. //
  246. y_pos += tab_height;
  247. }
  248. return ;
  249. }
  250. ////////////////////////////////////////////////////////////////
  251. //
  252. // Create_Control_Renderer
  253. //
  254. ////////////////////////////////////////////////////////////////
  255. void
  256. TabCtrlClass::Create_Control_Renderer (void)
  257. {
  258. Render2DClass &renderer = ControlRenderer;
  259. renderer.Reset ();
  260. //
  261. // Determine which UVs to use for the bubble
  262. //
  263. RectClass bubble_uvs = UnfocusUVs;
  264. if (IsBubbleDisplayed) {
  265. bubble_uvs = FocusUVs;
  266. }
  267. //
  268. // Scale the dimensions of the components
  269. //
  270. float bar_tile_height = BarTileUVs.Height () * ScaleY;
  271. float bar_tip_height = BarTopUVs.Height () * ScaleY;
  272. float bar_width = BarTileUVs.Width () * ScaleX;
  273. float sel_width = SelUVs.Width () * ScaleX;
  274. float sel_height = SelUVs.Height () * ScaleY;
  275. float bubble_width = bubble_uvs.Width () * ScaleX;
  276. float bubble_height = bubble_uvs.Height () * ScaleX;
  277. //
  278. // Setup some variables we can use to simulate a semi-circle for the text
  279. //
  280. float angle_min = DEG_TO_RADF (ANGLE_MIN);
  281. float angle_max = DEG_TO_RADF (ANGLE_MAX);
  282. float radius = TEXT_RADIUS * ScaleX;
  283. float min_y = Pos_From_Tab (0);
  284. float max_y = Pos_From_Tab (TabList.Count () - 1);
  285. float percent = (SelectorPos - min_y) / (max_y - min_y);
  286. float angle = angle_min + percent * (angle_max - angle_min);
  287. //float left_pos = -radius;
  288. float x_offset = radius + (WWMath::Cos (angle) * radius);
  289. //
  290. // Calculate the screen rectangle for the bar
  291. //
  292. RectClass bar_rect;
  293. bar_rect.Left = int(ClientRect.Left + x_offset + (BAR_OFFSET * ScaleX) - (bar_width / 2));
  294. bar_rect.Right = int(bar_rect.Left + bar_width);
  295. bar_rect.Top = int(ClientRect.Top - (x_offset * 1.5F));
  296. bar_rect.Bottom = int(ClientRect.Bottom + (x_offset * 1.5F));
  297. //
  298. // Calculate the screen rectangle for the selector
  299. //
  300. SelRect.Left = int(ClientRect.Left + x_offset);
  301. SelRect.Right = int(SelRect.Left + sel_width);
  302. SelRect.Top = int(SelectorPos - (sel_height / 2));
  303. SelRect.Bottom = int(SelRect.Top + sel_height);
  304. //
  305. // Calculate the screen rectangle for the bubble
  306. //
  307. RectClass bubble_rect;
  308. bubble_rect.Left = int(SelRect.Left + BUBBLE_OFFSET_X * ScaleX);
  309. bubble_rect.Right = int(bubble_rect.Left + bubble_width);
  310. bubble_rect.Top = int(SelRect.Top + BUBBLE_OFFSET_Y * ScaleY);
  311. bubble_rect.Bottom = int(bubble_rect.Top + bubble_height);
  312. //
  313. // Normalize the UVs
  314. //
  315. RectClass sel_uvs = SelUVs;
  316. RectClass bar_top_uvs = BarTopUVs;
  317. RectClass bar_bottom_uvs = BarBottomUVs;
  318. sel_uvs.Inverse_Scale (Vector2 (256, 256));
  319. bar_top_uvs.Inverse_Scale (Vector2 (256, 256));
  320. bar_bottom_uvs.Inverse_Scale (Vector2 (256, 256));
  321. bubble_uvs.Inverse_Scale (Vector2 (256, 256));
  322. //
  323. // Render the bar top and bottom
  324. //
  325. RectClass bar_top_rect;
  326. bar_top_rect.Left = int(bar_rect.Left);
  327. bar_top_rect.Right = int(bar_rect.Right);
  328. bar_top_rect.Top = int(bar_rect.Top);
  329. bar_top_rect.Bottom = int(bar_top_rect.Top + bar_tip_height);
  330. RectClass bar_bottom_rect;
  331. bar_bottom_rect.Left = int(bar_rect.Left);
  332. bar_bottom_rect.Right = int(bar_rect.Right);
  333. bar_bottom_rect.Top = int(bar_rect.Bottom - bar_tip_height);
  334. bar_bottom_rect.Bottom = int(bar_rect.Bottom);
  335. renderer.Add_Quad (bar_top_rect, bar_top_uvs);
  336. renderer.Add_Quad (bar_bottom_rect, bar_bottom_uvs);
  337. //
  338. // Tile the remaining pieces
  339. //
  340. float y_pos = bar_top_rect.Bottom;
  341. float total_height = bar_bottom_rect.Top - y_pos;
  342. while (total_height > 0) {
  343. float height = min (bar_tile_height, total_height);
  344. //
  345. // Setup the UV rectangle
  346. //
  347. RectClass uvs = BarTileUVs;
  348. uvs.Bottom = uvs.Top + height;
  349. uvs.Inverse_Scale (Vector2 (256, 256));
  350. //
  351. // Setup the screen rectangle
  352. //
  353. RectClass bar_tile_rect = bar_rect;
  354. bar_tile_rect.Top = y_pos;
  355. bar_tile_rect.Bottom = y_pos + height;
  356. //
  357. // Render the section
  358. //
  359. renderer.Add_Quad (bar_tile_rect, uvs);
  360. y_pos += height;
  361. total_height -= height;
  362. }
  363. //
  364. // Render the bar and selector
  365. //
  366. renderer.Add_Quad (SelRect, sel_uvs);
  367. renderer.Add_Quad (bubble_rect, bubble_uvs);
  368. return ;
  369. }
  370. ////////////////////////////////////////////////////////////////
  371. //
  372. // On_Set_Cursor
  373. //
  374. ////////////////////////////////////////////////////////////////
  375. void
  376. TabCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
  377. {
  378. //
  379. // Change the mouse cursor
  380. //
  381. if (mouse_pos.X < (ClientRect.Left + PAGE_AREA_OFFSET * ScaleX)) {
  382. MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ACTION);
  383. } else {
  384. MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ARROW);
  385. }
  386. return ;
  387. }
  388. ////////////////////////////////////////////////////////////////
  389. //
  390. // Update_Client_Rect
  391. //
  392. ////////////////////////////////////////////////////////////////
  393. void
  394. TabCtrlClass::Update_Client_Rect (void)
  395. {
  396. //
  397. // Set the client area
  398. //
  399. ClientRect = Rect;
  400. Set_Dirty ();
  401. //
  402. // Resize each child dialog so it fits in the new rectangle
  403. //
  404. for (int index = 0; index < TabList.Count (); index ++) {
  405. RectClass rect;
  406. rect.Left = (int)(ClientRect.Left + PAGE_AREA_OFFSET * ScaleX);
  407. rect.Top = (int)ClientRect.Top;
  408. rect.Right = (int)ClientRect.Right;
  409. rect.Bottom = (int)ClientRect.Bottom;
  410. TabList[index]->Set_Rect (rect);
  411. }
  412. return ;
  413. }
  414. ////////////////////////////////////////////////////////////////
  415. //
  416. // Render
  417. //
  418. ////////////////////////////////////////////////////////////////
  419. void
  420. TabCtrlClass::Render (void)
  421. {
  422. Update_Selector ();
  423. Update_Bubble ();
  424. //
  425. // Recreate the renderers (if necessary)
  426. //
  427. if (IsDirty) {
  428. Create_Control_Renderer ();
  429. Create_Text_Renderer ();
  430. }
  431. //
  432. // Render the background and text
  433. //
  434. GlowRenderer.Render ();
  435. HilightGlowRenderer.Render ();
  436. TextRenderer.Render ();
  437. HilightRenderer.Render ();
  438. ControlRenderer.Render ();
  439. DialogControlClass::Render ();
  440. return ;
  441. }
  442. ////////////////////////////////////////////////////////////////
  443. //
  444. // On_LButton_Down
  445. //
  446. ////////////////////////////////////////////////////////////////
  447. void
  448. TabCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
  449. {
  450. //
  451. // Change tabs
  452. //
  453. if (mouse_pos.X < (ClientRect.Left + PAGE_AREA_OFFSET * ScaleX)) {
  454. Set_Curr_Tab (Tab_From_Pos (mouse_pos));
  455. }
  456. return ;
  457. }
  458. ////////////////////////////////////////////////////////////////
  459. //
  460. // On_LButton_Up
  461. //
  462. ////////////////////////////////////////////////////////////////
  463. void
  464. TabCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
  465. {
  466. return ;
  467. }
  468. ////////////////////////////////////////////////////////////////
  469. //
  470. // On_Mouse_Move
  471. //
  472. ////////////////////////////////////////////////////////////////
  473. void
  474. TabCtrlClass::On_Mouse_Move (const Vector2 &mouse_pos)
  475. {
  476. return ;
  477. }
  478. ////////////////////////////////////////////////////////////////
  479. //
  480. // On_Set_Focus
  481. //
  482. ////////////////////////////////////////////////////////////////
  483. void
  484. TabCtrlClass::On_Set_Focus (void)
  485. {
  486. Set_Dirty ();
  487. DialogControlClass::On_Set_Focus ();
  488. return ;
  489. }
  490. ////////////////////////////////////////////////////////////////
  491. //
  492. // On_Kill_Focus
  493. //
  494. ////////////////////////////////////////////////////////////////
  495. void
  496. TabCtrlClass::On_Kill_Focus (DialogControlClass *focus)
  497. {
  498. Set_Dirty ();
  499. DialogControlClass::On_Kill_Focus (focus);
  500. return ;
  501. }
  502. ////////////////////////////////////////////////////////////////
  503. //
  504. // On_Key_Down
  505. //
  506. ////////////////////////////////////////////////////////////////
  507. bool
  508. TabCtrlClass::On_Key_Down (uint32 key_id, uint32 key_data)
  509. {
  510. bool handled = true;
  511. switch (key_id)
  512. {
  513. case VK_UP:
  514. case VK_LEFT:
  515. Set_Curr_Tab (CurrTabIndex - 1);
  516. break;
  517. case VK_DOWN:
  518. case VK_RIGHT:
  519. Set_Curr_Tab (CurrTabIndex + 1);
  520. break;
  521. case VK_HOME:
  522. Set_Curr_Tab (0);
  523. break;
  524. case VK_END:
  525. Set_Curr_Tab (TabList.Count () - 1);
  526. break;
  527. default:
  528. handled = false;
  529. break;
  530. }
  531. return handled;
  532. }
  533. ////////////////////////////////////////////////////////////////
  534. //
  535. // On_Create
  536. //
  537. ////////////////////////////////////////////////////////////////
  538. void
  539. TabCtrlClass::On_Create (void)
  540. {
  541. return ;
  542. }
  543. ////////////////////////////////////////////////////////////////
  544. //
  545. // Tab_From_Pos
  546. //
  547. ////////////////////////////////////////////////////////////////
  548. int
  549. TabCtrlClass::Tab_From_Pos (const Vector2 &mouse_pos)
  550. {
  551. int retval = -1;
  552. float height = ClientRect.Height ();
  553. float tab_height = height / max (TabList.Count (), 1);
  554. //
  555. // Check each of the tabs
  556. //
  557. float y_pos = ClientRect.Top;
  558. for (int index = 0; index < TabList.Count (); index ++) {
  559. //
  560. // Is the mouse over this tab?
  561. //
  562. if (mouse_pos.Y >= y_pos && mouse_pos.Y <= (y_pos + tab_height)) {
  563. retval = index;
  564. break;
  565. }
  566. //
  567. // Move down to the next tab
  568. //
  569. y_pos += tab_height;
  570. }
  571. return retval;
  572. }
  573. ////////////////////////////////////////////////////////////////
  574. //
  575. // Pos_From_Tab
  576. //
  577. ////////////////////////////////////////////////////////////////
  578. float
  579. TabCtrlClass::Pos_From_Tab (int index)
  580. {
  581. float height = ClientRect.Height ();
  582. float tab_height = height / max (TabList.Count (), 1);
  583. //
  584. // Return the centered y-position
  585. //
  586. return ClientRect.Top + (tab_height * (index + 1)) - (tab_height / 2);
  587. }
  588. ////////////////////////////////////////////////////////////////
  589. //
  590. // Set_Curr_Tab
  591. //
  592. ////////////////////////////////////////////////////////////////
  593. void
  594. TabCtrlClass::Set_Curr_Tab (int index)
  595. {
  596. //
  597. // Bound the index
  598. //
  599. index = min (TabList.Count () - 1, index);
  600. index = max (0, index);
  601. //
  602. // Did we change tabs?
  603. //
  604. if (CurrTabIndex != index) {
  605. //
  606. // Switch tabs visibly
  607. //
  608. TabList[CurrTabIndex]->Show (false);
  609. TabList[index]->Show (true);
  610. //
  611. // Change the tab and force a repaint
  612. //
  613. CurrTabIndex = index;
  614. Set_Dirty ();
  615. }
  616. return ;
  617. }
  618. ////////////////////////////////////////////////////////////////
  619. //
  620. // Update_Bubble
  621. //
  622. ////////////////////////////////////////////////////////////////
  623. void
  624. TabCtrlClass::Update_Bubble (void)
  625. {
  626. if (HasFocus) {
  627. //
  628. // Is it time to blink the bubble light?
  629. //
  630. int curr_time = DialogMgrClass::Get_Time ();
  631. if (curr_time > NextBlinkTime) {
  632. //
  633. // Toggle the bubble light
  634. //
  635. IsBubbleDisplayed = !IsBubbleDisplayed;
  636. NextBlinkTime = curr_time + BLINK_DELAY;
  637. //
  638. // Force a repaint
  639. //
  640. Set_Dirty ();
  641. }
  642. } else {
  643. IsBubbleDisplayed = false;
  644. }
  645. return ;
  646. }
  647. ////////////////////////////////////////////////////////////////
  648. //
  649. // Update_Selector
  650. //
  651. ////////////////////////////////////////////////////////////////
  652. void
  653. TabCtrlClass::Update_Selector (void)
  654. {
  655. //
  656. // Exit early if there's nothing to do
  657. //
  658. float end_pos = Pos_From_Tab (CurrTabIndex);
  659. if (SelectorPos == end_pos) {
  660. return ;
  661. }
  662. //
  663. // Calculate the rate the selector moves
  664. //
  665. float rate = PIXELS_PER_SEC * ScaleY;
  666. //
  667. // Calculate how far we need to move
  668. //
  669. float time = DialogMgrClass::Get_Frame_Time () / 1000.0F;
  670. float dist = rate * time;
  671. //
  672. // Move the selector
  673. //
  674. if (SelectorPos < end_pos) {
  675. SelectorPos += dist;
  676. SelectorPos = min (end_pos, SelectorPos);
  677. Set_Dirty ();
  678. } else if (SelectorPos > end_pos) {
  679. SelectorPos -= dist;
  680. SelectorPos = max (end_pos, SelectorPos);
  681. Set_Dirty ();
  682. }
  683. //
  684. // Bound the selector position
  685. //
  686. SelectorPos = min (Pos_From_Tab (TabList.Count () - 1), SelectorPos);
  687. SelectorPos = max (Pos_From_Tab (0), SelectorPos);
  688. return ;
  689. }
  690. ////////////////////////////////////////////////////////////////
  691. //
  692. // Add_Tab
  693. //
  694. ////////////////////////////////////////////////////////////////
  695. void
  696. TabCtrlClass::Add_Tab (ChildDialogClass *dialog)
  697. {
  698. if (dialog == NULL) {
  699. return ;
  700. }
  701. //
  702. // Start the dialog
  703. //
  704. dialog->Show (TabList.Count () == 0);
  705. dialog->Start_Dialog ();
  706. Parent->Add_Child_Dialog (dialog);
  707. //
  708. // Size the child so it fits perfectly in the
  709. // page area.
  710. //
  711. RectClass rect;
  712. rect.Left = (int)(ClientRect.Left + PAGE_AREA_OFFSET * ScaleX);
  713. rect.Top = (int)ClientRect.Top;
  714. rect.Right = (int)ClientRect.Right;
  715. rect.Bottom = (int)ClientRect.Bottom;
  716. dialog->Set_Rect (rect);
  717. //
  718. // Add this dialog to our list
  719. //
  720. dialog->Add_Ref ();
  721. TabList.Add (dialog);
  722. return ;
  723. }
  724. ////////////////////////////////////////////////////////////////
  725. //
  726. // Remove_Tab
  727. //
  728. ////////////////////////////////////////////////////////////////
  729. void
  730. TabCtrlClass::Remove_Tab (int index)
  731. {
  732. if (index < 0 || index >= TabList.Count ()) {
  733. return ;
  734. }
  735. //
  736. // Remove this dialog from our list
  737. //
  738. Parent->Remove_Child_Dialog (TabList[index]);
  739. TabList[index]->End_Dialog ();
  740. REF_PTR_RELEASE (TabList[index]);
  741. TabList.Delete (index);
  742. return ;
  743. }
  744. ////////////////////////////////////////////////////////////////
  745. //
  746. // Free_Tabs
  747. //
  748. ////////////////////////////////////////////////////////////////
  749. void
  750. TabCtrlClass::Free_Tabs (void)
  751. {
  752. //
  753. // Release our hold on each tab
  754. //
  755. for (int index = 0; index < TabList.Count (); index ++) {
  756. REF_PTR_RELEASE (TabList[index]);
  757. }
  758. TabList.Delete_All ();
  759. return ;
  760. }
  761. ////////////////////////////////////////////////////////////////
  762. //
  763. // On_Mouse_Wheel
  764. //
  765. ////////////////////////////////////////////////////////////////
  766. void
  767. TabCtrlClass::On_Mouse_Wheel (int direction)
  768. {
  769. if (direction < 0) {
  770. Set_Curr_Tab (CurrTabIndex - 1);
  771. } else {
  772. Set_Curr_Tab (CurrTabIndex + 1);
  773. }
  774. return ;
  775. }
  776. ////////////////////////////////////////////////////////////////
  777. //
  778. // Apply_Changes_On_Tabs
  779. //
  780. ////////////////////////////////////////////////////////////////
  781. bool
  782. TabCtrlClass::Apply_Changes_On_Tabs (void)
  783. {
  784. bool retval = true;
  785. //
  786. // Get each tab to apply their changes
  787. //
  788. for (int index = 0; retval && index < TabList.Count (); index ++) {
  789. retval &= TabList[index]->On_Apply ();
  790. }
  791. return retval;
  792. }
  793. ////////////////////////////////////////////////////////////////
  794. //
  795. // Discard_Changes_On_Tabs
  796. //
  797. ////////////////////////////////////////////////////////////////
  798. bool
  799. TabCtrlClass::Discard_Changes_On_Tabs (void)
  800. {
  801. bool retval = true;
  802. //
  803. // Get each tab to discard their changes
  804. //
  805. for (int index = 0; retval && index < TabList.Count (); index ++) {
  806. retval &= TabList[index]->On_Discard ();
  807. }
  808. return retval;
  809. }
  810. ////////////////////////////////////////////////////////////////
  811. //
  812. // Reload_Tabs
  813. //
  814. ////////////////////////////////////////////////////////////////
  815. void
  816. TabCtrlClass::Reload_Tabs (void)
  817. {
  818. //
  819. // Get each tab to discard their changes
  820. //
  821. for (int index = 0; index < TabList.Count (); index ++) {
  822. TabList[index]->On_Reload ();
  823. }
  824. return ;
  825. }