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 "GUIInfoManager.h"
23 #include "utils/XBMCTinyXML.h"
24 #include "utils/MathUtils.h"
25 #include "utils/StringUtils.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)
40 ControlType = GUICONTROL_TEXTBOX;
43 m_scrollTime = scrollTime;
45 m_autoScrollDelay = 3000;
46 m_autoScrollDelayTime = 0;
47 m_autoScrollRepeatAnim = NULL;
49 m_renderHeight = height;
52 CGUITextBox::CGUITextBox(const CGUITextBox &from)
53 : CGUIControl(from), CGUITextLayout(from)
55 m_pageControl = from.m_pageControl;
56 m_scrollTime = from.m_scrollTime;
57 m_autoScrollCondition = from.m_autoScrollCondition;
58 m_autoScrollTime = from.m_autoScrollTime;
59 m_autoScrollDelay = from.m_autoScrollDelay;
60 m_minHeight = from.m_minHeight;
61 m_renderHeight = from.m_renderHeight;
62 m_autoScrollRepeatAnim = NULL;
63 if (from.m_autoScrollRepeatAnim)
64 m_autoScrollRepeatAnim = new CAnimation(*from.m_autoScrollRepeatAnim);
65 m_label = from.m_label;
74 m_autoScrollDelayTime = 0;
75 ControlType = GUICONTROL_TEXTBOX;
78 CGUITextBox::~CGUITextBox(void)
80 delete m_autoScrollRepeatAnim;
81 m_autoScrollRepeatAnim = NULL;
84 bool CGUITextBox::UpdateColors()
86 bool changed = CGUIControl::UpdateColors();
87 changed |= m_label.UpdateColors();
92 #define CLAMP(x, low, high) (((x) > (high)) ? (high) : (((x) < (low)) ? (low) : (x)))
94 void CGUITextBox::UpdateInfo(const CGUIListItem *item)
96 m_textColor = m_label.textColor;
97 if (!CGUITextLayout::Update(item ? m_info.GetItemLabel(item) : m_info.GetLabel(m_parentID), m_width))
98 return; // nothing changed
100 // needed update, so reset to the top of the textbox and update our sizing/page control
104 ResetAutoScrolling();
106 m_itemHeight = m_font ? m_font->GetLineHeight() : 10;
107 float textHeight = m_font ? m_font->GetTextHeight(m_lines.size()) : m_itemHeight * m_lines.size();
108 float maxHeight = m_height ? m_height : textHeight;
109 m_renderHeight = m_minHeight ? CLAMP(textHeight, m_minHeight, maxHeight) : m_height;
110 m_itemsPerPage = (unsigned int)(m_renderHeight / m_itemHeight);
115 void CGUITextBox::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
117 CGUIControl::DoProcess(currentTime, dirtyregions);
119 // if not visible, we reset the autoscroll timer and positioning
120 if (!IsVisible() && m_autoScrollTime)
122 ResetAutoScrolling();
123 m_lastRenderTime = 0;
130 void CGUITextBox::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
132 // update our auto-scrolling as necessary
133 if (m_autoScrollTime && m_lines.size() > m_itemsPerPage)
135 if (!m_autoScrollCondition || m_autoScrollCondition->Get())
137 if (m_lastRenderTime)
138 m_autoScrollDelayTime += currentTime - m_lastRenderTime;
139 if (m_autoScrollDelayTime > (unsigned int)m_autoScrollDelay && m_scrollSpeed == 0)
140 { // delay is finished - start scrolling
142 if (m_offset < (int)m_lines.size() - m_itemsPerPage)
143 ScrollToOffset(m_offset + 1, true);
145 { // at the end, run a delay and restart
146 if (m_autoScrollRepeatAnim)
148 if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_NONE)
149 m_autoScrollRepeatAnim->QueueAnimation(ANIM_PROCESS_NORMAL);
150 else if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_APPLIED)
151 { // reset to the start of the list and start the scrolling again
154 ResetAutoScrolling();
160 else if (m_autoScrollCondition)
161 ResetAutoScrolling(); // conditional is false, so reset the autoscrolling
164 // render the repeat anim as appropriate
165 if (m_autoScrollRepeatAnim)
167 if (m_autoScrollRepeatAnim->GetProcess() != ANIM_PROCESS_NONE)
169 m_autoScrollRepeatAnim->Animate(currentTime, true);
170 TransformMatrix matrix;
171 m_autoScrollRepeatAnim->RenderAnimation(matrix);
172 m_cachedTextMatrix = g_graphicsContext.AddTransform(matrix);
175 // update our scroll position as necessary
176 if (m_scrollSpeed != 0)
179 if (m_lastRenderTime)
180 m_scrollOffset += m_scrollSpeed * (currentTime - m_lastRenderTime);
181 if ((m_scrollSpeed < 0 && m_scrollOffset < m_offset * m_itemHeight) ||
182 (m_scrollSpeed > 0 && m_scrollOffset > m_offset * m_itemHeight))
184 m_scrollOffset = m_offset * m_itemHeight;
187 m_lastRenderTime = currentTime;
191 CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), m_pageControl, MathUtils::round_int(m_scrollOffset / m_itemHeight));
192 SendWindowMessage(msg);
195 CGUIControl::Process(currentTime, dirtyregions);
197 if (m_autoScrollRepeatAnim)
198 g_graphicsContext.RemoveTransform();
201 void CGUITextBox::Render()
203 // render the repeat anim as appropriate
204 if (m_autoScrollRepeatAnim)
205 g_graphicsContext.SetTransform(m_cachedTextMatrix);
207 if (g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_renderHeight))
209 // we offset our draw position to take into account scrolling and whether or not our focused
210 // item is offscreen "above" the list.
211 int offset = (int)(m_scrollOffset / m_itemHeight);
213 float posY = m_posY + offset * m_itemHeight - m_scrollOffset;
215 // alignment correction
216 if (m_label.align & XBFONT_CENTER_X)
217 posX += m_width * 0.5f;
218 if (m_label.align & XBFONT_RIGHT)
224 int current = offset;
225 while (posY < m_posY + m_renderHeight && current < (int)m_lines.size())
227 uint32_t align = m_label.align;
228 if (m_lines[current].m_text.size() && m_lines[current].m_carriageReturn)
229 align &= ~XBFONT_JUSTIFIED; // last line of a paragraph shouldn't be justified
230 m_font->DrawText(posX, posY + 2, m_colors, m_label.shadowColor, m_lines[current].m_text, align, m_width);
231 posY += m_itemHeight;
237 g_graphicsContext.RestoreClipRegion();
239 if (m_autoScrollRepeatAnim)
240 g_graphicsContext.RemoveTransform();
241 CGUIControl::Render();
244 bool CGUITextBox::OnMessage(CGUIMessage& message)
246 if (message.GetControlId() == GetID())
248 if (message.GetMessage() == GUI_MSG_LABEL_SET)
252 ResetAutoScrolling();
253 CGUITextLayout::Reset();
254 m_info.SetLabel(message.GetLabel(), "", GetParentID());
257 if (message.GetMessage() == GUI_MSG_LABEL_RESET)
261 ResetAutoScrolling();
262 CGUITextLayout::Reset();
265 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size());
266 SendWindowMessage(msg);
270 if (message.GetMessage() == GUI_MSG_PAGE_CHANGE)
272 if (message.GetSenderId() == m_pageControl)
274 Scroll(message.GetParam1());
280 return CGUIControl::OnMessage(message);
283 float CGUITextBox::GetHeight() const
285 return m_renderHeight;
288 void CGUITextBox::SetMinHeight(float minHeight)
290 if (m_minHeight != minHeight)
293 m_minHeight = minHeight;
296 void CGUITextBox::UpdatePageControl()
300 CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size());
301 SendWindowMessage(msg);
305 bool CGUITextBox::CanFocus() const
310 void CGUITextBox::SetPageControl(int pageControl)
312 m_pageControl = pageControl;
315 void CGUITextBox::SetInfo(const CGUIInfoLabel &infoLabel)
320 void CGUITextBox::Scroll(unsigned int offset)
322 ResetAutoScrolling();
323 if (m_lines.size() <= m_itemsPerPage)
324 return; // no need to scroll
325 if (offset > m_lines.size() - m_itemsPerPage)
326 offset = m_lines.size() - m_itemsPerPage; // on last page
327 ScrollToOffset(offset);
330 void CGUITextBox::ScrollToOffset(int offset, bool autoScroll)
332 m_scrollOffset = m_offset * m_itemHeight;
333 int timeToScroll = autoScroll ? m_autoScrollTime : m_scrollTime;
334 m_scrollSpeed = (offset * m_itemHeight - m_scrollOffset) / timeToScroll;
338 void CGUITextBox::SetAutoScrolling(const TiXmlNode *node)
341 const TiXmlElement *scroll = node->FirstChildElement("autoscroll");
344 scroll->Attribute("delay", &m_autoScrollDelay);
345 scroll->Attribute("time", &m_autoScrollTime);
346 if (scroll->FirstChild())
347 m_autoScrollCondition = g_infoManager.Register(scroll->FirstChild()->ValueStr(), GetParentID());
349 if (scroll->Attribute("repeat", &repeatTime))
350 m_autoScrollRepeatAnim = new CAnimation(CAnimation::CreateFader(100, 0, repeatTime, 1000));
354 void CGUITextBox::ResetAutoScrolling()
356 m_autoScrollDelayTime = 0;
357 if (m_autoScrollRepeatAnim)
358 m_autoScrollRepeatAnim->ResetAnimation();
361 unsigned int CGUITextBox::GetRows() const
363 return m_lines.size();
366 int CGUITextBox::GetCurrentPage() const
368 if (m_offset + m_itemsPerPage >= GetRows()) // last page
369 return (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage;
370 return m_offset / m_itemsPerPage + 1;
373 CStdString CGUITextBox::GetLabel(int info) const
378 case CONTAINER_NUM_PAGES:
379 label = StringUtils::Format("%u", (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage);
381 case CONTAINER_CURRENT_PAGE:
382 label = StringUtils::Format("%u", GetCurrentPage());
390 CStdString CGUITextBox::GetDescription() const
395 void CGUITextBox::UpdateVisibility(const CGUIListItem *item)
397 // we have to update the page control when we become visible
398 // as another control may be sharing the same page control when we're
400 bool wasVisible = IsVisible();
401 CGUIControl::UpdateVisibility(item);
402 if (IsVisible() && !wasVisible)