[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / guilib / GUITextBox.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.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 "GUITextBox.h"
22 #include "utils/CharsetConverter.h"
23 #include "GUIInfoManager.h"
24 #include "utils/XBMCTinyXML.h"
25 #include "utils/MathUtils.h"
26
27 using namespace std;
28
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)
33 {
34   m_offset = 0;
35   m_scrollOffset = 0;
36   m_scrollSpeed = 0;
37   m_itemsPerPage = 10;
38   m_itemHeight = 10;
39   ControlType = GUICONTROL_TEXTBOX;
40   m_pageControl = 0;
41   m_lastRenderTime = 0;
42   m_scrollTime = scrollTime;
43   m_autoScrollCondition = 0;
44   m_autoScrollTime = 0;
45   m_autoScrollDelay = 3000;
46   m_autoScrollDelayTime = 0;
47   m_autoScrollRepeatAnim = NULL;
48   m_label = labelInfo;
49 }
50
51 CGUITextBox::CGUITextBox(const CGUITextBox &from)
52 : CGUIControl(from), CGUITextLayout(from)
53 {
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;
63   m_info = from.m_info;
64   // defaults
65   m_offset = 0;
66   m_scrollOffset = 0;
67   m_scrollSpeed = 0;
68   m_itemsPerPage = 10;
69   m_itemHeight = 10;
70   m_lastRenderTime = 0;
71   m_autoScrollDelayTime = 0;
72   ControlType = GUICONTROL_TEXTBOX;
73 }
74
75 CGUITextBox::~CGUITextBox(void)
76 {
77   delete m_autoScrollRepeatAnim;
78   m_autoScrollRepeatAnim = NULL;
79 }
80
81 bool CGUITextBox::UpdateColors()
82 {
83   bool changed = CGUIControl::UpdateColors();
84   changed |= m_label.UpdateColors();
85
86   return changed;
87 }
88
89 void CGUITextBox::UpdateInfo(const CGUIListItem *item)
90 {
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
94
95   // needed update, so reset to the top of the textbox and update our sizing/page control
96   SetInvalid();
97   m_offset = 0;
98   m_scrollOffset = 0;
99   ResetAutoScrolling();
100
101   m_itemHeight = m_font ? m_font->GetLineHeight() : 10;
102   m_itemsPerPage = (unsigned int)(m_height / m_itemHeight);
103
104   UpdatePageControl();
105 }
106
107 void CGUITextBox::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
108 {
109   CGUIControl::DoProcess(currentTime, dirtyregions);
110
111   // if not visible, we reset the autoscroll timer and positioning
112   if (!IsVisible() && m_autoScrollTime)
113   {
114     ResetAutoScrolling();
115     m_lastRenderTime = 0;
116     m_offset = 0;
117     m_scrollOffset = 0;
118     m_scrollSpeed = 0;
119   }
120 }
121
122 void CGUITextBox::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
123 {
124   // update our auto-scrolling as necessary
125   if (m_autoScrollTime && m_lines.size() > m_itemsPerPage)
126   {
127     if (!m_autoScrollCondition || g_infoManager.GetBoolValue(m_autoScrollCondition))
128     {
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
133         MarkDirtyRegion();
134         if (m_offset < (int)m_lines.size() - m_itemsPerPage)
135           ScrollToOffset(m_offset + 1, true);
136         else
137         { // at the end, run a delay and restart
138           if (m_autoScrollRepeatAnim)
139           {
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
144               m_offset = 0;
145               m_scrollOffset = 0;
146               ResetAutoScrolling();
147             }
148           }
149         }
150       }
151     }
152     else if (m_autoScrollCondition)
153       ResetAutoScrolling();  // conditional is false, so reset the autoscrolling
154   }
155
156   // render the repeat anim as appropriate
157   if (m_autoScrollRepeatAnim)
158   {
159     if (m_autoScrollRepeatAnim->GetProcess() != ANIM_PROCESS_NONE)
160       MarkDirtyRegion();
161     m_autoScrollRepeatAnim->Animate(currentTime, true);
162     TransformMatrix matrix;
163     m_autoScrollRepeatAnim->RenderAnimation(matrix);
164     m_cachedTextMatrix = g_graphicsContext.AddTransform(matrix);
165   }
166
167   // update our scroll position as necessary
168   if (m_scrollSpeed != 0)
169     MarkDirtyRegion();
170
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))
175   {
176     m_scrollOffset = m_offset * m_itemHeight;
177     m_scrollSpeed = 0;
178   }
179   m_lastRenderTime = currentTime;
180
181   if (m_pageControl)
182   {
183     CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), m_pageControl, MathUtils::round_int(m_scrollOffset / m_itemHeight));
184     SendWindowMessage(msg);
185   }
186
187   CGUIControl::Process(currentTime, dirtyregions);
188
189   if (m_autoScrollRepeatAnim)
190     g_graphicsContext.RemoveTransform();
191 }
192
193 void CGUITextBox::Render()
194 {
195   // render the repeat anim as appropriate
196   if (m_autoScrollRepeatAnim)
197     g_graphicsContext.SetTransform(m_cachedTextMatrix);
198
199   if (g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height))
200   {
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);
204     float posX = m_posX;
205     float posY = m_posY + offset * m_itemHeight - m_scrollOffset;
206
207     // alignment correction
208     if (m_label.align & XBFONT_CENTER_X)
209       posX += m_width * 0.5f;
210     if (m_label.align & XBFONT_RIGHT)
211       posX += m_width;
212
213     if (m_font)
214     {
215       m_font->Begin();
216       int current = offset;
217       while (posY < m_posY + m_height && current < (int)m_lines.size())
218       {
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;
224         current++;
225       }
226       m_font->End();
227     }
228
229     g_graphicsContext.RestoreClipRegion();
230   }
231   if (m_autoScrollRepeatAnim)
232     g_graphicsContext.RemoveTransform();
233   CGUIControl::Render();
234 }
235
236 bool CGUITextBox::OnMessage(CGUIMessage& message)
237 {
238   if (message.GetControlId() == GetID())
239   {
240     if (message.GetMessage() == GUI_MSG_LABEL_SET)
241     {
242       m_offset = 0;
243       m_scrollOffset = 0;
244       ResetAutoScrolling();
245       CGUITextLayout::Reset();
246       m_info.SetLabel(message.GetLabel(), "", GetParentID());
247     }
248
249     if (message.GetMessage() == GUI_MSG_LABEL_RESET)
250     {
251       m_offset = 0;
252       m_scrollOffset = 0;
253       ResetAutoScrolling();
254       CGUITextLayout::Reset();
255       if (m_pageControl)
256       {
257         CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size());
258         SendWindowMessage(msg);
259       }
260     }
261
262     if (message.GetMessage() == GUI_MSG_PAGE_CHANGE)
263     {
264       if (message.GetSenderId() == m_pageControl)
265       { // update our page
266         Scroll(message.GetParam1());
267         return true;
268       }
269     }
270   }
271
272   return CGUIControl::OnMessage(message);
273 }
274
275 void CGUITextBox::UpdatePageControl()
276 {
277   if (m_pageControl)
278   {
279     CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size());
280     SendWindowMessage(msg);
281   }
282 }
283
284 bool CGUITextBox::CanFocus() const
285 {
286   return false;
287 }
288
289 void CGUITextBox::SetPageControl(int pageControl)
290 {
291   m_pageControl = pageControl;
292 }
293
294 void CGUITextBox::SetInfo(const CGUIInfoLabel &infoLabel)
295 {
296   m_info = infoLabel;
297 }
298
299 void CGUITextBox::Scroll(unsigned int offset)
300 {
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);
307 }
308
309 void CGUITextBox::ScrollToOffset(int offset, bool autoScroll)
310 {
311   m_scrollOffset = m_offset * m_itemHeight;
312   int timeToScroll = autoScroll ? m_autoScrollTime : m_scrollTime;
313   m_scrollSpeed = (offset * m_itemHeight - m_scrollOffset) / timeToScroll;
314   m_offset = offset;
315 }
316
317 void CGUITextBox::SetAutoScrolling(const TiXmlNode *node)
318 {
319   if (!node) return;
320   const TiXmlElement *scroll = node->FirstChildElement("autoscroll");
321   if (scroll)
322   {
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());
327     int repeatTime;
328     if (scroll->Attribute("repeat", &repeatTime))
329       m_autoScrollRepeatAnim = new CAnimation(CAnimation::CreateFader(100, 0, repeatTime, 1000));
330   }
331 }
332
333 void CGUITextBox::ResetAutoScrolling()
334 {
335   m_autoScrollDelayTime = 0;
336   if (m_autoScrollRepeatAnim)
337     m_autoScrollRepeatAnim->ResetAnimation();
338 }
339
340 unsigned int CGUITextBox::GetRows() const
341 {
342   return m_lines.size();
343 }
344
345 int CGUITextBox::GetCurrentPage() const
346 {
347   if (m_offset + m_itemsPerPage >= GetRows())  // last page
348     return (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage;
349   return m_offset / m_itemsPerPage + 1;
350 }
351
352 CStdString CGUITextBox::GetLabel(int info) const
353 {
354   CStdString label;
355   switch (info)
356   {
357   case CONTAINER_NUM_PAGES:
358     label.Format("%u", (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage);
359     break;
360   case CONTAINER_CURRENT_PAGE:
361     label.Format("%u", GetCurrentPage());
362     break;
363   default:
364     break;
365   }
366   return label;
367 }
368
369 void CGUITextBox::UpdateVisibility(const CGUIListItem *item)
370 {
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
373   // not visible
374   bool wasVisible = IsVisible();
375   CGUIControl::UpdateVisibility(item);
376   if (IsVisible() && !wasVisible)
377     UpdatePageControl();
378 }