aboutsummaryrefslogtreecommitdiff
path: root/guilib/GUITextLayout.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'guilib/GUITextLayout.cpp')
-rw-r--r--guilib/GUITextLayout.cpp628
1 files changed, 0 insertions, 628 deletions
diff --git a/guilib/GUITextLayout.cpp b/guilib/GUITextLayout.cpp
deleted file mode 100644
index 2eb676cd3b..0000000000
--- a/guilib/GUITextLayout.cpp
+++ /dev/null
@@ -1,628 +0,0 @@
-/*
- * Copyright (C) 2005-2008 Team XBMC
- * http://www.xbmc.org
- *
- * 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 2, 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 XBMC; see the file COPYING. If not, write to
- * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
- * http://www.gnu.org/copyleft/gpl.html
- *
- */
-
-#include "GUITextLayout.h"
-#include "GUIFont.h"
-#include "GUIControl.h"
-#include "GUIColorManager.h"
-#include "utils/CharsetConverter.h"
-#include "StringUtils.h"
-
-using namespace std;
-
-#define WORK_AROUND_NEEDED_FOR_LINE_BREAKS
-
-CGUIString::CGUIString(iString start, iString end, bool carriageReturn)
-{
- m_text.assign(start, end);
- m_carriageReturn = carriageReturn;
-}
-
-CStdString CGUIString::GetAsString() const
-{
- CStdString text;
- for (unsigned int i = 0; i < m_text.size(); i++)
- text += (char)(m_text[i] & 0xff);
- return text;
-}
-
-CGUITextLayout::CGUITextLayout(CGUIFont *font, bool wrap, float fHeight, CGUIFont *borderFont)
-{
- m_font = font;
- m_borderFont = borderFont;
- m_textColor = 0;
- m_wrap = wrap;
- m_maxHeight = fHeight;
- m_textWidth = 0;
- m_textHeight = 0;
-}
-
-void CGUITextLayout::SetWrap(bool bWrap)
-{
- m_wrap = bWrap;
-}
-
-void CGUITextLayout::Render(float x, float y, float angle, color_t color, color_t shadowColor, uint32_t alignment, float maxWidth, bool solid)
-{
- if (!m_font)
- return;
-
- // set the main text color
- if (m_colors.size())
- m_colors[0] = color;
-
- // render the text at the required location, angle, and size
- if (angle)
- {
- static const float degrees_to_radians = 0.01745329252f;
- g_graphicsContext.AddTransform(TransformMatrix::CreateZRotation(angle * degrees_to_radians, x, y, g_graphicsContext.GetScalingPixelRatio()));
- }
- // center our text vertically
- if (alignment & XBFONT_CENTER_Y)
- {
- y -= m_font->GetTextHeight(m_lines.size()) * 0.5f;;
- alignment &= ~XBFONT_CENTER_Y;
- }
- m_font->Begin();
- for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); i++)
- {
- const CGUIString &string = *i;
- uint32_t align = alignment;
- if (align & XBFONT_JUSTIFIED && string.m_carriageReturn)
- align &= ~XBFONT_JUSTIFIED;
- if (solid)
- m_font->DrawText(x, y, m_colors[0], shadowColor, string.m_text, align, maxWidth);
- else
- m_font->DrawText(x, y, m_colors, shadowColor, string.m_text, align, maxWidth);
- y += m_font->GetLineHeight();
- }
- m_font->End();
- if (angle)
- g_graphicsContext.RemoveTransform();
-}
-
-
-void CGUITextLayout::RenderScrolling(float x, float y, float angle, color_t color, color_t shadowColor, uint32_t alignment, float maxWidth, CScrollInfo &scrollInfo)
-{
- if (!m_font)
- return;
-
- // set the main text color
- if (m_colors.size())
- m_colors[0] = color;
-
- // render the text at the required location, angle, and size
- if (angle)
- {
- static const float degrees_to_radians = 0.01745329252f;
- g_graphicsContext.AddTransform(TransformMatrix::CreateZRotation(angle * degrees_to_radians, x, y, g_graphicsContext.GetScalingPixelRatio()));
- }
- // center our text vertically
- if (alignment & XBFONT_CENTER_Y)
- {
- y -= m_font->GetTextHeight(m_lines.size()) * 0.5f;;
- alignment &= ~XBFONT_CENTER_Y;
- }
- m_font->Begin();
- // NOTE: This workaround is needed as otherwise multi-line text that scrolls
- // will scroll in proportion to the number of lines. Ideally we should
- // do the DrawScrollingText calculation here. This probably won't make
- // any difference to the smoothness of scrolling though which will be
- // jumpy with this sort of thing. It's not exactly a well used situation
- // though, so this hack is probably OK.
- float speed = scrollInfo.pixelSpeed;
- for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); i++)
- {
- const CGUIString &string = *i;
- m_font->DrawScrollingText(x, y, m_colors, shadowColor, string.m_text, alignment, maxWidth, scrollInfo);
- y += m_font->GetLineHeight();
- scrollInfo.pixelSpeed = 0;
- }
- scrollInfo.pixelSpeed = speed;
- m_font->End();
- if (angle)
- g_graphicsContext.RemoveTransform();
-}
-
-void CGUITextLayout::RenderOutline(float x, float y, color_t color, color_t outlineColor, uint32_t alignment, float maxWidth)
-{
- if (!m_font)
- return;
-
- // set the outline color
- vecColors outlineColors;
- if (m_colors.size())
- outlineColors.push_back(outlineColor);
-
- // center our text vertically
- if (alignment & XBFONT_CENTER_Y)
- {
- y -= m_font->GetTextHeight(m_lines.size()) * 0.5f;;
- alignment &= ~XBFONT_CENTER_Y;
- }
- if (m_borderFont)
- {
- float by = y;
- m_borderFont->Begin();
- for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); i++)
- {
- const CGUIString &string = *i;
- uint32_t align = alignment;
- if (align & XBFONT_JUSTIFIED && string.m_carriageReturn)
- align &= ~XBFONT_JUSTIFIED;
-
- m_borderFont->DrawText(x, by, outlineColors, 0, string.m_text, align, maxWidth);
- by += m_borderFont->GetLineHeight();
- }
- m_borderFont->End();
- }
-
- // set the main text color
- if (m_colors.size())
- m_colors[0] = color;
-
- m_font->Begin();
- for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); i++)
- {
- const CGUIString &string = *i;
- uint32_t align = alignment;
- if (align & XBFONT_JUSTIFIED && string.m_carriageReturn)
- align &= ~XBFONT_JUSTIFIED;
-
- m_font->DrawText(x, y, m_colors, 0, string.m_text, align, maxWidth);
- y += m_font->GetLineHeight();
- }
- m_font->End();
-}
-
-bool CGUITextLayout::Update(const CStdString &text, float maxWidth, bool forceUpdate /*= false*/, bool forceLTRReadingOrder /*= false*/)
-{
- if (text == m_lastText && !forceUpdate)
- return false;
-
- // convert to utf16
- CStdStringW utf16;
- utf8ToW(text, utf16);
-
- // update
- SetText(utf16, maxWidth, forceLTRReadingOrder);
-
- // and set our parameters to indicate no further update is required
- m_lastText = text;
- return true;
-}
-
-void CGUITextLayout::SetText(const CStdStringW &text, float maxWidth, bool forceLTRReadingOrder /*= false*/)
-{
- vecText parsedText;
-
- // empty out our previous string
- m_lines.clear();
- m_colors.clear();
- m_colors.push_back(m_textColor);
-
- // parse the text into our string objects
- ParseText(text, parsedText);
-
- // add \n to the end of the string
- parsedText.push_back(L'\n');
-
- // if we need to wrap the text, then do so
- if (m_wrap && maxWidth > 0)
- WrapText(parsedText, maxWidth);
- else
- LineBreakText(parsedText, m_lines);
-
- // remove any trailing blank lines
- while (!m_lines.empty() && m_lines.back().m_text.empty())
- m_lines.pop_back();
-
- BidiTransform(m_lines, forceLTRReadingOrder);
-
- // and cache the width and height for later reading
- CalcTextExtent();
-}
-
-// BidiTransform is used to handle RTL text flipping in the string
-void CGUITextLayout::BidiTransform(vector<CGUIString> &lines, bool forceLTRReadingOrder)
-{
- for (unsigned int i=0; i<lines.size(); i++)
- {
- CGUIString &line = lines[i];
-
- // reserve enough space in the flipped text
- vecText flippedText;
- flippedText.reserve(line.m_text.size());
-
- character_t sectionStyle = 0xffff0000; // impossible to achieve
- CStdStringW sectionText;
- for (vecText::iterator it = line.m_text.begin(); it != line.m_text.end(); ++it)
- {
- character_t style = *it & 0xffff0000;
- if (style != sectionStyle)
- {
- if (!sectionText.IsEmpty())
- { // style has changed, bidi flip text
- CStdStringW sectionFlipped = BidiFlip(sectionText, forceLTRReadingOrder);
- for (unsigned int j = 0; j < sectionFlipped.size(); j++)
- flippedText.push_back(sectionStyle | sectionFlipped[j]);
- }
- sectionStyle = style;
- sectionText.clear();
- }
- sectionText.push_back( (wchar_t)(*it & 0xffff) );
- }
-
- // handle the last section
- if (!sectionText.IsEmpty())
- {
- CStdStringW sectionFlipped = BidiFlip(sectionText, forceLTRReadingOrder);
- for (unsigned int j = 0; j < sectionFlipped.size(); j++)
- flippedText.push_back(sectionStyle | sectionFlipped[j]);
- }
-
- // replace the original line with the proccessed one
- lines[i] = CGUIString(flippedText.begin(), flippedText.end(), line.m_carriageReturn);
- }
-}
-
-CStdStringW CGUITextLayout::BidiFlip(const CStdStringW &text, bool forceLTRReadingOrder)
-{
- CStdStringA utf8text;
- CStdStringW visualText;
-
- // convert to utf8, and back to utf16 with bidi flipping
- g_charsetConverter.wToUTF8(text, utf8text);
- g_charsetConverter.utf8ToW(utf8text, visualText, true, forceLTRReadingOrder);
-
- return visualText;
-}
-
-void CGUITextLayout::Filter(CStdString &text)
-{
- CStdStringW utf16;
- utf8ToW(text, utf16);
- vecColors colors;
- vecText parsedText;
- ParseText(utf16, 0, colors, parsedText);
- utf16.Empty();
- for (unsigned int i = 0; i < parsedText.size(); i++)
- utf16 += (wchar_t)(0xffff & parsedText[i]);
- g_charsetConverter.wToUTF8(utf16, text);
-}
-
-void CGUITextLayout::ParseText(const CStdStringW &text, vecText &parsedText)
-{
- if (!m_font)
- return;
- ParseText(text, m_font->GetStyle(), m_colors, parsedText);
-}
-
-void CGUITextLayout::ParseText(const CStdStringW &text, uint32_t defaultStyle, vecColors &colors, vecText &parsedText)
-{
- // run through the string, searching for:
- // [B] or [/B] -> toggle bold on and off
- // [I] or [/I] -> toggle italics on and off
- // [COLOR ffab007f] or [/COLOR] -> toggle color on and off
- // [CAPS <option>] or [/CAPS] -> toggle capatilization on and off
-
- uint32_t currentStyle = defaultStyle; // start with the default font's style
- color_t currentColor = 0;
-
- stack<color_t> colorStack;
- colorStack.push(0);
-
- // these aren't independent, but that's probably not too much of an issue
- // eg [UPPERCASE]Glah[LOWERCASE]FReD[/LOWERCASE]Georeg[/UPPERCASE] will work (lower case >> upper case)
- // but [LOWERCASE]Glah[UPPERCASE]FReD[/UPPERCASE]Georeg[/LOWERCASE] won't
-#define FONT_STYLE_UPPERCASE 4
-#define FONT_STYLE_LOWERCASE 8
-
- int startPos = 0;
- size_t pos = text.Find(L'[');
- while (pos != CStdString::npos && pos + 1 < text.size())
- {
- uint32_t newStyle = 0;
- color_t newColor = currentColor;
- bool newLine = false;
- // have a [ - check if it's an ON or OFF switch
- bool on(true);
- int endPos = pos++; // finish of string
- if (text[pos] == L'/')
- {
- on = false;
- pos++;
- }
- // check for each type
- if (text.Mid(pos,2) == L"B]")
- { // bold - finish the current text block and assign the bold state
- pos += 2;
- if ((on && text.Find(L"[/B]",pos) >= 0) || // check for a matching end point
- (!on && (currentStyle & FONT_STYLE_BOLD))) // or matching start point
- newStyle = FONT_STYLE_BOLD;
- }
- else if (text.Mid(pos,2) == L"I]")
- { // italics
- pos += 2;
- if ((on && text.Find(L"[/I]",pos) >= 0) || // check for a matching end point
- (!on && (currentStyle & FONT_STYLE_ITALICS))) // or matching start point
- newStyle = FONT_STYLE_ITALICS;
- }
- else if (text.Mid(pos,10) == L"UPPERCASE]")
- {
- pos += 10;
- if ((on && text.Find(L"[/UPPERCASE]",pos) >= 0) || // check for a matching end point
- (!on && (currentStyle & FONT_STYLE_UPPERCASE))) // or matching start point
- newStyle = FONT_STYLE_UPPERCASE;
- }
- else if (text.Mid(pos,10) == L"LOWERCASE]")
- {
- pos += 10;
- if ((on && text.Find(L"[/LOWERCASE]",pos) >= 0) || // check for a matching end point
- (!on && (currentStyle & FONT_STYLE_LOWERCASE))) // or matching start point
- newStyle = FONT_STYLE_LOWERCASE;
- }
- else if (text.Mid(pos,3) == L"CR]" && on)
- {
- newLine = true;
- pos += 3;
- }
- else if (text.Mid(pos,5) == L"COLOR")
- { // color
- size_t finish = text.Find(L']', pos + 5);
- if (on && finish != CStdString::npos && (size_t)text.Find(L"[/COLOR]",finish) != CStdString::npos)
- { // create new color
- newColor = colors.size();
- colors.push_back(g_colorManager.GetColor(text.Mid(pos + 5, finish - pos - 5)));
- colorStack.push(newColor);
- }
- else if (!on && finish == pos + 5 && colorStack.size() > 1)
- { // revert to previous color
- colorStack.pop();
- newColor = colorStack.top();
- }
- pos = finish + 1;
- }
-
- if (newStyle || newColor != currentColor || newLine)
- { // we have a new style or a new color, so format up the previous segment
- CStdStringW subText = text.Mid(startPos, endPos - startPos);
- if (currentStyle & FONT_STYLE_UPPERCASE)
- subText.ToUpper();
- if (currentStyle & FONT_STYLE_LOWERCASE)
- subText.ToLower();
- AppendToUTF32(subText, ((currentStyle & 3) << 24) | (currentColor << 16), parsedText);
- if (newLine)
- parsedText.push_back(L'\n');
-
- // and switch to the new style
- startPos = pos;
- currentColor = newColor;
- if (on)
- currentStyle |= newStyle;
- else
- currentStyle &= ~newStyle;
- }
- pos = text.Find(L'[',pos);
- }
- // now grab the remainder of the string
- CStdStringW subText = text.Mid(startPos, text.GetLength() - startPos);
- if (currentStyle & FONT_STYLE_UPPERCASE)
- subText.ToUpper();
- if (currentStyle & FONT_STYLE_LOWERCASE)
- subText.ToLower();
- AppendToUTF32(subText, ((currentStyle & 3) << 24) | (currentColor << 16), parsedText);
-}
-
-void CGUITextLayout::SetMaxHeight(float fHeight)
-{
- m_maxHeight = fHeight;
-}
-
-void CGUITextLayout::WrapText(const vecText &text, float maxWidth)
-{
- if (!m_font)
- return;
-
- int nMaxLines = (m_maxHeight > 0 && m_font->GetLineHeight() > 0)?(int)ceilf(m_maxHeight / m_font->GetLineHeight()):-1;
-
- m_lines.clear();
-
- vector<CGUIString> lines;
- LineBreakText(text, lines);
-
- for (unsigned int i = 0; i < lines.size(); i++)
- {
- const CGUIString &line = lines[i];
- vecText::const_iterator lastSpace = line.m_text.begin();
- vecText::const_iterator pos = line.m_text.begin();
- unsigned int lastSpaceInLine = 0;
- vecText curLine;
- while (pos != line.m_text.end() && (nMaxLines <= 0 || m_lines.size() < (size_t)nMaxLines))
- {
- // Get the current letter in the string
- character_t letter = *pos;
- // check for a space
- if (CanWrapAtLetter(letter))
- {
- float width = m_font->GetTextWidth(curLine);
- if (width > maxWidth)
- {
- if (lastSpace != line.m_text.begin() && lastSpaceInLine > 0)
- {
- CGUIString string(curLine.begin(), curLine.begin() + lastSpaceInLine, false);
- m_lines.push_back(string);
- // skip over spaces
- pos = lastSpace;
- while (pos != line.m_text.end() && IsSpace(*pos))
- pos++;
- curLine.clear();
- lastSpaceInLine = 0;
- lastSpace = line.m_text.begin();
- continue;
- }
- }
- lastSpace = pos;
- lastSpaceInLine = curLine.size();
- }
- curLine.push_back(letter);
- pos++;
- }
- // now add whatever we have left to the string
- float width = m_font->GetTextWidth(curLine);
- if (width > maxWidth)
- {
- // too long - put up to the last space on if we can + remove it from what's left.
- if (lastSpace != line.m_text.begin() && lastSpaceInLine > 0)
- {
- CGUIString string(curLine.begin(), curLine.begin() + lastSpaceInLine, false);
- m_lines.push_back(string);
- curLine.erase(curLine.begin(), curLine.begin() + lastSpaceInLine);
- while (curLine.size() && IsSpace(curLine.at(0)))
- curLine.erase(curLine.begin());
- }
- }
- CGUIString string(curLine.begin(), curLine.end(), true);
- m_lines.push_back(string);
- }
-}
-
-void CGUITextLayout::LineBreakText(const vecText &text, vector<CGUIString> &lines)
-{
- int nMaxLines = (m_maxHeight > 0 && m_font && m_font->GetLineHeight() > 0)?(int)ceilf(m_maxHeight / m_font->GetLineHeight()):-1;
- vecText::const_iterator lineStart = text.begin();
- vecText::const_iterator pos = text.begin();
- while (pos != text.end() && (nMaxLines <= 0 || lines.size() < (size_t)nMaxLines))
- {
- // Get the current letter in the string
- character_t letter = *pos;
-
- // Handle the newline character
- if ((letter & 0xffff) == L'\n' )
- { // push back everything up till now
- CGUIString string(lineStart, pos, true);
- lines.push_back(string);
- lineStart = pos + 1;
- }
- pos++;
- }
-}
-
-void CGUITextLayout::GetTextExtent(float &width, float &height) const
-{
- width = m_textWidth;
- height = m_textHeight;
-}
-
-void CGUITextLayout::CalcTextExtent()
-{
- m_textWidth = 0;
- m_textHeight = 0;
- if (!m_font) return;
-
- for (vector<CGUIString>::iterator i = m_lines.begin(); i != m_lines.end(); i++)
- {
- const CGUIString &string = *i;
- float w = m_font->GetTextWidth(string.m_text);
- if (w > m_textWidth)
- m_textWidth = w;
- }
- m_textHeight = m_font->GetTextHeight(m_lines.size());
-}
-
-unsigned int CGUITextLayout::GetTextLength() const
-{
- unsigned int length = 0;
- for (vector<CGUIString>::const_iterator i = m_lines.begin(); i != m_lines.end(); i++)
- length += i->m_text.size();
- return length;
-}
-
-void CGUITextLayout::GetFirstText(vecText &text) const
-{
- text.clear();
- if (m_lines.size())
- text = m_lines[0].m_text;
-}
-
-float CGUITextLayout::GetTextWidth(const CStdStringW &text) const
-{
- // NOTE: Assumes a single line of text
- if (!m_font) return 0;
- vecText utf32;
- AppendToUTF32(text, (m_font->GetStyle() & 3) << 24, utf32);
- return m_font->GetTextWidth(utf32);
-}
-
-void CGUITextLayout::DrawText(CGUIFont *font, float x, float y, color_t color, color_t shadowColor, const CStdString &text, uint32_t align)
-{
- if (!font) return;
- vecText utf32;
- AppendToUTF32(text, 0, utf32);
- font->DrawText(x, y, color, shadowColor, utf32, align, 0);
-}
-
-void CGUITextLayout::AppendToUTF32(const CStdStringW &utf16, character_t colStyle, vecText &utf32)
-{
- // NOTE: Assumes a single line of text
- utf32.reserve(utf32.size() + utf16.size());
- for (unsigned int i = 0; i < utf16.size(); i++)
- utf32.push_back(utf16[i] | colStyle);
-}
-
-void CGUITextLayout::utf8ToW(const CStdString &utf8, CStdStringW &utf16)
-{
-#ifdef WORK_AROUND_NEEDED_FOR_LINE_BREAKS
- // NOTE: This appears to strip \n characters from text. This may be a consequence of incorrect
- // expression of the \n in utf8 (we just use character code 10) or it might be something
- // more sinister. For now, we use the workaround below.
- CStdStringArray multiLines;
- StringUtils::SplitString(utf8, "\n", multiLines);
- for (unsigned int i = 0; i < multiLines.size(); i++)
- {
- CStdStringW line;
- // no need to bidiflip here - it's done in BidiTransform above
- g_charsetConverter.utf8ToW(multiLines[i], line, false);
- utf16 += line;
- if (i < multiLines.size() - 1)
- utf16.push_back(L'\n');
- }
-#else
- // no need to bidiflip here - it's done in BidiTransform above
- g_charsetConverter.utf8ToW(utf8, utf16, false);
-#endif
-}
-
-void CGUITextLayout::AppendToUTF32(const CStdString &utf8, character_t colStyle, vecText &utf32)
-{
- CStdStringW utf16;
- utf8ToW(utf8, utf16);
- AppendToUTF32(utf16, colStyle, utf32);
-}
-
-void CGUITextLayout::Reset()
-{
- m_lines.clear();
- m_lastText.Empty();
- m_textWidth = m_textHeight = 0;
-}
-
-