dropdownctrl.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783
  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/dropdownctrl.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 1/07/02 10:42a $*
  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 "dropdownctrl.h"
  38. #include "comboboxctrl.h"
  39. #include "dialogbase.h"
  40. #include "assetmgr.h"
  41. #include "refcount.h"
  42. #include "font3d.h"
  43. #include "mousemgr.h"
  44. #include "ww3d.h"
  45. #include "dialogmgr.h"
  46. #include "stylemgr.h"
  47. ////////////////////////////////////////////////////////////////
  48. //
  49. // DropDownCtrlClass
  50. //
  51. ////////////////////////////////////////////////////////////////
  52. DropDownCtrlClass::DropDownCtrlClass (void) :
  53. CellSize (0, 0),
  54. CurrSel (-1),
  55. ComboBox (NULL),
  56. ScrollPos (0),
  57. CountPerPage (0),
  58. FullRect (0, 0, 0, 0),
  59. DisplayScrollBar (false)
  60. {
  61. //
  62. // Set the font for the text renderers
  63. //
  64. StyleMgrClass::Assign_Font (&TextRenderer, StyleMgrClass::FONT_LISTS);
  65. //
  66. // We don't want the scroll bar getting focus
  67. //
  68. ScrollBarCtrl.Set_Wants_Focus (false);
  69. ScrollBarCtrl.Set_Small_BMP_Mode (true);
  70. ScrollBarCtrl.Set_Advise_Sink (this);
  71. return ;
  72. }
  73. ////////////////////////////////////////////////////////////////
  74. //
  75. // ~DropDownCtrlClass
  76. //
  77. ////////////////////////////////////////////////////////////////
  78. DropDownCtrlClass::~DropDownCtrlClass (void)
  79. {
  80. return ;
  81. }
  82. ////////////////////////////////////////////////////////////////
  83. //
  84. // Create_Text_Renderer
  85. //
  86. ////////////////////////////////////////////////////////////////
  87. void
  88. DropDownCtrlClass::Create_Text_Renderer (void)
  89. {
  90. HilightRenderer.Reset ();
  91. HilightRenderer.Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution());
  92. StyleMgrClass::Configure_Hilighter (&HilightRenderer);
  93. //
  94. // Setup the coordinate system of the renderer
  95. //
  96. TextRenderer.Reset ();
  97. //
  98. // Add each string to the list
  99. //
  100. float curr_y_pos = ClientRect.Top;
  101. for (int index = ScrollPos; index < EntryList.Count (); index ++) {
  102. //
  103. // Get the width and height of the current entry
  104. //
  105. const WCHAR *text = EntryList[index].text;
  106. Vector2 text_extent = TextRenderer.Get_Text_Extents (text);
  107. //
  108. // Build a rectangle we'll draw the text into
  109. //
  110. RectClass text_rect;
  111. text_rect.Left = ClientRect.Left;
  112. text_rect.Top = curr_y_pos;
  113. text_rect.Right = text_rect.Left + CellSize.X;
  114. text_rect.Bottom = text_rect.Top + CellSize.Y;
  115. if (text_rect.Bottom <= ClientRect.Bottom) {
  116. //
  117. // Draw the text
  118. //
  119. StyleMgrClass::Render_Text (text, &TextRenderer, text_rect, true, true);
  120. //
  121. // Hilight this entry (if its the currently selected one)
  122. //
  123. if (index == CurrSel) {
  124. StyleMgrClass::Render_Hilight (&HilightRenderer, text_rect);
  125. }
  126. }
  127. //
  128. // Move on to the next y-position
  129. //
  130. curr_y_pos += CellSize.Y;
  131. }
  132. return ;
  133. }
  134. ////////////////////////////////////////////////////////////////
  135. //
  136. // Create_Control_Renderer
  137. //
  138. ////////////////////////////////////////////////////////////////
  139. void
  140. DropDownCtrlClass::Create_Control_Renderer (void)
  141. {
  142. //
  143. // Configure this renderer
  144. //
  145. ControlRenderer.Reset ();
  146. ControlRenderer.Enable_Texturing (false);
  147. ControlRenderer.Set_Coordinate_Range (Render2DClass::Get_Screen_Resolution());
  148. ControlRenderer.Add_Quad (FullRect, RGBA_TO_INT32 (0, 0, 0, 236));
  149. //
  150. // Lookup the colors to use
  151. //
  152. int color = StyleMgrClass::Get_Line_Color ();
  153. int bkcolor = StyleMgrClass::Get_Bk_Color ();
  154. //
  155. // Draw the control outline
  156. //
  157. RectClass rect = Rect;
  158. ControlRenderer.Add_Outline (rect, 1.0F, color);
  159. //
  160. // Now draw the background
  161. //
  162. rect.Right -= 1;
  163. rect.Bottom -= 1;
  164. ControlRenderer.Add_Quad (rect, bkcolor);
  165. return ;
  166. }
  167. ////////////////////////////////////////////////////////////////
  168. //
  169. // On_Set_Cursor
  170. //
  171. ////////////////////////////////////////////////////////////////
  172. void
  173. DropDownCtrlClass::On_Set_Cursor (const Vector2 &mouse_pos)
  174. {
  175. //
  176. // Change the mouse cursor if necessary
  177. //
  178. if (ClientRect.Contains (mouse_pos)) {
  179. MouseMgrClass::Set_Cursor (MouseMgrClass::CURSOR_ACTION);
  180. }
  181. return ;
  182. }
  183. ////////////////////////////////////////////////////////////////
  184. //
  185. // Update_Client_Rect
  186. //
  187. ////////////////////////////////////////////////////////////////
  188. void
  189. DropDownCtrlClass::Update_Client_Rect (void)
  190. {
  191. Rect = FullRect;
  192. //
  193. // Determine what one character spacing would be
  194. //
  195. Vector2 char_size = TextRenderer.Get_Text_Extents (L"W");
  196. float border_width = char_size.X + 2;
  197. float border_height = 2;
  198. //
  199. // Shrink the client area
  200. //
  201. ClientRect = Rect;
  202. ClientRect.Inflate (Vector2 (-border_width, -border_height));
  203. //
  204. // Calculate how each "text" cell should be
  205. //
  206. CellSize.X = int(ClientRect.Width ());
  207. CellSize.Y = int(char_size.Y * 1.5F);
  208. //
  209. // Update the number of entries we can display at one time
  210. //
  211. CountPerPage = int(ClientRect.Height () / CellSize.Y);
  212. //
  213. // Do we need to show a scroll bar?
  214. //
  215. if (CountPerPage < EntryList.Count ()) {
  216. float width = (char_size.X * 3);
  217. //
  218. // Calculate the width of the scroll bar
  219. //
  220. RectClass scroll_rect;
  221. scroll_rect.Left = Rect.Right - width;
  222. scroll_rect.Top = Rect.Top;
  223. scroll_rect.Right = Rect.Right;
  224. scroll_rect.Bottom = Rect.Bottom;
  225. //
  226. // Size the scroll bar
  227. //
  228. ScrollBarCtrl.Set_Window_Rect (scroll_rect);
  229. ScrollBarCtrl.Set_Page_Size (CountPerPage - 1);
  230. ScrollBarCtrl.Set_Range (0, EntryList.Count () - CountPerPage);
  231. DisplayScrollBar = true;
  232. //
  233. // Shrink the client area
  234. //
  235. float new_width = ScrollBarCtrl.Get_Window_Rect ().Left;
  236. ClientRect.Right = new_width;
  237. Rect.Right = new_width;
  238. CellSize.X = int(ClientRect.Width ());
  239. }
  240. Set_Dirty ();
  241. return ;
  242. }
  243. ////////////////////////////////////////////////////////////////
  244. //
  245. // Render
  246. //
  247. ////////////////////////////////////////////////////////////////
  248. void
  249. DropDownCtrlClass::Render (void)
  250. {
  251. //
  252. // Recreate the renderers (if necessary)
  253. //
  254. if (IsDirty) {
  255. Create_Control_Renderer ();
  256. Create_Text_Renderer ();
  257. }
  258. //
  259. // Render the background and text for the current state
  260. //
  261. ControlRenderer.Render ();
  262. TextRenderer.Render ();
  263. HilightRenderer.Render ();
  264. DialogControlClass::Render ();
  265. return ;
  266. }
  267. ////////////////////////////////////////////////////////////////
  268. //
  269. // On_LButton_Down
  270. //
  271. ////////////////////////////////////////////////////////////////
  272. void
  273. DropDownCtrlClass::On_LButton_Down (const Vector2 &mouse_pos)
  274. {
  275. Set_Curr_Sel (Entry_From_Pos (mouse_pos));
  276. return ;
  277. }
  278. ////////////////////////////////////////////////////////////////
  279. //
  280. // On_LButton_Up
  281. //
  282. ////////////////////////////////////////////////////////////////
  283. void
  284. DropDownCtrlClass::On_LButton_Up (const Vector2 &mouse_pos)
  285. {
  286. Set_Curr_Sel (Entry_From_Pos (mouse_pos));
  287. if (ComboBox) {
  288. ComboBox->On_Drop_Down_End (CurrSel);
  289. }
  290. return ;
  291. }
  292. ////////////////////////////////////////////////////////////////
  293. //
  294. // On_Mouse_Move
  295. //
  296. ////////////////////////////////////////////////////////////////
  297. void
  298. DropDownCtrlClass::On_Mouse_Move (const Vector2 &mouse_pos)
  299. {
  300. if (DialogMgrClass::Is_Button_Down (VK_LBUTTON)) {
  301. Set_Curr_Sel (Entry_From_Pos (mouse_pos));
  302. }
  303. return ;
  304. }
  305. ////////////////////////////////////////////////////////////////
  306. //
  307. // On_Key_Down
  308. //
  309. ////////////////////////////////////////////////////////////////
  310. bool
  311. DropDownCtrlClass::On_Key_Down (uint32 key_id, uint32 key_data)
  312. {
  313. bool handled = true;
  314. switch (key_id)
  315. {
  316. case VK_HOME:
  317. Set_Curr_Sel (0);
  318. break;
  319. case VK_END:
  320. Set_Curr_Sel (EntryList.Count () - 1);
  321. break;
  322. case VK_UP:
  323. case VK_LEFT:
  324. Set_Curr_Sel (CurrSel - 1);
  325. break;
  326. case VK_DOWN:
  327. case VK_RIGHT:
  328. Set_Curr_Sel (CurrSel + 1);
  329. break;
  330. default:
  331. handled = false;
  332. break;
  333. }
  334. return handled;
  335. }
  336. ////////////////////////////////////////////////////////////////
  337. //
  338. // On_Create
  339. //
  340. ////////////////////////////////////////////////////////////////
  341. void
  342. DropDownCtrlClass::On_Create (void)
  343. {
  344. return ;
  345. }
  346. ////////////////////////////////////////////////////////////////
  347. //
  348. // On_Kill_Focus
  349. //
  350. ////////////////////////////////////////////////////////////////
  351. void
  352. DropDownCtrlClass::On_Kill_Focus (DialogControlClass *focus)
  353. {
  354. //
  355. // Noitify the combobox that we are done
  356. //
  357. if (ComboBox) {
  358. ComboBox->On_Drop_Down_End (CurrSel);
  359. }
  360. return ;
  361. }
  362. ////////////////////////////////////////////////////////////////
  363. //
  364. // Add_String
  365. //
  366. ////////////////////////////////////////////////////////////////
  367. int
  368. DropDownCtrlClass::Add_String (const WCHAR* string)
  369. {
  370. if (string == NULL) {
  371. return -1;
  372. }
  373. //
  374. // Add the entry to the list
  375. //
  376. EntryList.Add (ENTRY (string, 0));
  377. //
  378. // Force scrollbars, etc to be recalculated and painted
  379. //
  380. Update_Client_Rect ();
  381. Set_Dirty ();
  382. return (EntryList.Count () - 1);
  383. }
  384. ////////////////////////////////////////////////////////////////
  385. //
  386. // Delete_String
  387. //
  388. ////////////////////////////////////////////////////////////////
  389. void
  390. DropDownCtrlClass::Delete_String (int index)
  391. {
  392. if (index >= 0 && index < EntryList.Count ()) {
  393. //
  394. // Delete the entry
  395. //
  396. EntryList.Delete (index);
  397. Set_Curr_Sel (min (CurrSel, EntryList.Count () - 1));
  398. Set_Dirty ();
  399. }
  400. return ;
  401. }
  402. ////////////////////////////////////////////////////////////////
  403. //
  404. // Find_String
  405. //
  406. ////////////////////////////////////////////////////////////////
  407. int
  408. DropDownCtrlClass::Find_String (const WCHAR* string)
  409. {
  410. int retval = -1;
  411. if (string) {
  412. for (int index = 0; index < EntryList.Count (); index ++) {
  413. //
  414. // If this is the entry the user is requesting, then
  415. // return its index to the caller
  416. //
  417. if (EntryList[index].text.Compare_No_Case (string) == 0) {
  418. retval = index;
  419. break;
  420. }
  421. }
  422. }
  423. return retval;
  424. }
  425. int DropDownCtrlClass::Find_Closest_String(const WCHAR* string)
  426. {
  427. int retval = -1;
  428. if (string && (wcslen(string) > 0)) {
  429. for (int index = 0; index < EntryList.Count (); index ++) {
  430. int relation = EntryList[index].text.Compare_No_Case(string);
  431. if (relation == 0) {
  432. return index;
  433. } else if (relation < 0) {
  434. return index;
  435. }
  436. }
  437. }
  438. return retval;
  439. }
  440. ////////////////////////////////////////////////////////////////
  441. //
  442. // Set_Item_Data
  443. //
  444. ////////////////////////////////////////////////////////////////
  445. void
  446. DropDownCtrlClass::Set_Item_Data (int index, uint32 data)
  447. {
  448. //
  449. // Index into the list and set the user data
  450. //
  451. if (index >= 0 && index < EntryList.Count ()) {
  452. EntryList[index].user_data = data;
  453. }
  454. return ;
  455. }
  456. ////////////////////////////////////////////////////////////////
  457. //
  458. // Get_Item_Data
  459. //
  460. ////////////////////////////////////////////////////////////////
  461. uint32
  462. DropDownCtrlClass::Get_Item_Data (int index)
  463. {
  464. uint32 retval = 0;
  465. //
  466. // Index into the list and return the user data to the caller
  467. //
  468. if (index >= 0 && index < EntryList.Count ()) {
  469. retval = EntryList[index].user_data;
  470. }
  471. return retval;
  472. }
  473. ////////////////////////////////////////////////////////////////
  474. //
  475. // Reset_Content
  476. //
  477. ////////////////////////////////////////////////////////////////
  478. void
  479. DropDownCtrlClass::Reset_Content (void)
  480. {
  481. //
  482. // Remove everything from the list
  483. //
  484. EntryList.Delete_All ();
  485. Set_Curr_Sel (0);
  486. //
  487. // Repaint the control
  488. //
  489. Set_Dirty ();
  490. return ;
  491. }
  492. ////////////////////////////////////////////////////////////////
  493. //
  494. // Entry_From_Pos
  495. //
  496. ////////////////////////////////////////////////////////////////
  497. int
  498. DropDownCtrlClass::Entry_From_Pos (const Vector2 &mouse_pos)
  499. {
  500. int retval = 0;
  501. //
  502. // Loop over all the entries in our current view
  503. //
  504. float curr_y_pos = ClientRect.Top;
  505. for (int index = ScrollPos; index < EntryList.Count (); index ++) {
  506. retval = index;
  507. //
  508. // Is ths mouse over this entry?
  509. //
  510. if ( (mouse_pos.Y >= curr_y_pos &&
  511. mouse_pos.Y <= (curr_y_pos + CellSize.Y)) ||
  512. mouse_pos.Y > ClientRect.Bottom)
  513. {
  514. break;
  515. }
  516. //
  517. // Move on to the next y-position
  518. //
  519. curr_y_pos += CellSize.Y;
  520. }
  521. return retval;
  522. }
  523. ////////////////////////////////////////////////////////////////
  524. //
  525. // Set_Curr_Sel
  526. //
  527. ////////////////////////////////////////////////////////////////
  528. void
  529. DropDownCtrlClass::Set_Curr_Sel (int index)
  530. {
  531. if (index >= -1 && index < EntryList.Count () && CurrSel != index) {
  532. //
  533. // Change the selection
  534. //
  535. CurrSel = index;
  536. //
  537. // Update our current scroll position
  538. //
  539. Update_Scroll_Pos ();
  540. //
  541. // Repaint the view
  542. //
  543. Set_Dirty ();
  544. }
  545. return ;
  546. }
  547. ////////////////////////////////////////////////////////////////
  548. //
  549. // Get_String
  550. //
  551. ////////////////////////////////////////////////////////////////
  552. bool
  553. DropDownCtrlClass::Get_String (int index, WideStringClass &string) const
  554. {
  555. bool retval = false;
  556. if (index >= 0 && index < EntryList.Count ()) {
  557. //
  558. // Index into the entry list and return the string
  559. //
  560. string = EntryList[index].text;
  561. retval = true;
  562. }
  563. return retval;
  564. }
  565. ////////////////////////////////////////////////////////////////
  566. //
  567. // Get_String
  568. //
  569. ////////////////////////////////////////////////////////////////
  570. const WCHAR *
  571. DropDownCtrlClass::Get_String (int index) const
  572. {
  573. const WCHAR *retval = NULL;
  574. if (index >= 0 && index < EntryList.Count ()) {
  575. //
  576. // Index into the entry list and return the string
  577. //
  578. retval = EntryList[index].text;
  579. }
  580. return retval;
  581. }
  582. ////////////////////////////////////////////////////////////////
  583. //
  584. // Update_Scroll_Pos
  585. //
  586. ////////////////////////////////////////////////////////////////
  587. void
  588. DropDownCtrlClass::Update_Scroll_Pos (void)
  589. {
  590. if (CurrSel == -1) {
  591. return;
  592. }
  593. if (CurrSel < ScrollPos) {
  594. //
  595. // Scroll up so the current selection is in view
  596. //
  597. ScrollPos = CurrSel;
  598. Set_Dirty ();
  599. } else if (CurrSel >= ScrollPos + CountPerPage) {
  600. //
  601. // Scroll down so the current selection is in view
  602. //
  603. ScrollPos = max (CurrSel - (CountPerPage - 1), 0);
  604. Set_Dirty ();
  605. }
  606. //
  607. // Update the scrollbar
  608. //
  609. ScrollBarCtrl.Set_Pos (ScrollPos, false);
  610. return ;
  611. }
  612. ////////////////////////////////////////////////////////////////
  613. //
  614. // On_Add_To_Dialog
  615. //
  616. ////////////////////////////////////////////////////////////////
  617. void
  618. DropDownCtrlClass::On_Add_To_Dialog (void)
  619. {
  620. if (DisplayScrollBar) {
  621. Parent->Add_Control (&ScrollBarCtrl);
  622. }
  623. TextColor.Set (0.35F, 1.0F, 0.35F);
  624. return ;
  625. }
  626. ////////////////////////////////////////////////////////////////
  627. //
  628. // On_Remove_From_Dialog
  629. //
  630. ////////////////////////////////////////////////////////////////
  631. void
  632. DropDownCtrlClass::On_Remove_From_Dialog (void)
  633. {
  634. if (DisplayScrollBar) {
  635. Parent->Remove_Control (&ScrollBarCtrl);
  636. }
  637. return ;
  638. }
  639. ////////////////////////////////////////////////////////////////
  640. //
  641. // On_VScroll
  642. //
  643. ////////////////////////////////////////////////////////////////
  644. void
  645. DropDownCtrlClass::On_VScroll (ScrollBarCtrlClass *, int , int new_position)
  646. {
  647. ScrollPos = new_position;
  648. Set_Dirty ();
  649. return ;
  650. }