2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
21 #include "GUITextBox.h"
22 #include "utils/CharsetConverter.h"
23 #include "GUIInfoManager.h"
24 #include "utils/XBMCTinyXML.h"
25 #include "utils/MathUtils.h"
29 CGUITextBox::CGUITextBox(int parentID, int controlID, float posX, float posY, float width, float height,
30 const CLabelInfo& labelInfo, int scrollTime)
31 : CGUIControl(parentID, controlID, posX, posY, width, height)
32 , CGUITextLayout(labelInfo.font, true)
39 ControlType = GUICONTROL_TEXTBOX;
42 m_scrollTime = scrollTime;
43 m_autoScrollCondition = 0;
45 m_autoScrollDelay = 3000;
46 m_autoScrollDelayTime = 0;
47 m_autoScrollRepeatAnim = NULL;
51 CGUITextBox::CGUITextBox(const CGUITextBox &from)
52 : CGUIControl(from), CGUITextLayout(from)
54 m_pageControl = from.m_pageControl;
55 m_scrollTime = from.m_scrollTime;
56 m_autoScrollCondition = from.m_autoScrollCondition;
57 m_autoScrollTime = from.m_autoScrollTime;
58 m_autoScrollDelay = from.m_autoScrollDelay;
59 m_autoScrollRepeatAnim = NULL;
60 if (from.m_autoScrollRepeatAnim)
61 m_autoScrollRepeatAnim = new CAnimation(*from.m_autoScrollRepeatAnim);
62 m_label = from.m_label;
71 m_autoScrollDelayTime = 0;
72 ControlType = GUICONTROL_TEXTBOX;
75 CGUITextBox::~CGUITextBox(void)
77 delete m_autoScrollRepeatAnim;
78 m_autoScrollRepeatAnim = NULL;
81 bool CGUITextBox::UpdateColors()
83 bool changed = CGUIControl::UpdateColors();
84 changed |= m_label.UpdateColors();
89 void CGUITextBox::UpdateInfo(const CGUIListItem *item)
91 m_textColor = m_label.textColor;
92 if (!CGUITextLayout::Update(item ? m_info.GetItemLabel(item) : m_info.GetLabel(m_parentID), m_width))
93 return; // nothing changed
95 // needed update, so reset to the top of the textbox and update our sizing/page control
101 m_itemHeight = m_font ? m_font->GetLineHeight() : 10;
102 m_itemsPerPage = (unsigned int)(m_height / m_itemHeight);
107 void CGUITextBox::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
109 CGUIControl::DoProcess(currentTime, dirtyregions);
111 // if not visible, we reset the autoscroll timer and positioning
112 if (!IsVisible() && m_autoScrollTime)
114 ResetAutoScrolling();
115 m_lastRenderTime = 0;
122 void CGUITextBox::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
124 // update our auto-scrolling as necessary
125 if (m_autoScrollTime && m_lines.size() > m_itemsPerPage)
127 if (!m_autoScrollCondition || g_infoManager.GetBoolValue(m_autoScrollCondition))
129 if (m_lastRenderTime)
130 m_autoScrollDelayTime += currentTime - m_lastRenderTime;
131 if (m_autoScrollDelayTime > (unsigned int)m_autoScrollDelay && m_scrollSpeed == 0)
132 { // delay is finished - start scrolling
134 if (m_offset < (int)m_lines.size() - m_itemsPerPage)
135 ScrollToOffset(m_offset + 1, true);
137 { // at the end, run a delay and restart
138 if (m_autoScrollRepeatAnim)
140 if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_NONE)
141 m_autoScrollRepeatAnim->QueueAnimation(ANIM_PROCESS_NORMAL);
142 else if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_APPLIED)
143 { // reset to the start of the list and start the scrolling again
146 ResetAutoScrolling();
152 else if (m_autoScrollCondition)
153 ResetAutoScrolling(); // conditional is false, so reset the autoscrolling
156 // render the repeat anim as appropriate
157 if (m_autoScrollRepeatAnim)
159 if (m_autoScrollRepeatAnim->GetProcess() != ANIM_PROCESS_NONE)
161 m_autoScrollRepeatAnim->Animate(currentTime, true);
162 TransformMatrix matrix;
163 m_autoScrollRepeatAnim->RenderAnimation(matrix);
164 m_cachedTextMatrix = g_graphicsContext.AddTransform(matrix);
167 // update our scroll position as necessary
168 if (m_scrollSpeed != 0)
171 if (m_lastRenderTime)
172 m_scrollOffset += m_scrollSpeed * (currentTime - m_lastRenderTime);
173 if ((m_scrollSpeed < 0 && m_scrollOffset < m_offset * m_itemHeight) ||
174 (m_scrollSpeed > 0 && m_scrollOffset > m_offset * m_itemHeight))
176 m_scrollOffset = m_offset * m_itemHeight;
179 m_lastRenderTime = currentTime;
183 CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), m_pageControl, MathUtils::round_int(m_scrollOffset / m_itemHeight));
184 SendWindowMessage(msg);
187 CGUIControl::Process(currentTime, dirtyregions);
189 if (m_autoScrollRepeatAnim)
190 g_graphicsContext.RemoveTransform();
193 void CGUITextBox::Render()
195 // render the repeat anim as appropriate
196 if (m_autoScrollRepeatAnim)
197 g_graphicsContext.SetTransform(m_cachedTextMatrix);
199 if (g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height))
201 // we offset our draw position to take into account scrolling and whether or not our focused
202 // item is offscreen "above" the list.
203 int offset = (int)(m_scrollOffset / m_itemHeight);
205 float posY = m_posY + offset * m_itemHeight - m_scrollOffset;
207 // alignment correction
208 if (m_label.align & XBFONT_CENTER_X)
209 posX += m_width * 0.5f;
210 if (m_label.align & XBFONT_RIGHT)
216 int current = offset;
217 while (posY < m_posY + m_height && current < (int)m_lines.size())
219 uint32_t align = m_label.align;
220 if (m_lines[current].m_text.size() && m_lines[current].m_carriageReturn)
221 align &= ~XBFONT_JUSTIFIED; // last line of a paragraph shouldn't be justified
222 m_font->DrawText(posX, posY + 2, m_colors, m_label.shadowColor, m_lines[current].m_text, align, m_width);
223 posY += m_itemHeight;
229 g_graphicsContext.RestoreClipRegion();
231 if (m_autoScrollRepeatAnim)
232 g_graphicsContext.RemoveTransform();
233 CGUIControl::Render();
236 bool CGUITextBox::OnMessage(CGUIMessage& message)
238 if (message.GetControlId() == GetID())
240 if (message.GetMessage() == GUI_MSG_LABEL_SET)
244 ResetAutoScrolling();
245 CGUITextLayout::Reset();
246 m_info.SetLabel(message.GetLabel(), "", GetParentID());
249 if (message.GetMessage() == GUI_MSG_LABEL_RESET)
253 ResetAutoScrolling();
254 CGUITextLayout::Reset();
257 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size());
258 SendWindowMessage(msg);
262 if (message.GetMessage() == GUI_MSG_PAGE_CHANGE)
264 if (message.GetSenderId() == m_pageControl)
266 Scroll(message.GetParam1());
272 return CGUIControl::OnMessage(message);
275 void CGUITextBox::UpdatePageControl()
279 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size());
280 SendWindowMessage(msg);
284 bool CGUITextBox::CanFocus() const
289 void CGUITextBox::SetPageControl(int pageControl)
291 m_pageControl = pageControl;
294 void CGUITextBox::SetInfo(const CGUIInfoLabel &infoLabel)
299 void CGUITextBox::Scroll(unsigned int offset)
301 ResetAutoScrolling();
302 if (m_lines.size() <= m_itemsPerPage)
303 return; // no need to scroll
304 if (offset > m_lines.size() - m_itemsPerPage)
305 offset = m_lines.size() - m_itemsPerPage; // on last page
306 ScrollToOffset(offset);
309 void CGUITextBox::ScrollToOffset(int offset, bool autoScroll)
311 m_scrollOffset = m_offset * m_itemHeight;
312 int timeToScroll = autoScroll ? m_autoScrollTime : m_scrollTime;
313 m_scrollSpeed = (offset * m_itemHeight - m_scrollOffset) / timeToScroll;
317 void CGUITextBox::SetAutoScrolling(const TiXmlNode *node)
320 const TiXmlElement *scroll = node->FirstChildElement("autoscroll");
323 scroll->Attribute("delay", &m_autoScrollDelay);
324 scroll->Attribute("time", &m_autoScrollTime);
325 if (scroll->FirstChild())
326 m_autoScrollCondition = g_infoManager.Register(scroll->FirstChild()->ValueStr(), GetParentID());
328 if (scroll->Attribute("repeat", &repeatTime))
329 m_autoScrollRepeatAnim = new CAnimation(CAnimation::CreateFader(100, 0, repeatTime, 1000));
333 void CGUITextBox::ResetAutoScrolling()
335 m_autoScrollDelayTime = 0;
336 if (m_autoScrollRepeatAnim)
337 m_autoScrollRepeatAnim->ResetAnimation();
340 unsigned int CGUITextBox::GetRows() const
342 return m_lines.size();
345 int CGUITextBox::GetCurrentPage() const
347 if (m_offset + m_itemsPerPage >= GetRows()) // last page
348 return (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage;
349 return m_offset / m_itemsPerPage + 1;
352 CStdString CGUITextBox::GetLabel(int info) const
357 case CONTAINER_NUM_PAGES:
358 label.Format("%u", (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage);
360 case CONTAINER_CURRENT_PAGE:
361 label.Format("%u", GetCurrentPage());
369 void CGUITextBox::UpdateVisibility(const CGUIListItem *item)
371 // we have to update the page control when we become visible
372 // as another control may be sharing the same page control when we're
374 bool wasVisible = IsVisible();
375 CGUIControl::UpdateVisibility(item);
376 if (IsVisible() && !wasVisible)