Team Fortress 2 Source Code as on 22/4/2020
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.

985 lines
23 KiB

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