[gui] fix <hinttext> not being shown once unfocused/refocused
[vuplus_xbmc] / xbmc / guilib / GUITextLayout.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
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 2, or (at your option)
8  *  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 XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20
21 #include "GUITextLayout.h"
22 #include "GUIFont.h"
23 #include "GUIControl.h"
24 #include "GUIColorManager.h"
25 #include "utils/CharsetConverter.h"
26 #include "utils/StringUtils.h"
27
28 using namespace std;
29
30 #define WORK_AROUND_NEEDED_FOR_LINE_BREAKS
31
32 CGUIString::CGUIString(iString start, iString end, bool carriageReturn)
33 {
34   m_text.assign(start, end);
35   m_carriageReturn = carriageReturn;
36 }
37
38 CStdString CGUIString::GetAsString() const
39 {
40   CStdString text;
41   for (unsigned int i = 0; i < m_text.size(); i++)
42     text += (char)(m_text[i] & 0xff);
43   return text;
44 }
45
46 CGUITextLayout::CGUITextLayout(CGUIFont *font, bool wrap, float fHeight, CGUIFont *borderFont)
47 {
48   m_font = font;
49   m_borderFont = borderFont;
50   m_textColor = 0;
51   m_wrap = wrap;
52   m_maxHeight = fHeight;
53   m_textWidth = 0;
54   m_textHeight = 0;
55   m_lastUpdateW = false;
56 }
57
58 void CGUITextLayout::SetWrap(bool bWrap)
59 {
60   m_wrap = bWrap;
61 }
62
63 void CGUITextLayout::Render(float x, float y, float angle, color_t color, color_t shadowColor, uint32_t alignment, float maxWidth, bool solid)
64 {
65   if (!m_font)
66     return;
67
68   // set the main text color
69   if (m_colors.size())
70     m_colors[0] = color;
71
72   // render the text at the required location, angle, and size
73   if (angle)
74   {
75     static const float degrees_to_radians = 0.01745329252f;
76     g_graphicsContext.AddTransform(TransformMatrix::CreateZRotation(angle * degrees_to_radians, x, y, g_graphicsContext.GetScalingPixelRatio()));
77   }
78   // center our text vertically
79   if (alignment & XBFONT_CENTER_Y)
80   {
81     y -= m_font->GetTextHeight(m_lines.size()) * 0.5f;;
82     alignment &= ~XBFONT_CENTER_Y;
83   }
84   m_font->Begin();
85   for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); ++i)
86   {
87     const CGUIString &string = *i;
88     uint32_t align = alignment;
89     if (align & XBFONT_JUSTIFIED && string.m_carriageReturn)
90       align &= ~XBFONT_JUSTIFIED;
91     if (solid)
92       m_font->DrawText(x, y, m_colors[0], shadowColor, string.m_text, align, maxWidth);
93     else
94       m_font->DrawText(x, y, m_colors, shadowColor, string.m_text, align, maxWidth);
95     y += m_font->GetLineHeight();
96   }
97   m_font->End();
98   if (angle)
99     g_graphicsContext.RemoveTransform();
100 }
101
102 bool CGUITextLayout::UpdateScrollinfo(CScrollInfo &scrollInfo)
103 {
104   if (!m_font)
105     return false;
106   if (m_lines.empty())
107     return false;
108
109   return m_font->UpdateScrollInfo(m_lines[0].m_text, scrollInfo);
110 }
111
112
113 void CGUITextLayout::RenderScrolling(float x, float y, float angle, color_t color, color_t shadowColor, uint32_t alignment, float maxWidth, const CScrollInfo &scrollInfo)
114 {
115   if (!m_font)
116     return;
117
118   // set the main text color
119   if (m_colors.size())
120     m_colors[0] = color;
121
122   // render the text at the required location, angle, and size
123   if (angle)
124   {
125     static const float degrees_to_radians = 0.01745329252f;
126     g_graphicsContext.AddTransform(TransformMatrix::CreateZRotation(angle * degrees_to_radians, x, y, g_graphicsContext.GetScalingPixelRatio()));
127   }
128   // center our text vertically
129   if (alignment & XBFONT_CENTER_Y)
130   {
131     y -= m_font->GetTextHeight(m_lines.size()) * 0.5f;;
132     alignment &= ~XBFONT_CENTER_Y;
133   }
134   m_font->Begin();
135   // NOTE: This workaround is needed as otherwise multi-line text that scrolls
136   //       will scroll in proportion to the number of lines.  Ideally we should
137   //       do the DrawScrollingText calculation here.  This probably won't make
138   //       any difference to the smoothness of scrolling though which will be
139   //       jumpy with this sort of thing.  It's not exactly a well used situation
140   //       though, so this hack is probably OK.
141   for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); ++i)
142   {
143     const CGUIString &string = *i;
144     m_font->DrawScrollingText(x, y, m_colors, shadowColor, string.m_text, alignment, maxWidth, scrollInfo);
145     y += m_font->GetLineHeight();
146   }
147   m_font->End();
148   if (angle)
149     g_graphicsContext.RemoveTransform();
150 }
151
152 void CGUITextLayout::RenderOutline(float x, float y, color_t color, color_t outlineColor, uint32_t alignment, float maxWidth)
153 {
154   if (!m_font)
155     return;
156
157   // set the outline color
158   vecColors outlineColors;
159   if (m_colors.size())
160     outlineColors.push_back(outlineColor);
161
162   // center our text vertically
163   if (alignment & XBFONT_CENTER_Y)
164   {
165     y -= m_font->GetTextHeight(m_lines.size()) * 0.5f;;
166     alignment &= ~XBFONT_CENTER_Y;
167   }
168   if (m_borderFont)
169   {
170     // adjust so the baselines of the fonts align
171     float by = y + m_font->GetTextBaseLine() - m_borderFont->GetTextBaseLine();
172     m_borderFont->Begin();
173     for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); ++i)
174     {
175       const CGUIString &string = *i;
176       uint32_t align = alignment;
177       if (align & XBFONT_JUSTIFIED && string.m_carriageReturn)
178         align &= ~XBFONT_JUSTIFIED;
179       // text centered horizontally must be computed using the original font, not the bordered
180       // font, as the bordered font will be wider, and thus will end up uncentered.
181       // TODO: We should really have a better way to handle text extent - at the moment we assume
182       //       that text is rendered from a posx, posy, width, and height which isn't enough to
183       //       accurately position text. We need a vertical and horizontal offset of the baseline
184       //       and cursor as well.
185       float bx = x;
186       if (align & XBFONT_CENTER_X)
187       {
188         bx -= m_font->GetTextWidth(string.m_text) * 0.5f;
189         align &= ~XBFONT_CENTER_X;
190       }
191
192       // don't pass maxWidth through to the renderer for the same reason above: it will cause clipping
193       // on the left.
194       m_borderFont->DrawText(bx, by, outlineColors, 0, string.m_text, align, 0);
195       by += m_borderFont->GetLineHeight();
196     }
197     m_borderFont->End();
198   }
199
200   // set the main text color
201   if (m_colors.size())
202     m_colors[0] = color;
203
204   m_font->Begin();
205   for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); ++i)
206   {
207     const CGUIString &string = *i;
208     uint32_t align = alignment;
209     if (align & XBFONT_JUSTIFIED && string.m_carriageReturn)
210       align &= ~XBFONT_JUSTIFIED;
211
212     // don't pass maxWidth through to the renderer for the reason above.
213     m_font->DrawText(x, y, m_colors, 0, string.m_text, align, 0);
214     y += m_font->GetLineHeight();
215   }
216   m_font->End();
217 }
218
219 bool CGUITextLayout::Update(const CStdString &text, float maxWidth, bool forceUpdate /*= false*/, bool forceLTRReadingOrder /*= false*/)
220 {
221   if (text == m_lastUtf8Text && !forceUpdate && !m_lastUpdateW)
222     return false;
223
224   m_lastUtf8Text = text;
225   m_lastUpdateW = false;
226   CStdStringW utf16;
227   utf8ToW(text, utf16);
228   UpdateCommon(utf16, maxWidth, forceLTRReadingOrder);
229   return true;
230 }
231
232 bool CGUITextLayout::UpdateW(const CStdStringW &text, float maxWidth /*= 0*/, bool forceUpdate /*= false*/, bool forceLTRReadingOrder /*= false*/)
233 {
234   if (text == m_lastText && !forceUpdate && m_lastUpdateW)
235     return false;
236
237   m_lastText = text;
238   m_lastUpdateW = true;
239   UpdateCommon(text, maxWidth, forceLTRReadingOrder);
240   return true;
241 }
242
243 void CGUITextLayout::UpdateCommon(const CStdStringW &text, float maxWidth, bool forceLTRReadingOrder)
244 {
245   // parse the text for style information
246   vecText parsedText;
247   vecColors colors;
248   ParseText(text, m_font ? m_font->GetStyle() : 0, m_textColor, colors, parsedText);
249
250   // and update
251   UpdateStyled(parsedText, colors, maxWidth, forceLTRReadingOrder);
252 }
253
254 void CGUITextLayout::UpdateStyled(const vecText &text, const vecColors &colors, float maxWidth, bool forceLTRReadingOrder)
255 {
256   // empty out our previous string
257   m_lines.clear();
258   m_colors = colors;
259
260   // if we need to wrap the text, then do so
261   if (m_wrap && maxWidth > 0)
262     WrapText(text, maxWidth);
263   else
264     LineBreakText(text, m_lines);
265
266   // remove any trailing blank lines
267   while (!m_lines.empty() && m_lines.back().m_text.empty())
268     m_lines.pop_back();
269
270   BidiTransform(m_lines, forceLTRReadingOrder);
271
272   // and cache the width and height for later reading
273   CalcTextExtent();
274 }
275
276 // BidiTransform is used to handle RTL text flipping in the string
277 void CGUITextLayout::BidiTransform(vector<CGUIString> &lines, bool forceLTRReadingOrder)
278 {
279   for (unsigned int i=0; i<lines.size(); i++)
280   {
281     CGUIString &line = lines[i];
282
283     // reserve enough space in the flipped text
284     vecText flippedText;
285     flippedText.reserve(line.m_text.size());
286
287     character_t sectionStyle = 0xffff0000; // impossible to achieve
288     CStdStringW sectionText;
289     for (vecText::iterator it = line.m_text.begin(); it != line.m_text.end(); ++it)
290     {
291       character_t style = *it & 0xffff0000;
292       if (style != sectionStyle)
293       {
294         if (!sectionText.empty())
295         { // style has changed, bidi flip text
296           CStdStringW sectionFlipped = BidiFlip(sectionText, forceLTRReadingOrder);
297           for (unsigned int j = 0; j < sectionFlipped.size(); j++)
298             flippedText.push_back(sectionStyle | sectionFlipped[j]);
299         }
300         sectionStyle = style;
301         sectionText.clear();
302       }
303       sectionText.push_back( (wchar_t)(*it & 0xffff) );
304     }
305
306     // handle the last section
307     if (!sectionText.empty())
308     {
309       CStdStringW sectionFlipped = BidiFlip(sectionText, forceLTRReadingOrder);
310       for (unsigned int j = 0; j < sectionFlipped.size(); j++)
311         flippedText.push_back(sectionStyle | sectionFlipped[j]);
312     }
313
314     // replace the original line with the proccessed one
315     lines[i] = CGUIString(flippedText.begin(), flippedText.end(), line.m_carriageReturn);
316   }
317 }
318
319 CStdStringW CGUITextLayout::BidiFlip(const CStdStringW &text, bool forceLTRReadingOrder)
320 {
321   CStdStringA utf8text;
322   CStdStringW visualText;
323
324   // convert to utf8, and back to utf16 with bidi flipping
325   g_charsetConverter.wToUTF8(text, utf8text);
326   g_charsetConverter.utf8ToW(utf8text, visualText, true, forceLTRReadingOrder);
327
328   return visualText;
329 }
330
331 void CGUITextLayout::Filter(CStdString &text)
332 {
333   CStdStringW utf16;
334   utf8ToW(text, utf16);
335   vecColors colors;
336   vecText parsedText;
337   ParseText(utf16, 0, 0xffffffff, colors, parsedText);
338   utf16.clear();
339   for (unsigned int i = 0; i < parsedText.size(); i++)
340     utf16 += (wchar_t)(0xffff & parsedText[i]);
341   g_charsetConverter.wToUTF8(utf16, text);
342 }
343
344 void CGUITextLayout::ParseText(const CStdStringW &text, uint32_t defaultStyle, color_t defaultColor, vecColors &colors, vecText &parsedText)
345 {
346   // run through the string, searching for:
347   // [B] or [/B] -> toggle bold on and off
348   // [I] or [/I] -> toggle italics on and off
349   // [COLOR ffab007f] or [/COLOR] -> toggle color on and off
350   // [CAPS <option>] or [/CAPS] -> toggle capatilization on and off
351
352   uint32_t currentStyle = defaultStyle; // start with the default font's style
353   color_t currentColor = 0;
354
355   colors.push_back(defaultColor);
356   stack<color_t> colorStack;
357   colorStack.push(0);
358
359   // these aren't independent, but that's probably not too much of an issue
360   // eg [UPPERCASE]Glah[LOWERCASE]FReD[/LOWERCASE]Georeg[/UPPERCASE] will work (lower case >> upper case)
361   // but [LOWERCASE]Glah[UPPERCASE]FReD[/UPPERCASE]Georeg[/LOWERCASE] won't
362
363   int startPos = 0;
364   size_t pos = text.find(L'[');
365   while (pos != std::string::npos && pos + 1 < text.size())
366   {
367     uint32_t newStyle = 0;
368     color_t newColor = currentColor;
369     bool colorTagChange = false;
370     bool newLine = false;
371     // have a [ - check if it's an ON or OFF switch
372     bool on(true);
373     size_t endPos = pos++; // finish of string
374     if (text[pos] == L'/')
375     {
376       on = false;
377       pos++;
378     }
379     // check for each type
380     if (text.compare(pos, 2, L"B]") == 0)
381     { // bold - finish the current text block and assign the bold state
382       pos += 2;
383       if ((on && text.find(L"[/B]",pos) != std::string::npos) ||          // check for a matching end point
384          (!on && (currentStyle & FONT_STYLE_BOLD)))       // or matching start point
385         newStyle = FONT_STYLE_BOLD;
386     }
387     else if (text.compare(pos, 2, L"I]") == 0)
388     { // italics
389       pos += 2;
390       if ((on && text.find(L"[/I]", pos) != std::string::npos) ||          // check for a matching end point
391          (!on && (currentStyle & FONT_STYLE_ITALICS)))    // or matching start point
392         newStyle = FONT_STYLE_ITALICS;
393     }
394     else if (text.compare(pos, 10, L"UPPERCASE]") == 0)
395     {
396       pos += 10;
397       if ((on && text.find(L"[/UPPERCASE]", pos) != std::string::npos) ||  // check for a matching end point
398          (!on && (currentStyle & FONT_STYLE_UPPERCASE)))  // or matching start point
399         newStyle = FONT_STYLE_UPPERCASE;
400     }
401     else if (text.compare(pos, 10, L"LOWERCASE]") == 0)
402     {
403       pos += 10;
404       if ((on && text.find(L"[/LOWERCASE]", pos) != std::string::npos) ||  // check for a matching end point
405          (!on && (currentStyle & FONT_STYLE_LOWERCASE)))  // or matching start point
406         newStyle = FONT_STYLE_LOWERCASE;
407     }
408     else if (text.compare(pos, 3, L"CR]") == 0 && on)
409     {
410       newLine = true;
411       pos += 3;
412     }
413     else if (text.compare(pos,5, L"COLOR") == 0)
414     { // color
415       size_t finish = text.find(L']', pos + 5);
416       if (on && finish != std::string::npos && text.find(L"[/COLOR]",finish) != std::string::npos)
417       {
418         std::string t;
419         g_charsetConverter.wToUTF8(text.substr(pos + 5, finish - pos - 5), t);
420         color_t color = g_colorManager.GetColor(t);
421         vecColors::const_iterator it = std::find(colors.begin(), colors.end(), color);
422         if (it == colors.end())
423         { // create new color
424           if (colors.size() <= 0xFF)
425           {
426             newColor = colors.size();
427             colors.push_back(color);
428           }
429           else // we have only 8 bits for color index, fallback to first color if reach max.
430             newColor = 0;
431         }
432         else
433           // reuse existing color
434           newColor = it - colors.begin();
435         colorStack.push(newColor);
436         colorTagChange = true;
437       }
438       else if (!on && finish == pos + 5 && colorStack.size() > 1)
439       { // revert to previous color
440         colorStack.pop();
441         newColor = colorStack.top();
442         colorTagChange = true;
443       }
444       if (finish != CStdString::npos)
445         pos = finish + 1;
446     }
447
448     if (newStyle || colorTagChange || newLine)
449     { // we have a new style or a new color, so format up the previous segment
450       CStdStringW subText = text.substr(startPos, endPos - startPos);
451       if (currentStyle & FONT_STYLE_UPPERCASE)
452         StringUtils::ToUpper(subText);
453       if (currentStyle & FONT_STYLE_LOWERCASE)
454         StringUtils::ToLower(subText);
455       AppendToUTF32(subText, ((currentStyle & 3) << 24) | (currentColor << 16), parsedText);
456       if (newLine)
457         parsedText.push_back(L'\n');
458
459       // and switch to the new style
460       startPos = pos;
461       currentColor = newColor;
462       if (on)
463         currentStyle |= newStyle;
464       else
465         currentStyle &= ~newStyle;
466     }
467     pos = text.find(L'[', pos);
468   }
469   // now grab the remainder of the string
470   CStdStringW subText = text.substr(startPos);
471   if (currentStyle & FONT_STYLE_UPPERCASE)
472     StringUtils::ToUpper(subText);
473   if (currentStyle & FONT_STYLE_LOWERCASE)
474     StringUtils::ToLower(subText);
475   AppendToUTF32(subText, ((currentStyle & 3) << 24) | (currentColor << 16), parsedText);
476 }
477
478 void CGUITextLayout::SetMaxHeight(float fHeight)
479 {
480   m_maxHeight = fHeight;
481 }
482
483 void CGUITextLayout::WrapText(const vecText &text, float maxWidth)
484 {
485   if (!m_font)
486     return;
487
488   int nMaxLines = (m_maxHeight > 0 && m_font->GetLineHeight() > 0)?(int)ceilf(m_maxHeight / m_font->GetLineHeight()):-1;
489
490   m_lines.clear();
491
492   vector<CGUIString> lines;
493   LineBreakText(text, lines);
494
495   for (unsigned int i = 0; i < lines.size(); i++)
496   {
497     const CGUIString &line = lines[i];
498     vecText::const_iterator lastSpace = line.m_text.begin();
499     vecText::const_iterator pos = line.m_text.begin();
500     unsigned int lastSpaceInLine = 0;
501     vecText curLine;
502     while (pos != line.m_text.end())
503     {
504       // Get the current letter in the string
505       character_t letter = *pos;
506       // check for a space
507       if (CanWrapAtLetter(letter))
508       {
509         float width = m_font->GetTextWidth(curLine);
510         if (width > maxWidth)
511         {
512           if (lastSpace != line.m_text.begin() && lastSpaceInLine > 0)
513           {
514             CGUIString string(curLine.begin(), curLine.begin() + lastSpaceInLine, false);
515             m_lines.push_back(string);
516             // check for exceeding our number of lines
517             if (nMaxLines > 0 && m_lines.size() >= (size_t)nMaxLines)
518               return;
519             // skip over spaces
520             pos = lastSpace;
521             while (pos != line.m_text.end() && IsSpace(*pos))
522               ++pos;
523             curLine.clear();
524             lastSpaceInLine = 0;
525             lastSpace = line.m_text.begin();
526             continue;
527           }
528         }
529         lastSpace = pos;
530         lastSpaceInLine = curLine.size();
531       }
532       curLine.push_back(letter);
533       ++pos;
534     }
535     // now add whatever we have left to the string
536     float width = m_font->GetTextWidth(curLine);
537     if (width > maxWidth)
538     {
539       // too long - put up to the last space on if we can + remove it from what's left.
540       if (lastSpace != line.m_text.begin() && lastSpaceInLine > 0)
541       {
542         CGUIString string(curLine.begin(), curLine.begin() + lastSpaceInLine, false);
543         m_lines.push_back(string);
544         // check for exceeding our number of lines
545         if (nMaxLines > 0 && m_lines.size() >= (size_t)nMaxLines)
546           return;
547         curLine.erase(curLine.begin(), curLine.begin() + lastSpaceInLine);
548         while (curLine.size() && IsSpace(curLine.at(0)))
549           curLine.erase(curLine.begin());
550       }
551     }
552     CGUIString string(curLine.begin(), curLine.end(), true);
553     m_lines.push_back(string);
554     // check for exceeding our number of lines
555     if (nMaxLines > 0 && m_lines.size() >= (size_t)nMaxLines)
556       return;
557   }
558 }
559
560 void CGUITextLayout::LineBreakText(const vecText &text, vector<CGUIString> &lines)
561 {
562   int nMaxLines = (m_maxHeight > 0 && m_font && m_font->GetLineHeight() > 0)?(int)ceilf(m_maxHeight / m_font->GetLineHeight()):-1;
563   vecText::const_iterator lineStart = text.begin();
564   vecText::const_iterator pos = text.begin();
565   while (pos != text.end() && (nMaxLines <= 0 || lines.size() < (size_t)nMaxLines))
566   {
567     // Get the current letter in the string
568     character_t letter = *pos;
569
570     // Handle the newline character
571     if ((letter & 0xffff) == L'\n' )
572     { // push back everything up till now
573       CGUIString string(lineStart, pos, true);
574       lines.push_back(string);
575       lineStart = pos + 1;
576     }
577     ++pos;
578   }
579   // handle the last line if non-empty
580   if (lineStart < text.end() && (nMaxLines <= 0 || lines.size() < (size_t)nMaxLines))
581   {
582     CGUIString string(lineStart, text.end(), true);
583     lines.push_back(string);
584   }
585 }
586
587 void CGUITextLayout::GetTextExtent(float &width, float &height) const
588 {
589   width = m_textWidth;
590   height = m_textHeight;
591 }
592
593 void CGUITextLayout::CalcTextExtent()
594 {
595   m_textWidth = 0;
596   m_textHeight = 0;
597   if (!m_font) return;
598
599   for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); ++i)
600   {
601     const CGUIString &string = *i;
602     float w = m_font->GetTextWidth(string.m_text);
603     if (w > m_textWidth)
604       m_textWidth = w;
605   }
606   m_textHeight = m_font->GetTextHeight(m_lines.size());
607 }
608
609 unsigned int CGUITextLayout::GetTextLength() const
610 {
611   unsigned int length = 0;
612   for (vector<CGUIString>::const_iterator i = m_lines.begin(); i != m_lines.end(); ++i)
613     length += i->m_text.size();
614   return length;
615 }
616
617 void CGUITextLayout::GetFirstText(vecText &text) const
618 {
619   text.clear();
620   if (m_lines.size())
621     text = m_lines[0].m_text;
622 }
623
624 float CGUITextLayout::GetTextWidth(const CStdStringW &text) const
625 {
626   // NOTE: Assumes a single line of text
627   if (!m_font) return 0;
628   vecText utf32;
629   AppendToUTF32(text, (m_font->GetStyle() & 3) << 24, utf32);
630   return m_font->GetTextWidth(utf32);
631 }
632
633 void CGUITextLayout::DrawText(CGUIFont *font, float x, float y, color_t color, color_t shadowColor, const CStdString &text, uint32_t align)
634 {
635   if (!font) return;
636   vecText utf32;
637   AppendToUTF32(text, 0, utf32);
638   font->DrawText(x, y, color, shadowColor, utf32, align, 0);
639 }
640
641 void CGUITextLayout::AppendToUTF32(const CStdStringW &utf16, character_t colStyle, vecText &utf32)
642 {
643   // NOTE: Assumes a single line of text
644   utf32.reserve(utf32.size() + utf16.size());
645   for (unsigned int i = 0; i < utf16.size(); i++)
646     utf32.push_back(utf16[i] | colStyle);
647 }
648
649 void CGUITextLayout::utf8ToW(const CStdString &utf8, CStdStringW &utf16)
650 {
651 #ifdef WORK_AROUND_NEEDED_FOR_LINE_BREAKS
652   // NOTE: This appears to strip \n characters from text.  This may be a consequence of incorrect
653   //       expression of the \n in utf8 (we just use character code 10) or it might be something
654   //       more sinister.  For now, we use the workaround below.
655   CStdStringArray multiLines;
656   StringUtils::SplitString(utf8, "\n", multiLines);
657   for (unsigned int i = 0; i < multiLines.size(); i++)
658   {
659     CStdStringW line;
660     // no need to bidiflip here - it's done in BidiTransform above
661     g_charsetConverter.utf8ToW(multiLines[i], line, false);
662     utf16 += line;
663     if (i < multiLines.size() - 1)
664       utf16.push_back(L'\n');
665   }
666 #else
667   // no need to bidiflip here - it's done in BidiTransform above
668   g_charsetConverter.utf8ToW(utf8, utf16, false);
669 #endif
670 }
671
672 void CGUITextLayout::AppendToUTF32(const CStdString &utf8, character_t colStyle, vecText &utf32)
673 {
674   CStdStringW utf16;
675   utf8ToW(utf8, utf16);
676   AppendToUTF32(utf16, colStyle, utf32);
677 }
678
679 void CGUITextLayout::Reset()
680 {
681   m_lines.clear();
682   m_lastText.clear();
683   m_lastUtf8Text.clear();
684   m_textWidth = m_textHeight = 0;
685 }
686
687