/* * 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::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::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 if (m_colors.size()) m_colors[0] = 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::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, m_colors, 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::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 &lines, bool forceLTRReadingOrder) { for (unsigned int i=0; iGetStyle(), 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