textmarqueectrl.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531
  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 : wwui *
  23. * *
  24. * $Archive:: /Commando/Code/wwui/textmarqueectrl.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 12/17/01 3:32p $*
  29. * *
  30. * $Revision:: 3 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "textmarqueectrl.h"
  36. #include "stylemgr.h"
  37. #include "dialogmgr.h"
  38. //////////////////////////////////////////////////////////////////////
  39. //
  40. // TextMarqueeCtrlClass
  41. //
  42. //////////////////////////////////////////////////////////////////////
  43. TextMarqueeCtrlClass::TextMarqueeCtrlClass (void) :
  44. ScrollPos (0),
  45. PixelHeight (0),
  46. ScrollRate (2.0F)
  47. {
  48. //
  49. // Configure the renderers
  50. //
  51. StyleMgrClass::Assign_Font (&TextRenderers[0], StyleMgrClass::FONT_CREDITS);
  52. StyleMgrClass::Assign_Font (&TextRenderers[1], StyleMgrClass::FONT_CREDITS_BOLD);
  53. StyleMgrClass::Configure_Renderer (&ControlRenderer);
  54. TextRenderers[0].Set_Texture_Size_Hint (256);
  55. TextRenderers[1].Set_Texture_Size_Hint (256);
  56. return ;
  57. }
  58. //////////////////////////////////////////////////////////////////////
  59. //
  60. // ~TextMarqueeCtrlClass
  61. //
  62. //////////////////////////////////////////////////////////////////////
  63. TextMarqueeCtrlClass::~TextMarqueeCtrlClass (void)
  64. {
  65. return ;
  66. }
  67. //////////////////////////////////////////////////////////////////////
  68. //
  69. // Create_Control_Renderer
  70. //
  71. //////////////////////////////////////////////////////////////////////
  72. void
  73. TextMarqueeCtrlClass::Create_Control_Renderer (void)
  74. {
  75. Render2DClass &renderer = ControlRenderer;
  76. //
  77. // Configure this renderer
  78. //
  79. renderer.Reset ();
  80. renderer.Enable_Texturing (false);
  81. //
  82. // Determine which color to draw the outline in
  83. //
  84. int color = StyleMgrClass::Get_Line_Color ();
  85. int bkcolor = StyleMgrClass::Get_Bk_Color ();
  86. if (IsEnabled == false) {
  87. color = StyleMgrClass::Get_Disabled_Line_Color ();
  88. bkcolor = StyleMgrClass::Get_Disabled_Bk_Color ();
  89. }
  90. if (Style & WS_BORDER) {
  91. //
  92. // Draw the control's outline
  93. //
  94. renderer.Add_Outline (Rect, 1.0F, color);
  95. }
  96. return ;
  97. }
  98. //////////////////////////////////////////////////////////////////////
  99. //
  100. // Create_Text_Renderer
  101. //
  102. //////////////////////////////////////////////////////////////////////
  103. void
  104. TextMarqueeCtrlClass::Create_Text_Renderer (void)
  105. {
  106. TextRenderers[0].Reset ();
  107. TextRenderers[1].Reset ();
  108. TextRenderers[0].Set_Clipping_Rect (ClientRect);
  109. TextRenderers[1].Set_Clipping_Rect (ClientRect);
  110. TextRenderers[0].Set_Wrapping_Width (0);
  111. TextRenderers[1].Set_Wrapping_Width (0);
  112. int row_count = CreditLines.Count ();
  113. if (row_count <= 0) {
  114. return ;
  115. }
  116. //
  117. // Calculate where to start rendering text
  118. //
  119. int ctrl_height = ClientRect.Height ();
  120. float start_height = (ScrollPos - ctrl_height);
  121. float curr_height = 0;
  122. for (int curr_row = 0; curr_row < CreditLines.Count (); curr_row ++) {
  123. if ((curr_height + CreditLines[curr_row].Height) >= start_height) {
  124. break;
  125. }
  126. curr_height += CreditLines[curr_row].Height;
  127. }
  128. //
  129. // Configure our starting text rectangle
  130. //
  131. RectClass text_rect = ClientRect;
  132. text_rect.Top += curr_height - (ScrollPos - ctrl_height);
  133. //
  134. // Now, render the visible lines of text...
  135. //
  136. for ( int index = curr_row;
  137. (index < CreditLines.Count ()) && (text_rect.Top < ClientRect.Bottom);
  138. index ++)
  139. {
  140. CREDIT_LINE &line = CreditLines[index];
  141. //
  142. // Use the formatting parameters for this line...
  143. //
  144. Render2DSentenceClass *renderer = &TextRenderers[line.FontIndex];
  145. int color = line.Color;
  146. int shadow_color = StyleMgrClass::Get_Text_Shadow_Color ();
  147. //
  148. // Render this line of text centered
  149. //
  150. StyleMgrClass::Render_Text (line.Text, renderer, color, shadow_color,
  151. text_rect, true, false, StyleMgrClass::CENTER_JUSTIFY, false);
  152. //
  153. // Move down to the next row...
  154. //
  155. text_rect.Top = int(text_rect.Top + line.Height);
  156. }
  157. return ;
  158. }
  159. ////////////////////////////////////////////////////////////////
  160. //
  161. // Render
  162. //
  163. ////////////////////////////////////////////////////////////////
  164. void
  165. TextMarqueeCtrlClass::Render (void)
  166. {
  167. //
  168. // Recreate the renderers (if necessary)
  169. //
  170. if (IsDirty) {
  171. Create_Control_Renderer ();
  172. Create_Text_Renderer ();
  173. }
  174. //
  175. // Render the image...
  176. //
  177. ControlRenderer.Render ();
  178. TextRenderers[0].Render ();
  179. TextRenderers[1].Render ();
  180. DialogControlClass::Render ();
  181. return ;
  182. }
  183. ////////////////////////////////////////////////////////////////
  184. //
  185. // Update_Client_Rect
  186. //
  187. ////////////////////////////////////////////////////////////////
  188. void
  189. TextMarqueeCtrlClass::Update_Client_Rect (void)
  190. {
  191. //
  192. // Set the client area
  193. //
  194. ClientRect = Rect;
  195. ClientRect.Inflate (Vector2 (-5.0F * StyleMgrClass::Get_X_Scale (), -3.75F * StyleMgrClass::Get_Y_Scale ()));
  196. ClientRect.Left = int(ClientRect.Left);
  197. ClientRect.Top = int(ClientRect.Top);
  198. ClientRect.Right = int(ClientRect.Right);
  199. ClientRect.Bottom = int(ClientRect.Bottom);
  200. Set_Dirty ();
  201. return ;
  202. }
  203. ////////////////////////////////////////////////////////////////
  204. //
  205. // Set_Text
  206. //
  207. ////////////////////////////////////////////////////////////////
  208. void
  209. TextMarqueeCtrlClass::Set_Text (const WCHAR *title)
  210. {
  211. DialogControlClass::Set_Text (title);
  212. ScrollPos = 0.0F;
  213. //
  214. // Rebuild the data structures necessary for us to
  215. // render formatted text
  216. //
  217. Build_Credit_Lines ();
  218. //
  219. // Count up the total height of all lines
  220. //
  221. for (int index = 0; index < CreditLines.Count (); index ++) {
  222. PixelHeight += TextRenderers[CreditLines[index].FontIndex].Peek_Font ()->Get_Char_Height ();
  223. }
  224. //
  225. // Add two blank pages
  226. //
  227. PixelHeight += ClientRect.Height () * 2;
  228. return ;
  229. }
  230. ////////////////////////////////////////////////////////////////
  231. //
  232. // Build_Credit_Lines
  233. //
  234. ////////////////////////////////////////////////////////////////
  235. void
  236. TextMarqueeCtrlClass::Build_Credit_Lines (void)
  237. {
  238. CreditLines.Delete_All ();
  239. if (Title.Get_Length () == 0) {
  240. return;
  241. }
  242. //
  243. // Handy macro
  244. //
  245. #define COPY_LINE(dest, src_start, src_end) \
  246. if (src_end == NULL) { \
  247. dest = src_start; \
  248. } else { \
  249. uint32 bytes = ((uint32)src_end - (uint32)src_start); \
  250. uint32 len = bytes / sizeof (WCHAR); \
  251. ::memcpy (dest.Get_Buffer (len + 1), src_start, bytes); \
  252. dest.Peek_Buffer ()[len] = 0; \
  253. }
  254. int default_color = StyleMgrClass::Get_Text_Color ();
  255. CREDIT_LINE default_line (L"", 0, default_color);
  256. //
  257. // Build an array of formatted text lines
  258. //
  259. const WCHAR *text = Title;
  260. while (text != NULL) {
  261. //
  262. // Scan this line for formatting information
  263. //
  264. CREDIT_LINE line (default_line);
  265. text = Read_Line (text, line);
  266. //
  267. // Determine which renderer to use...
  268. //
  269. Render2DSentenceClass *renderer = &TextRenderers[line.FontIndex];
  270. renderer->Set_Wrapping_Width (ClientRect.Width ());
  271. //
  272. // Loop over all the lines of text and check for wrapping...
  273. //
  274. const WCHAR *line_start = renderer->Find_Row_Start (line.Text, 0);
  275. while (line_start != NULL) {
  276. //
  277. // Lookup the start of the next line...
  278. //
  279. const WCHAR *line_end = renderer->Find_Row_Start (line_start, 1);
  280. //
  281. // Copy this line of text into the control
  282. //
  283. WideStringClass text;
  284. COPY_LINE (text, line_start, line_end);
  285. //
  286. // Add this line to our list
  287. //
  288. CREDIT_LINE new_line (line);
  289. new_line.Text = text;
  290. new_line.Height = TextRenderers[new_line.FontIndex].Peek_Font ()->Get_Char_Height ();
  291. CreditLines.Add (new_line);
  292. //
  293. // Advance to the next line...
  294. //
  295. line_start = line_end;
  296. }
  297. }
  298. return ;
  299. }
  300. ////////////////////////////////////////////////////////////////
  301. //
  302. // Read_Tag
  303. //
  304. ////////////////////////////////////////////////////////////////
  305. int
  306. TextMarqueeCtrlClass::Read_Tag (const WCHAR *text, CREDIT_LINE &line)
  307. {
  308. int retval = -1;
  309. const WCHAR *TAG_BOLD = L"bold";
  310. const WCHAR *TAG_COLOR = L"color=";
  311. if (text[0] == L'<') {
  312. for (int index = 1; text[index] != 0; index ++) {
  313. //
  314. // Is this the 'end-tag' bracket?
  315. //
  316. if (text[index] == L'>') {
  317. //
  318. // What type of specifier did we find?
  319. //
  320. if (::_wcsnicmp (text+1, TAG_BOLD, ::wcslen (TAG_BOLD)) == 0) {
  321. //
  322. // We found the bold specifier
  323. //
  324. line.FontIndex = 1;
  325. retval = index;
  326. } else if (::_wcsnicmp (text+1, TAG_COLOR, ::wcslen (TAG_COLOR)) == 0) {
  327. //
  328. // We found the color specifier
  329. //
  330. int tag_len = ::wcslen (TAG_COLOR)+1;
  331. text += tag_len;
  332. //
  333. // Copy the params to their own temp string
  334. //
  335. WideStringClass temp_buffer (index + 1, true);
  336. // ::lstrcpynW (temp_buffer.Peek_Buffer (), text, (index + 1) - tag_len);
  337. int length = ((index + 1) - tag_len);
  338. WCHAR* tempPtr = temp_buffer.Peek_Buffer();
  339. wcsncpy(tempPtr, text, length);
  340. tempPtr[length - 1] = 0;
  341. int color[3] = { 0 };
  342. //
  343. // Parse the params for the colors
  344. //
  345. WCHAR *buffer = temp_buffer.Peek_Buffer ();
  346. for (int color_index = 0; color_index < 3; color_index ++) {
  347. WCHAR *comma_str = ::wcschr (buffer, L',');
  348. if (comma_str != NULL) {
  349. comma_str[0] = 0;
  350. color[color_index] = ::_wtoi (buffer);
  351. buffer = &comma_str[1];
  352. } else {
  353. color[color_index] = ::_wtoi (buffer);
  354. break;
  355. }
  356. }
  357. //
  358. // Store the color
  359. //
  360. line.Color = RGB_TO_INT32 ((uint8)color[0], (uint8)color[1], (uint8)color[2]);
  361. retval = index;
  362. }
  363. break;
  364. }
  365. }
  366. }
  367. return retval;
  368. }
  369. ////////////////////////////////////////////////////////////////
  370. //
  371. // Read_Line
  372. //
  373. ////////////////////////////////////////////////////////////////
  374. const WCHAR *
  375. TextMarqueeCtrlClass::Read_Line (const WCHAR *text, CREDIT_LINE &line)
  376. {
  377. const WCHAR *text_start = text;
  378. //
  379. // Set some defaults
  380. //
  381. line.FontIndex = 0;
  382. line.Color = StyleMgrClass::Get_Text_Color ();
  383. //
  384. // Loop over all the text in the control
  385. //
  386. bool keep_going = true;
  387. do
  388. {
  389. WCHAR ch = *text;
  390. //
  391. // Check to see if this character ends the
  392. //
  393. if (ch == L'\n' || ch == 0) {
  394. int len = text - text_start;
  395. // ::lstrcpynW (line.Text.Get_Buffer (len + 1), text_start, len + 1);
  396. WCHAR* buffer = line.Text.Get_Buffer(len + 1);
  397. wcsncpy(buffer, text_start, len + 1);
  398. buffer[len] = 0;
  399. keep_going = false;
  400. } else if (ch == L'<') {
  401. //
  402. // Did we find a tag?
  403. //
  404. int new_index = Read_Tag (text, line);
  405. if (new_index != -1) {
  406. text = &text[new_index];
  407. text_start = text + 1;
  408. }
  409. }
  410. //
  411. // Move to the next character
  412. //
  413. if (ch != 0) {
  414. text ++;
  415. }
  416. } while (keep_going);
  417. return text[0] != 0 ? text : 0;
  418. }
  419. ////////////////////////////////////////////////////////////////
  420. //
  421. // On_Frame_Update
  422. //
  423. ////////////////////////////////////////////////////////////////
  424. void
  425. TextMarqueeCtrlClass::On_Frame_Update (void)
  426. {
  427. float char_height = TextRenderers[0].Peek_Font ()->Get_Char_Height ();
  428. //
  429. // Determine how many pixels to scroll
  430. //
  431. float time = DialogMgrClass::Get_Frame_Time () / 1000.0F;
  432. float pixels_scrolled = time * ScrollRate * char_height;
  433. //
  434. // Update the scroll position
  435. //
  436. float old_pos = ScrollPos;
  437. ScrollPos += pixels_scrolled;
  438. if (ScrollPos >= PixelHeight) {
  439. ScrollPos = 0;
  440. }
  441. //
  442. // Update the view
  443. //
  444. if (int(old_pos) != int(ScrollPos)) {
  445. Set_Dirty ();
  446. }
  447. return ;
  448. }