Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1027 lines
25 KiB

  1. //========= Copyright (c) 1996-2005, Valve Corporation, All rights reserved. ============//
  2. //
  3. // Purpose: Implementation of vgui::TextImage control
  4. //
  5. // $NoKeywords: $
  6. //=============================================================================//
  7. #include <string.h>
  8. #include <stdio.h>
  9. #include <ctype.h>
  10. #ifdef _PS3
  11. #include <wctype.h>
  12. #endif
  13. #include <assert.h>
  14. #include <vgui/IPanel.h>
  15. #include <vgui/ISurface.h>
  16. #include <vgui/IScheme.h>
  17. #include <vgui/IInput.h>
  18. #include <vgui/ILocalize.h>
  19. #include <keyvalues.h>
  20. #include <vgui_controls/TextImage.h>
  21. #include <vgui_controls/Controls.h>
  22. #include "tier0/dbg.h"
  23. // memdbgon must be the last include file in a .cpp file!!!
  24. #include <tier0/memdbgon.h>
  25. // enable this define if you want unlocalized strings logged to files unfound.txt and unlocalized.txt
  26. // #define LOG_UNLOCALIZED_STRINGS
  27. using namespace vgui;
  28. //-----------------------------------------------------------------------------
  29. // Purpose: Constructor
  30. //-----------------------------------------------------------------------------
  31. TextImage::TextImage(const char *text) : Image()
  32. {
  33. _utext = NULL;
  34. _textBufferLen = 0;
  35. _font = INVALID_FONT;
  36. _fallbackFont = INVALID_FONT;
  37. _unlocalizedTextSymbol = INVALID_STRING_INDEX;
  38. _drawWidth = 0;
  39. _textBufferLen = 0;
  40. _textLen = 0;
  41. m_bNoShortcutSyntax = false;
  42. m_bWrap = false;
  43. m_bWrapCenter = false;
  44. m_LineBreaks.RemoveAll();
  45. m_LineXIndent.RemoveAll();
  46. m_pwszEllipsesPosition = NULL;
  47. m_bUseFallbackFont = false;
  48. m_bRenderUsingFallbackFont = false;
  49. m_bAllCaps = false;
  50. m_bUseAsianWordWrapping = false;
  51. m_bRecalculateTruncation = true;
  52. SetText(text); // set the text.
  53. }
  54. //-----------------------------------------------------------------------------
  55. // Purpose: Constructor
  56. //-----------------------------------------------------------------------------
  57. TextImage::TextImage(const wchar_t *wszText) : Image()
  58. {
  59. _utext = NULL;
  60. _textBufferLen = 0;
  61. _font = INVALID_FONT;
  62. _fallbackFont = INVALID_FONT;
  63. _unlocalizedTextSymbol = INVALID_STRING_INDEX;
  64. _drawWidth = 0;
  65. _textBufferLen = 0;
  66. _textLen = 0;
  67. m_bNoShortcutSyntax = false;
  68. m_bWrap = false;
  69. m_bWrapCenter = false;
  70. m_LineBreaks.RemoveAll();
  71. m_LineXIndent.RemoveAll();
  72. m_bUseFallbackFont = false;
  73. m_bRenderUsingFallbackFont = false;
  74. m_bAllCaps = false;
  75. m_bUseAsianWordWrapping = false;
  76. m_bRecalculateTruncation = true;
  77. SetText(wszText); // set the text.
  78. }
  79. //-----------------------------------------------------------------------------
  80. // Purpose: Destructor
  81. //-----------------------------------------------------------------------------
  82. TextImage::~TextImage()
  83. {
  84. delete [] _utext;
  85. }
  86. //-----------------------------------------------------------------------------
  87. // Purpose: takes the string and looks it up in the localization file to convert it to unicode
  88. //-----------------------------------------------------------------------------
  89. void TextImage::SetText(const char *text)
  90. {
  91. if (!text)
  92. {
  93. text = "";
  94. }
  95. // check for localization
  96. if (*text == '#')
  97. {
  98. // try lookup in localization tables
  99. _unlocalizedTextSymbol = g_pVGuiLocalize->FindIndex(text + 1);
  100. if (_unlocalizedTextSymbol != INVALID_STRING_INDEX)
  101. {
  102. wchar_t *unicode = g_pVGuiLocalize->GetValueByIndex(_unlocalizedTextSymbol);
  103. SetText(unicode);
  104. return;
  105. }
  106. else
  107. {
  108. // could not find string
  109. // debug code for logging unlocalized strings
  110. #if defined(LOG_UNLOCALIZED_STRINGS)
  111. if (*text)
  112. {
  113. // write out error to unfound.txt log file
  114. static bool first = true;
  115. FILE *f;
  116. if (first)
  117. {
  118. first = false;
  119. f = fopen("unfound.txt", "wt");
  120. }
  121. else
  122. {
  123. f = fopen("unfound.txt", "at");
  124. }
  125. if (f)
  126. {
  127. fprintf(f, "\"%s\"\n", text);
  128. fclose(f);
  129. }
  130. }
  131. #endif // LOG_UNLOCALIZED_STRINGS
  132. }
  133. }
  134. else
  135. {
  136. // debug code for logging unlocalized strings
  137. #if defined(LOG_UNLOCALIZED_STRINGS)
  138. if (text[0])
  139. {
  140. // setting a label to be ANSI text, write out error to unlocalized.txt log file
  141. static bool first = true;
  142. FILE *f;
  143. if (first)
  144. {
  145. first = false;
  146. f = fopen("unlocalized.txt", "wt");
  147. }
  148. else
  149. {
  150. f = fopen("unlocalized.txt", "at");
  151. }
  152. if (f)
  153. {
  154. fprintf(f, "\"%s\"\n", text);
  155. fclose(f);
  156. }
  157. }
  158. #endif // LOG_UNLOCALIZED_STRINGS
  159. }
  160. // convert the ansi string to unicode and use that
  161. wchar_t unicode[1024];
  162. g_pVGuiLocalize->ConvertANSIToUnicode(text, unicode, sizeof(unicode));
  163. SetText(unicode);
  164. }
  165. //-----------------------------------------------------------------------------
  166. // Purpose: Sets the width that the text can be.
  167. //-----------------------------------------------------------------------------
  168. void TextImage::SetDrawWidth(int width)
  169. {
  170. _drawWidth = width;
  171. m_bRecalculateTruncation = true;
  172. }
  173. //-----------------------------------------------------------------------------
  174. // Purpose: Gets the width that the text can be.
  175. //-----------------------------------------------------------------------------
  176. void TextImage::GetDrawWidth(int &width)
  177. {
  178. width = _drawWidth;
  179. }
  180. //-----------------------------------------------------------------------------
  181. // Purpose: sets unicode text directly
  182. //-----------------------------------------------------------------------------
  183. void TextImage::SetText(const wchar_t *unicode, bool bClearUnlocalizedSymbol)
  184. {
  185. if ( bClearUnlocalizedSymbol )
  186. {
  187. // Clear out unlocalized text symbol so that changing dialog variables
  188. // doesn't stomp over the custom unicode string we're being set to.
  189. _unlocalizedTextSymbol = INVALID_STRING_INDEX;
  190. }
  191. if (!unicode)
  192. {
  193. unicode = L"";
  194. }
  195. // reallocate the buffer if necessary
  196. _textLen = (short)wcslen(unicode);
  197. if (_textLen >= _textBufferLen)
  198. {
  199. delete [] _utext;
  200. _textBufferLen = (short)(_textLen + 1);
  201. _utext = new wchar_t[_textBufferLen];
  202. }
  203. m_LineBreaks.RemoveAll();
  204. m_LineXIndent.RemoveAll();
  205. // store the text as unicode
  206. wcscpy(_utext, unicode);
  207. m_bRecalculateTruncation = true;
  208. }
  209. //-----------------------------------------------------------------------------
  210. // Purpose: Gets the text in the textImage
  211. //-----------------------------------------------------------------------------
  212. void TextImage::GetText(char *buffer, int bufferSize)
  213. {
  214. g_pVGuiLocalize->ConvertUnicodeToANSI(_utext, buffer, bufferSize);
  215. if ( m_bAllCaps )
  216. {
  217. // Uppercase all the letters
  218. for ( int i = Q_strlen( buffer ); i >= 0; --i )
  219. {
  220. buffer[ i ] = toupper( buffer[ i ] );
  221. }
  222. }
  223. }
  224. //-----------------------------------------------------------------------------
  225. // Purpose: Gets the text in the textImage
  226. //-----------------------------------------------------------------------------
  227. void TextImage::GetText(wchar_t *buffer, int bufLenInBytes)
  228. {
  229. V_wcsncpy(buffer, _utext, bufLenInBytes );
  230. if ( m_bAllCaps )
  231. {
  232. // Uppercase all the letters
  233. for ( int i = Q_wcslen( buffer ) - 1; i >= 0; --i )
  234. {
  235. buffer[ i ] = towupper( buffer[ i ] );
  236. }
  237. }
  238. }
  239. //-----------------------------------------------------------------------------
  240. // Purpose: data accessor
  241. //-----------------------------------------------------------------------------
  242. void TextImage::GetUnlocalizedText(char *buffer, int bufferSize)
  243. {
  244. if (_unlocalizedTextSymbol != INVALID_STRING_INDEX)
  245. {
  246. const char *text = g_pVGuiLocalize->GetNameByIndex(_unlocalizedTextSymbol);
  247. buffer[0] = '#';
  248. Q_strncpy(buffer + 1, text, bufferSize - 1);
  249. buffer[bufferSize-1] = 0;
  250. }
  251. else
  252. {
  253. GetText(buffer, bufferSize);
  254. }
  255. }
  256. //-----------------------------------------------------------------------------
  257. // Purpose: unlocalized text symbol
  258. //-----------------------------------------------------------------------------
  259. StringIndex_t TextImage::GetUnlocalizedTextSymbol()
  260. {
  261. return _unlocalizedTextSymbol;
  262. }
  263. //-----------------------------------------------------------------------------
  264. // Purpose: Changes the current font
  265. //-----------------------------------------------------------------------------
  266. void TextImage::SetFont(HFont font)
  267. {
  268. _font = font;
  269. m_bRecalculateTruncation = true;
  270. }
  271. //-----------------------------------------------------------------------------
  272. // Purpose: Gets the font of the text.
  273. //-----------------------------------------------------------------------------
  274. HFont TextImage::GetFont()
  275. {
  276. if ( m_bRenderUsingFallbackFont )
  277. {
  278. return _fallbackFont;
  279. }
  280. return _font;
  281. }
  282. //-----------------------------------------------------------------------------
  283. // Purpose: Sets the size of the TextImage. This is directly tied to drawWidth
  284. //-----------------------------------------------------------------------------
  285. void TextImage::SetSize(int wide, int tall)
  286. {
  287. Image::SetSize(wide, tall);
  288. _drawWidth = wide;
  289. m_bRecalculateTruncation = true;
  290. }
  291. //-----------------------------------------------------------------------------
  292. // Purpose: Draw the Image on screen.
  293. //-----------------------------------------------------------------------------
  294. void TextImage::Paint()
  295. {
  296. int wide, tall;
  297. GetSize(wide, tall);
  298. if (!_utext || GetFont() == INVALID_FONT )
  299. return;
  300. if (m_bRecalculateTruncation)
  301. {
  302. if ( m_bWrap || m_bWrapCenter )
  303. {
  304. RecalculateNewLinePositions();
  305. }
  306. RecalculateEllipsesPosition();
  307. }
  308. DrawSetTextColor(GetColor());
  309. HFont font = GetFont();
  310. DrawSetTextFont(font);
  311. int lineHeight = surface()->GetFontTall(font);
  312. int x = 0, y = 0;
  313. int iIndent = 0;
  314. int px, py;
  315. GetPos(px, py);
  316. int currentLineBreak = 0;
  317. if ( m_bWrapCenter && m_LineXIndent.Count() )
  318. {
  319. x = m_LineXIndent[0];
  320. }
  321. for (wchar_t *wsz = _utext; *wsz != 0; wsz++)
  322. {
  323. wchar_t ch = wsz[0];
  324. if ( m_bAllCaps )
  325. {
  326. ch = towupper( ch );
  327. }
  328. // check for special characters
  329. if (ch == '\r')
  330. {
  331. // ignore, just use \n for newlines
  332. continue;
  333. }
  334. else if (ch == '\n')
  335. {
  336. // newline
  337. iIndent++;
  338. if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() )
  339. {
  340. x = m_LineXIndent[iIndent];
  341. }
  342. else
  343. {
  344. x = 0;
  345. }
  346. y += lineHeight;
  347. continue;
  348. }
  349. else if ((ch == '&') && !m_bNoShortcutSyntax)
  350. {
  351. // "&&" means draw a single ampersand, single one is a shortcut character
  352. if (wsz[1] == '&')
  353. {
  354. // just move on and draw the second ampersand
  355. wsz++;
  356. }
  357. else
  358. {
  359. // draw the underline, then continue to the next character without moving forward
  360. #ifdef VGUI_DRAW_HOTKEYS_ENABLED
  361. surface()->DrawSetTextPos(x + px, y + py);
  362. surface()->DrawUnicodeChar('_');
  363. #endif
  364. continue;
  365. }
  366. }
  367. // see if we've hit the truncated portion of the string
  368. if (wsz == m_pwszEllipsesPosition)
  369. {
  370. // string is truncated, draw ellipses
  371. for (int i = 0; i < 3; i++)
  372. {
  373. surface()->DrawSetTextPos(x + px, y + py);
  374. surface()->DrawUnicodeChar('.');
  375. x += surface()->GetCharacterWidth(font, '.');
  376. }
  377. break;
  378. }
  379. if (currentLineBreak != m_LineBreaks.Count())
  380. {
  381. if (wsz == m_LineBreaks[currentLineBreak])
  382. {
  383. // newline
  384. iIndent++;
  385. if ( m_bWrapCenter && iIndent < m_LineXIndent.Count() )
  386. {
  387. x = m_LineXIndent[iIndent];
  388. }
  389. else
  390. {
  391. x = 0;
  392. }
  393. y += lineHeight;
  394. currentLineBreak++;
  395. }
  396. }
  397. // Underlined text wants to draw the spaces anyway
  398. #if defined( PLATFORM_POSIX ) && !defined( _PS3 )
  399. float xPos = x;
  400. wchar_t chBefore = 0;
  401. wchar_t chAfter = 0;
  402. if ( wsz > _utext )
  403. chBefore = wsz[-1];
  404. chAfter = wsz[1];
  405. if ( m_bAllCaps )
  406. {
  407. chBefore = towupper( chBefore );
  408. chAfter = towupper( chAfter );
  409. }
  410. float flWide = 0.0f, flabcA = 0.0f, flabcC = 0.0f;
  411. surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA, flabcC );
  412. x += floor( flabcA + flWide + flabcC + 0.6f );
  413. //printf( "moving pen to %f,%d\n", floor( xPos + flabcA + 0.6f ) + px, y + py );
  414. surface()->DrawSetTextPos( floor( xPos + flabcA + 0.6f ) + px, y + py );
  415. //printf( "drawing '%C' - (%f,%f,%f) - %f pixels\n", ch, flabcA, flWide, flabcC, floor( flabcA + flWide + 0.6f ) );
  416. surface()->DrawUnicodeChar(ch);
  417. #else
  418. surface()->DrawSetTextPos( x + px, y + py);
  419. surface()->DrawUnicodeChar(ch);
  420. x+= surface()->GetCharacterWidth(font, ch);
  421. #endif
  422. }
  423. // Useful debugging
  424. /*
  425. DrawSetColor(GetColor());
  426. DrawOutlinedRect( 0,0, _drawWidth, tall );
  427. */
  428. }
  429. //-----------------------------------------------------------------------------
  430. // Purpose: Get the size of a text string in pixels
  431. //-----------------------------------------------------------------------------
  432. void TextImage::GetTextSize(int &wide, int &tall)
  433. {
  434. wide = 0;
  435. tall = 0;
  436. int maxWide = 0;
  437. const wchar_t *text = _utext;
  438. HFont font = _font;
  439. if ( font == INVALID_FONT )
  440. return;
  441. if ( m_bWrap || m_bWrapCenter )
  442. {
  443. RecalculateNewLinePositions();
  444. }
  445. // For height, use the remapped font
  446. int fontHeight = surface()->GetFontTall(GetFont());
  447. tall = fontHeight;
  448. int textLen = wcslen(text);
  449. for (int i = 0; i < textLen; i++)
  450. {
  451. wchar_t ch = text[i];
  452. if ( m_bAllCaps )
  453. {
  454. ch = towupper( ch );
  455. }
  456. // handle stupid special characters, these should be removed
  457. if ( (ch == '&') && !m_bNoShortcutSyntax && (text[i + 1] != 0) )
  458. {
  459. continue;
  460. }
  461. int nCharWidth;
  462. #if defined( PLATFORM_POSIX ) && !defined( _PS3 )
  463. wchar_t chBefore = 0;
  464. wchar_t chAfter = 0;
  465. if ( i > 0 )
  466. chBefore = text[i-1];
  467. chAfter = text[i+1];
  468. float flWide = 0.0f, flabcA, flabcC;
  469. surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA, flabcC );
  470. // don't include a negative C measure here - over estimate slightly how long the string might be
  471. flWide = flabcA + flWide;
  472. if ( flabcC > 0.0 )
  473. flWide += flabcC;
  474. nCharWidth = floor( flWide + 0.6f );
  475. #else
  476. int a, b, c;
  477. surface()->GetCharABCwide(font, ch, a, b, c);
  478. nCharWidth = (a + b + c);
  479. #endif
  480. wide += nCharWidth;
  481. if (ch == '\n')
  482. {
  483. tall += fontHeight;
  484. if (wide > maxWide)
  485. {
  486. maxWide=wide;
  487. }
  488. wide=0; // new line, wide is reset...
  489. }
  490. if ( m_bWrap || m_bWrapCenter )
  491. {
  492. for (int j=0; j<m_LineBreaks.Count(); j++)
  493. {
  494. if ( &text[i] == m_LineBreaks[j] )
  495. {
  496. tall += fontHeight;
  497. if ( wide > maxWide )
  498. {
  499. maxWide = wide;
  500. }
  501. wide = nCharWidth; // new line, wide is reset...
  502. }
  503. }
  504. }
  505. }
  506. #ifdef PLATFORM_OSX
  507. wide += 2;
  508. if ( textLen < 3 )
  509. wide += 3;
  510. #endif
  511. if (wide < maxWide)
  512. {
  513. // maxWide only gets set if a newline is in the label
  514. wide = maxWide;
  515. }
  516. }
  517. //-----------------------------------------------------------------------------
  518. // Purpose: Get the size of the text in the image
  519. //-----------------------------------------------------------------------------
  520. void TextImage::GetContentSize(int &wide, int &tall)
  521. {
  522. GetTextSize(wide, tall);
  523. }
  524. //-----------------------------------------------------------------------------
  525. // Purpose: Resize the text image to the content size
  526. //-----------------------------------------------------------------------------
  527. void TextImage::ResizeImageToContent()
  528. {
  529. int wide, tall;
  530. GetContentSize(wide, tall);
  531. SetSize(wide, tall);
  532. }
  533. //-----------------------------------------------------------------------------
  534. // Purpose: Recalculates line breaks
  535. //-----------------------------------------------------------------------------
  536. void TextImage::RecalculateNewLinePositions()
  537. {
  538. HFont font = GetFont();
  539. float charWidth;
  540. float x = 0;
  541. //int wordStartIndex = 0;
  542. wchar_t *wordStartIndex = _utext;
  543. float wordLength = 0;
  544. bool hasWord = false;
  545. bool justStartedNewLine = true;
  546. bool wordStartedOnNewLine = true;
  547. int startChar = 0;
  548. // clear the line breaks list
  549. m_LineBreaks.RemoveAll();
  550. m_LineXIndent.RemoveAll();
  551. // handle the case where this char is a new line, in that case
  552. // we have already taken its break index into account above so skip it.
  553. if (_utext[startChar] == '\r' || _utext[startChar] == '\n')
  554. {
  555. startChar++;
  556. }
  557. // If we're using Asian word wrap rules, we need to know if the current potential line break is due to a space
  558. // or because the rules allow us to break between the previous character and the current character.
  559. bool bStartAsianWordHere = m_bUseAsianWordWrapping;
  560. // loop through all the characters
  561. for (wchar_t *wsz = &_utext[startChar]; *wsz != 0; wsz++)
  562. {
  563. wchar_t ch = wsz[0];
  564. if ( m_bAllCaps )
  565. {
  566. ch = towupper( ch );
  567. }
  568. // line break only on whitespace characters
  569. if ( !isbreakablewspace(ch) )
  570. {
  571. if ( !hasWord || bStartAsianWordHere ) // Asian word-breaking doesn't require spaces between "words"
  572. {
  573. // Start a new word
  574. wordStartIndex = wsz;
  575. hasWord = true;
  576. wordStartedOnNewLine = justStartedNewLine;
  577. wordLength = 0;
  578. }
  579. //else append to the current word
  580. }
  581. else
  582. {
  583. // whitespace/punctuation character
  584. // end the word
  585. hasWord = false;
  586. }
  587. // Can we start a new word after the current character?
  588. bStartAsianWordHere = m_bUseAsianWordWrapping && AsianWordWrap::CanBreakAfter( wsz );
  589. // get the width
  590. #if defined( PLATFORM_POSIX ) && !defined( _PS3 )
  591. wchar_t chBefore = 0;
  592. wchar_t chAfter = 0;
  593. if ( wsz > _utext )
  594. chBefore = wsz[-1];
  595. chAfter = wsz[1];
  596. float flWide = 0.0f, flabcA = 0.0f, flabcC = 0.0f;
  597. surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA, flabcC );
  598. charWidth = floor(flWide + flabcA + flabcC + 0.6f);
  599. #else
  600. charWidth = surface()->GetCharacterWidth(font, ch);
  601. #endif
  602. if (!iswcntrl(ch))
  603. {
  604. justStartedNewLine = false;
  605. }
  606. // check to see if the word is past the end of the line [wordStartIndex, i)
  607. if ((x + charWidth) > _drawWidth || ch == '\r' || ch == '\n')
  608. {
  609. justStartedNewLine = true;
  610. hasWord = false;
  611. if (ch == '\r' || ch == '\n')
  612. {
  613. // set the break at the current character
  614. //don't do this, paint will manually wrap on newline chars
  615. // m_LineBreaks.AddToTail(i);
  616. }
  617. else if (wordStartedOnNewLine)
  618. {
  619. // word is longer than a line, so set the break at the current cursor
  620. m_LineBreaks.AddToTail(wsz);
  621. }
  622. else
  623. {
  624. // set it at the last word Start
  625. m_LineBreaks.AddToTail(wordStartIndex);
  626. // just back to reparse the next line of text
  627. // ywb 8/1/07: Back off one extra char since the 'continue' will increment wsz for us by one in the for loop
  628. wsz = wordStartIndex - 1;
  629. }
  630. // reset word length
  631. wordLength = 0;
  632. x = 0;
  633. continue;
  634. }
  635. // add to the size
  636. x += charWidth;
  637. wordLength += charWidth;
  638. }
  639. RecalculateCenterWrapIndents();
  640. }
  641. //-----------------------------------------------------------------------------
  642. // Purpose: Calculates where the text should be truncated
  643. //-----------------------------------------------------------------------------
  644. void TextImage::RecalculateEllipsesPosition()
  645. {
  646. m_bRecalculateTruncation = false;
  647. m_pwszEllipsesPosition = NULL;
  648. //don't insert ellipses on wrapped strings
  649. if ( m_bWrap || m_bWrapCenter )
  650. return;
  651. // don't truncate strings with newlines
  652. if (wcschr(_utext, '\n') != NULL)
  653. return;
  654. if ( _drawWidth == 0 )
  655. {
  656. int h;
  657. GetSize( _drawWidth, h );
  658. }
  659. for ( int check = 0; check < (m_bUseFallbackFont ? 2 : 1); ++check )
  660. {
  661. HFont font = GetFont();
  662. if ( check == 1 && _fallbackFont != INVALID_FONT )
  663. {
  664. m_pwszEllipsesPosition = NULL;
  665. font = _fallbackFont;
  666. m_bRenderUsingFallbackFont = true;
  667. }
  668. int ellipsesWidth = 3 * surface()->GetCharacterWidth(font, '.');
  669. int x = 0;
  670. for (wchar_t *wsz = _utext; *wsz != 0; wsz++)
  671. {
  672. wchar_t ch = wsz[0];
  673. if ( m_bAllCaps )
  674. {
  675. ch = towupper( ch );
  676. }
  677. // check for special characters
  678. if (ch == '\r')
  679. {
  680. // ignore, just use \n for newlines
  681. continue;
  682. }
  683. else if ((ch == '&') && !m_bNoShortcutSyntax)
  684. {
  685. // "&&" means draw a single ampersand, single one is a shortcut character
  686. if (wsz[1] == '&')
  687. {
  688. // just move on and draw the second ampersand
  689. wsz++;
  690. }
  691. else
  692. {
  693. continue;
  694. }
  695. }
  696. #if defined( PLATFORM_POSIX ) && !defined( _PS3 )
  697. wchar_t chBefore = 0;
  698. wchar_t chAfter = 0;
  699. if ( wsz > _utext )
  700. chBefore = wsz[-1];
  701. chAfter = wsz[1];
  702. float flWide = 0.0f, flabcA = 0.0f, flabcC = 0.0f;
  703. surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA, flabcC );
  704. float len = floor(flWide + flabcA + flabcC + 0.6f);
  705. #else
  706. float len = surface()->GetCharacterWidth(font, ch);
  707. #endif
  708. // don't truncate the first character
  709. if (wsz == _utext)
  710. {
  711. x += len;
  712. continue;
  713. }
  714. if (x + len + ellipsesWidth > _drawWidth)
  715. {
  716. // potential have an ellipses, see if the remaining characters will fit
  717. int remainingLength = len;
  718. for (const wchar_t *rwsz = wsz + 1; *rwsz != 0; rwsz++)
  719. {
  720. #if defined( PLATFORM_POSIX ) && !defined( _PS3 )
  721. wchar_t chBefore = 0;
  722. wchar_t chAfter = 0;
  723. if ( rwsz > _utext )
  724. chBefore = *(rwsz-1);
  725. chAfter = *(rwsz+1);
  726. float flWide = 0.0f, flabcA = 0.0f, flabcC = 0.0f;
  727. surface()->GetKernedCharWidth( font, *rwsz, chBefore, chAfter, flWide, flabcA, flabcC );
  728. remainingLength += floor(flWide + flabcA + flabcC + 0.6f);
  729. #else
  730. remainingLength += surface()->GetCharacterWidth(font, *rwsz);
  731. #endif
  732. }
  733. if (x + remainingLength > _drawWidth)
  734. {
  735. // looks like we've got an ellipses situation
  736. m_pwszEllipsesPosition = wsz;
  737. break;
  738. }
  739. }
  740. x += len;
  741. }
  742. // Didn't need ellipses...
  743. if ( !m_pwszEllipsesPosition )
  744. break;
  745. }
  746. }
  747. void TextImage::SetUseAsianWordWrapping()
  748. {
  749. static bool bCheckForAsianLanguage = false;
  750. static bool bIsAsianLanguage = false;
  751. if ( !bCheckForAsianLanguage )
  752. {
  753. bCheckForAsianLanguage = true;
  754. const char *pLanguage = scheme()->GetLanguage();
  755. if ( pLanguage )
  756. {
  757. if ( !V_stricmp( pLanguage, "japanese" ) ||
  758. !V_stricmp( pLanguage, "schinese" ) ||
  759. !V_stricmp( pLanguage, "tchinese" ) )
  760. {
  761. bIsAsianLanguage = true;
  762. }
  763. }
  764. }
  765. m_bUseAsianWordWrapping = bIsAsianLanguage;
  766. }
  767. void TextImage::SetWrap( bool bWrap )
  768. {
  769. m_bWrap = bWrap;
  770. if ( m_bWrap )
  771. {
  772. SetUseAsianWordWrapping();
  773. }
  774. }
  775. void TextImage::SetCenterWrap( bool bWrap )
  776. {
  777. m_bWrapCenter = bWrap;
  778. if ( m_bWrapCenter )
  779. {
  780. SetUseAsianWordWrapping();
  781. }
  782. }
  783. void TextImage::SetNoShortcutSyntax( bool bNoShortcutSyntax )
  784. {
  785. m_bNoShortcutSyntax = bNoShortcutSyntax;
  786. }
  787. void TextImage::SetUseFallbackFont( bool bState, HFont hFallback )
  788. {
  789. m_bUseFallbackFont = bState;
  790. _fallbackFont = hFallback;
  791. }
  792. void TextImage::SetAllCaps( bool bAllCaps )
  793. {
  794. m_bAllCaps = bAllCaps;
  795. }
  796. void TextImage::ResizeImageToContentMaxWidth( int nMaxWidth )
  797. {
  798. _drawWidth = nMaxWidth;
  799. // Since we might have to use the "fallback" font, go ahead and recalc the ellipses state first to see if that's the case
  800. // NOTE: I think there may be a race condition lurking here, but this seems to work.
  801. if ( m_bRecalculateTruncation )
  802. {
  803. if ( m_bWrap || m_bWrapCenter )
  804. {
  805. RecalculateNewLinePositions();
  806. }
  807. RecalculateEllipsesPosition();
  808. }
  809. ResizeImageToContent();
  810. }
  811. //-----------------------------------------------------------------------------
  812. // Purpose: For center wrapping of multi-line text images, determines the indent each line needs to be centered
  813. //-----------------------------------------------------------------------------
  814. void TextImage::RecalculateCenterWrapIndents()
  815. {
  816. m_LineXIndent.RemoveAll();
  817. if ( !m_bWrapCenter )
  818. return;
  819. if (!_utext || GetFont() == INVALID_FONT )
  820. return;
  821. HFont font = GetFont();
  822. int px, py;
  823. GetPos(px, py);
  824. int currentLineBreak = 0;
  825. float iCurLineW = 0;
  826. for (wchar_t *wsz = _utext; *wsz != 0; wsz++)
  827. {
  828. wchar_t ch = wsz[0];
  829. if ( m_bAllCaps )
  830. {
  831. ch = towupper( ch );
  832. }
  833. // check for special characters
  834. if (ch == '\r')
  835. {
  836. // ignore, just use \n for newlines
  837. continue;
  838. }
  839. else if (ch == '\n')
  840. {
  841. int iIdx = m_LineXIndent.AddToTail();
  842. m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5;
  843. iCurLineW = 0;
  844. continue;
  845. }
  846. else if ((ch == '&') && !m_bNoShortcutSyntax)
  847. {
  848. // "&&" means draw a single ampersand, single one is a shortcut character
  849. if (wsz[1] == '&')
  850. {
  851. // just move on and draw the second ampersand
  852. wsz++;
  853. }
  854. else
  855. {
  856. // draw the underline, then continue to the next character without moving forward
  857. continue;
  858. }
  859. }
  860. // Don't need to check ellipses, they're not used when wrapping
  861. if (currentLineBreak != m_LineBreaks.Count())
  862. {
  863. if (wsz == m_LineBreaks[currentLineBreak])
  864. {
  865. int iIdx = m_LineXIndent.AddToTail();
  866. m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5;
  867. iCurLineW = 0;
  868. currentLineBreak++;
  869. }
  870. }
  871. #if defined( PLATFORM_POSIX ) && !defined( _PS3 )
  872. wchar_t chBefore = 0;
  873. wchar_t chAfter = 0;
  874. if ( wsz > _utext )
  875. chBefore = wsz[-1];
  876. chAfter = wsz[1];
  877. float flWide = 0.0f, flabcA = 0.0f, flabcC = 0.0f;
  878. surface()->GetKernedCharWidth( font, ch, chBefore, chAfter, flWide, flabcA, flabcC );
  879. iCurLineW += floor(flWide + flabcA + flabcC + 0.6f);
  880. #else
  881. iCurLineW += surface()->GetCharacterWidth(font, ch);
  882. #endif
  883. }
  884. // Add the final line
  885. int iIdx = m_LineXIndent.AddToTail();
  886. m_LineXIndent[iIdx] = (_drawWidth - iCurLineW) * 0.5;
  887. }
  888. void TextImage::SetBounds( int x, int y, int w, int h )
  889. {
  890. SetPos( x, y );
  891. SetSize( w, h );
  892. }
  893. void TextImage::AddColorChange( Color col, int iTextStreamIndex )
  894. {
  895. label_colorchange_t tmpChange;
  896. tmpChange.color = col;
  897. tmpChange.textStreamIndex = iTextStreamIndex;
  898. m_ColorChangeStream.Insert( tmpChange );
  899. }