/* ** Command & Conquer Renegade(tm) ** Copyright 2025 Electronic Arts Inc. ** ** This program is free software: you can redistribute it and/or modify ** it under the terms of the GNU General Public License as published by ** the Free Software Foundation, either version 3 of the License, or ** (at your option) any later version. ** ** This program is distributed in the hope that it will be useful, ** but WITHOUT ANY WARRANTY; without even the implied warranty of ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ** GNU General Public License for more details. ** ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ /*********************************************************************************************** *** 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 *** *********************************************************************************************** * * * Project Name : wwui * * * * $Archive:: /Commando/Code/wwui/textmarqueectrl.cpp $* * * * Author:: Patrick Smith * * * * $Modtime:: 12/17/01 3:32p $* * * * $Revision:: 3 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "textmarqueectrl.h" #include "stylemgr.h" #include "dialogmgr.h" ////////////////////////////////////////////////////////////////////// // // TextMarqueeCtrlClass // ////////////////////////////////////////////////////////////////////// TextMarqueeCtrlClass::TextMarqueeCtrlClass (void) : ScrollPos (0), PixelHeight (0), ScrollRate (2.0F) { // // Configure the renderers // StyleMgrClass::Assign_Font (&TextRenderers[0], StyleMgrClass::FONT_CREDITS); StyleMgrClass::Assign_Font (&TextRenderers[1], StyleMgrClass::FONT_CREDITS_BOLD); StyleMgrClass::Configure_Renderer (&ControlRenderer); TextRenderers[0].Set_Texture_Size_Hint (256); TextRenderers[1].Set_Texture_Size_Hint (256); return ; } ////////////////////////////////////////////////////////////////////// // // ~TextMarqueeCtrlClass // ////////////////////////////////////////////////////////////////////// TextMarqueeCtrlClass::~TextMarqueeCtrlClass (void) { return ; } ////////////////////////////////////////////////////////////////////// // // Create_Control_Renderer // ////////////////////////////////////////////////////////////////////// void TextMarqueeCtrlClass::Create_Control_Renderer (void) { Render2DClass &renderer = ControlRenderer; // // Configure this renderer // renderer.Reset (); renderer.Enable_Texturing (false); // // Determine which color to draw the outline in // int color = StyleMgrClass::Get_Line_Color (); int bkcolor = StyleMgrClass::Get_Bk_Color (); if (IsEnabled == false) { color = StyleMgrClass::Get_Disabled_Line_Color (); bkcolor = StyleMgrClass::Get_Disabled_Bk_Color (); } if (Style & WS_BORDER) { // // Draw the control's outline // renderer.Add_Outline (Rect, 1.0F, color); } return ; } ////////////////////////////////////////////////////////////////////// // // Create_Text_Renderer // ////////////////////////////////////////////////////////////////////// void TextMarqueeCtrlClass::Create_Text_Renderer (void) { TextRenderers[0].Reset (); TextRenderers[1].Reset (); TextRenderers[0].Set_Clipping_Rect (ClientRect); TextRenderers[1].Set_Clipping_Rect (ClientRect); TextRenderers[0].Set_Wrapping_Width (0); TextRenderers[1].Set_Wrapping_Width (0); int row_count = CreditLines.Count (); if (row_count <= 0) { return ; } // // Calculate where to start rendering text // int ctrl_height = ClientRect.Height (); float start_height = (ScrollPos - ctrl_height); float curr_height = 0; for (int curr_row = 0; curr_row < CreditLines.Count (); curr_row ++) { if ((curr_height + CreditLines[curr_row].Height) >= start_height) { break; } curr_height += CreditLines[curr_row].Height; } // // Configure our starting text rectangle // RectClass text_rect = ClientRect; text_rect.Top += curr_height - (ScrollPos - ctrl_height); // // Now, render the visible lines of text... // for ( int index = curr_row; (index < CreditLines.Count ()) && (text_rect.Top < ClientRect.Bottom); index ++) { CREDIT_LINE &line = CreditLines[index]; // // Use the formatting parameters for this line... // Render2DSentenceClass *renderer = &TextRenderers[line.FontIndex]; int color = line.Color; int shadow_color = StyleMgrClass::Get_Text_Shadow_Color (); // // Render this line of text centered // StyleMgrClass::Render_Text (line.Text, renderer, color, shadow_color, text_rect, true, false, StyleMgrClass::CENTER_JUSTIFY, false); // // Move down to the next row... // text_rect.Top = int(text_rect.Top + line.Height); } return ; } //////////////////////////////////////////////////////////////// // // Render // //////////////////////////////////////////////////////////////// void TextMarqueeCtrlClass::Render (void) { // // Recreate the renderers (if necessary) // if (IsDirty) { Create_Control_Renderer (); Create_Text_Renderer (); } // // Render the image... // ControlRenderer.Render (); TextRenderers[0].Render (); TextRenderers[1].Render (); DialogControlClass::Render (); return ; } //////////////////////////////////////////////////////////////// // // Update_Client_Rect // //////////////////////////////////////////////////////////////// void TextMarqueeCtrlClass::Update_Client_Rect (void) { // // Set the client area // ClientRect = Rect; ClientRect.Inflate (Vector2 (-5.0F * StyleMgrClass::Get_X_Scale (), -3.75F * StyleMgrClass::Get_Y_Scale ())); ClientRect.Left = int(ClientRect.Left); ClientRect.Top = int(ClientRect.Top); ClientRect.Right = int(ClientRect.Right); ClientRect.Bottom = int(ClientRect.Bottom); Set_Dirty (); return ; } //////////////////////////////////////////////////////////////// // // Set_Text // //////////////////////////////////////////////////////////////// void TextMarqueeCtrlClass::Set_Text (const WCHAR *title) { DialogControlClass::Set_Text (title); ScrollPos = 0.0F; // // Rebuild the data structures necessary for us to // render formatted text // Build_Credit_Lines (); // // Count up the total height of all lines // for (int index = 0; index < CreditLines.Count (); index ++) { PixelHeight += TextRenderers[CreditLines[index].FontIndex].Peek_Font ()->Get_Char_Height (); } // // Add two blank pages // PixelHeight += ClientRect.Height () * 2; return ; } //////////////////////////////////////////////////////////////// // // Build_Credit_Lines // //////////////////////////////////////////////////////////////// void TextMarqueeCtrlClass::Build_Credit_Lines (void) { CreditLines.Delete_All (); if (Title.Get_Length () == 0) { return; } // // Handy macro // #define COPY_LINE(dest, src_start, src_end) \ if (src_end == NULL) { \ dest = src_start; \ } else { \ uint32 bytes = ((uint32)src_end - (uint32)src_start); \ uint32 len = bytes / sizeof (WCHAR); \ ::memcpy (dest.Get_Buffer (len + 1), src_start, bytes); \ dest.Peek_Buffer ()[len] = 0; \ } int default_color = StyleMgrClass::Get_Text_Color (); CREDIT_LINE default_line (L"", 0, default_color); // // Build an array of formatted text lines // const WCHAR *text = Title; while (text != NULL) { // // Scan this line for formatting information // CREDIT_LINE line (default_line); text = Read_Line (text, line); // // Determine which renderer to use... // Render2DSentenceClass *renderer = &TextRenderers[line.FontIndex]; renderer->Set_Wrapping_Width (ClientRect.Width ()); // // Loop over all the lines of text and check for wrapping... // const WCHAR *line_start = renderer->Find_Row_Start (line.Text, 0); while (line_start != NULL) { // // Lookup the start of the next line... // const WCHAR *line_end = renderer->Find_Row_Start (line_start, 1); // // Copy this line of text into the control // WideStringClass text; COPY_LINE (text, line_start, line_end); // // Add this line to our list // CREDIT_LINE new_line (line); new_line.Text = text; new_line.Height = TextRenderers[new_line.FontIndex].Peek_Font ()->Get_Char_Height (); CreditLines.Add (new_line); // // Advance to the next line... // line_start = line_end; } } return ; } //////////////////////////////////////////////////////////////// // // Read_Tag // //////////////////////////////////////////////////////////////// int TextMarqueeCtrlClass::Read_Tag (const WCHAR *text, CREDIT_LINE &line) { int retval = -1; const WCHAR *TAG_BOLD = L"bold"; const WCHAR *TAG_COLOR = L"color="; if (text[0] == L'<') { for (int index = 1; text[index] != 0; index ++) { // // Is this the 'end-tag' bracket? // if (text[index] == L'>') { // // What type of specifier did we find? // if (::_wcsnicmp (text+1, TAG_BOLD, ::wcslen (TAG_BOLD)) == 0) { // // We found the bold specifier // line.FontIndex = 1; retval = index; } else if (::_wcsnicmp (text+1, TAG_COLOR, ::wcslen (TAG_COLOR)) == 0) { // // We found the color specifier // int tag_len = ::wcslen (TAG_COLOR)+1; text += tag_len; // // Copy the params to their own temp string // WideStringClass temp_buffer (index + 1, true); // ::lstrcpynW (temp_buffer.Peek_Buffer (), text, (index + 1) - tag_len); int length = ((index + 1) - tag_len); WCHAR* tempPtr = temp_buffer.Peek_Buffer(); wcsncpy(tempPtr, text, length); tempPtr[length - 1] = 0; int color[3] = { 0 }; // // Parse the params for the colors // WCHAR *buffer = temp_buffer.Peek_Buffer (); for (int color_index = 0; color_index < 3; color_index ++) { WCHAR *comma_str = ::wcschr (buffer, L','); if (comma_str != NULL) { comma_str[0] = 0; color[color_index] = ::_wtoi (buffer); buffer = &comma_str[1]; } else { color[color_index] = ::_wtoi (buffer); break; } } // // Store the color // line.Color = RGB_TO_INT32 ((uint8)color[0], (uint8)color[1], (uint8)color[2]); retval = index; } break; } } } return retval; } //////////////////////////////////////////////////////////////// // // Read_Line // //////////////////////////////////////////////////////////////// const WCHAR * TextMarqueeCtrlClass::Read_Line (const WCHAR *text, CREDIT_LINE &line) { const WCHAR *text_start = text; // // Set some defaults // line.FontIndex = 0; line.Color = StyleMgrClass::Get_Text_Color (); // // Loop over all the text in the control // bool keep_going = true; do { WCHAR ch = *text; // // Check to see if this character ends the // if (ch == L'\n' || ch == 0) { int len = text - text_start; // ::lstrcpynW (line.Text.Get_Buffer (len + 1), text_start, len + 1); WCHAR* buffer = line.Text.Get_Buffer(len + 1); wcsncpy(buffer, text_start, len + 1); buffer[len] = 0; keep_going = false; } else if (ch == L'<') { // // Did we find a tag? // int new_index = Read_Tag (text, line); if (new_index != -1) { text = &text[new_index]; text_start = text + 1; } } // // Move to the next character // if (ch != 0) { text ++; } } while (keep_going); return text[0] != 0 ? text : 0; } //////////////////////////////////////////////////////////////// // // On_Frame_Update // //////////////////////////////////////////////////////////////// void TextMarqueeCtrlClass::On_Frame_Update (void) { float char_height = TextRenderers[0].Peek_Font ()->Get_Char_Height (); // // Determine how many pixels to scroll // float time = DialogMgrClass::Get_Frame_Time () / 1000.0F; float pixels_scrolled = time * ScrollRate * char_height; // // Update the scroll position // float old_pos = ScrollPos; ScrollPos += pixels_scrolled; if (ScrollPos >= PixelHeight) { ScrollPos = 0; } // // Update the view // if (int(old_pos) != int(ScrollPos)) { Set_Dirty (); } return ; }