ec7b6c27e2f7783c47bb9e296e7b086049f2edad
[vuplus_xbmc] / xbmc / guilib / GUITextBox.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://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 "GUIInfoManager.h"
23 #include "utils/XBMCTinyXML.h"
24 #include "utils/MathUtils.h"
25
26 using namespace std;
27
28 CGUITextBox::CGUITextBox(int parentID, int controlID, float posX, float posY, float width, float height,
29                          const CLabelInfo& labelInfo, int scrollTime)
30     : CGUIControl(parentID, controlID, posX, posY, width, height)
31     , CGUITextLayout(labelInfo.font, true)
32     , m_label(labelInfo)
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 }
49
50 CGUITextBox::CGUITextBox(const CGUITextBox &from)
51 : CGUIControl(from), CGUITextLayout(from)
52 {
53   m_pageControl = from.m_pageControl;
54   m_scrollTime = from.m_scrollTime;
55   m_autoScrollCondition = from.m_autoScrollCondition;
56   m_autoScrollTime = from.m_autoScrollTime;
57   m_autoScrollDelay = from.m_autoScrollDelay;
58   m_autoScrollRepeatAnim = NULL;
59   if (from.m_autoScrollRepeatAnim)
60     m_autoScrollRepeatAnim = new CAnimation(*from.m_autoScrollRepeatAnim);
61   m_label = from.m_label;
62   m_info = from.m_info;
63   // defaults
64   m_offset = 0;
65   m_scrollOffset = 0;
66   m_scrollSpeed = 0;
67   m_itemsPerPage = 10;
68   m_itemHeight = 10;
69   m_lastRenderTime = 0;
70   m_autoScrollDelayTime = 0;
71   ControlType = GUICONTROL_TEXTBOX;
72 }
73
74 CGUITextBox::~CGUITextBox(void)
75 {
76   delete m_autoScrollRepeatAnim;
77   m_autoScrollRepeatAnim = NULL;
78 }
79
80 bool CGUITextBox::UpdateColors()
81 {
82   bool changed = CGUIControl::UpdateColors();
83   changed |= m_label.UpdateColors();
84
85   return changed;
86 }
87
88 void CGUITextBox::UpdateInfo(const CGUIListItem *item)
89 {
90   m_textColor = m_label.textColor;
91   if (!CGUITextLayout::Update(item ? m_info.GetItemLabel(item) : m_info.GetLabel(m_parentID), m_width))
92     return; // nothing changed
93
94   // needed update, so reset to the top of the textbox and update our sizing/page control
95   SetInvalid();
96   m_offset = 0;
97   m_scrollOffset = 0;
98   ResetAutoScrolling();
99
100   m_itemHeight = m_font ? m_font->GetLineHeight() : 10;
101   m_itemsPerPage = (unsigned int)(m_height / m_itemHeight);
102
103   UpdatePageControl();
104 }
105
106 void CGUITextBox::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
107 {
108   CGUIControl::DoProcess(currentTime, dirtyregions);
109
110   // if not visible, we reset the autoscroll timer and positioning
111   if (!IsVisible() && m_autoScrollTime)
112   {
113     ResetAutoScrolling();
114     m_lastRenderTime = 0;
115     m_offset = 0;
116     m_scrollOffset = 0;
117     m_scrollSpeed = 0;
118   }
119 }
120
121 void CGUITextBox::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
122 {
123   // update our auto-scrolling as necessary
124   if (m_autoScrollTime && m_lines.size() > m_itemsPerPage)
125   {
126     if (!m_autoScrollCondition || g_infoManager.GetBoolValue(m_autoScrollCondition))
127     {
128       if (m_lastRenderTime)
129         m_autoScrollDelayTime += currentTime - m_lastRenderTime;
130       if (m_autoScrollDelayTime > (unsigned int)m_autoScrollDelay && m_scrollSpeed == 0)
131       { // delay is finished - start scrolling
132         MarkDirtyRegion();
133         if (m_offset < (int)m_lines.size() - m_itemsPerPage)
134           ScrollToOffset(m_offset + 1, true);
135         else
136         { // at the end, run a delay and restart
137           if (m_autoScrollRepeatAnim)
138           {
139             if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_NONE)
140               m_autoScrollRepeatAnim->QueueAnimation(ANIM_PROCESS_NORMAL);
141             else if (m_autoScrollRepeatAnim->GetState() == ANIM_STATE_APPLIED)
142             { // reset to the start of the list and start the scrolling again
143               m_offset = 0;
144               m_scrollOffset = 0;
145               ResetAutoScrolling();
146             }
147           }
148         }
149       }
150     }
151     else if (m_autoScrollCondition)
152       ResetAutoScrolling();  // conditional is false, so reset the autoscrolling
153   }
154
155   // render the repeat anim as appropriate
156   if (m_autoScrollRepeatAnim)
157   {
158     if (m_autoScrollRepeatAnim->GetProcess() != ANIM_PROCESS_NONE)
159       MarkDirtyRegion();
160     m_autoScrollRepeatAnim->Animate(currentTime, true);
161     TransformMatrix matrix;
162     m_autoScrollRepeatAnim->RenderAnimation(matrix);
163     m_cachedTextMatrix = g_graphicsContext.AddTransform(matrix);
164   }
165
166   // update our scroll position as necessary
167   if (m_scrollSpeed != 0)
168     MarkDirtyRegion();
169
170   if (m_lastRenderTime)
171     m_scrollOffset += m_scrollSpeed * (currentTime - m_lastRenderTime);
172   if ((m_scrollSpeed < 0 && m_scrollOffset < m_offset * m_itemHeight) ||
173       (m_scrollSpeed > 0 && m_scrollOffset > m_offset * m_itemHeight))
174   {
175     m_scrollOffset = m_offset * m_itemHeight;
176     m_scrollSpeed = 0;
177   }
178   m_lastRenderTime = currentTime;
179
180   if (m_pageControl)
181   {
182     CGUIMessage msg(GUI_MSG_ITEM_SELECT, GetID(), m_pageControl, MathUtils::round_int(m_scrollOffset / m_itemHeight));
183     SendWindowMessage(msg);
184   }
185
186   CGUIControl::Process(currentTime, dirtyregions);
187
188   if (m_autoScrollRepeatAnim)
189     g_graphicsContext.RemoveTransform();
190 }
191
192 void CGUITextBox::Render()
193 {
194   // render the repeat anim as appropriate
195   if (m_autoScrollRepeatAnim)
196     g_graphicsContext.SetTransform(m_cachedTextMatrix);
197
198   if (g_graphicsContext.SetClipRegion(m_posX, m_posY, m_width, m_height))
199   {
200     // we offset our draw position to take into account scrolling and whether or not our focused
201     // item is offscreen "above" the list.
202     int offset = (int)(m_scrollOffset / m_itemHeight);
203     float posX = m_posX;
204     float posY = m_posY + offset * m_itemHeight - m_scrollOffset;
205
206     // alignment correction
207     if (m_label.align & XBFONT_CENTER_X)
208       posX += m_width * 0.5f;
209     if (m_label.align & XBFONT_RIGHT)
210       posX += m_width;
211
212     if (m_font)
213     {
214       m_font->Begin();
215       int current = offset;
216       while (posY < m_posY + m_height && current < (int)m_lines.size())
217       {
218         uint32_t align = m_label.align;
219         if (m_lines[current].m_text.size() && m_lines[current].m_carriageReturn)
220           align &= ~XBFONT_JUSTIFIED; // last line of a paragraph shouldn't be justified
221         m_font->DrawText(posX, posY + 2, m_colors, m_label.shadowColor, m_lines[current].m_text, align, m_width);
222         posY += m_itemHeight;
223         current++;
224       }
225       m_font->End();
226     }
227
228     g_graphicsContext.RestoreClipRegion();
229   }
230   if (m_autoScrollRepeatAnim)
231     g_graphicsContext.RemoveTransform();
232   CGUIControl::Render();
233 }
234
235 bool CGUITextBox::OnMessage(CGUIMessage& message)
236 {
237   if (message.GetControlId() == GetID())
238   {
239     if (message.GetMessage() == GUI_MSG_LABEL_SET)
240     {
241       m_offset = 0;
242       m_scrollOffset = 0;
243       ResetAutoScrolling();
244       CGUITextLayout::Reset();
245       m_info.SetLabel(message.GetLabel(), "", GetParentID());
246     }
247
248     if (message.GetMessage() == GUI_MSG_LABEL_RESET)
249     {
250       m_offset = 0;
251       m_scrollOffset = 0;
252       ResetAutoScrolling();
253       CGUITextLayout::Reset();
254       if (m_pageControl)
255       {
256         CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size());
257         SendWindowMessage(msg);
258       }
259     }
260
261     if (message.GetMessage() == GUI_MSG_PAGE_CHANGE)
262     {
263       if (message.GetSenderId() == m_pageControl)
264       { // update our page
265         Scroll(message.GetParam1());
266         return true;
267       }
268     }
269   }
270
271   return CGUIControl::OnMessage(message);
272 }
273
274 void CGUITextBox::UpdatePageControl()
275 {
276   if (m_pageControl)
277   {
278     CGUIMessage msg(GUI_MSG_LABEL_RESET, GetID(), m_pageControl, m_itemsPerPage, m_lines.size());
279     SendWindowMessage(msg);
280   }
281 }
282
283 bool CGUITextBox::CanFocus() const
284 {
285   return false;
286 }
287
288 void CGUITextBox::SetPageControl(int pageControl)
289 {
290   m_pageControl = pageControl;
291 }
292
293 void CGUITextBox::SetInfo(const CGUIInfoLabel &infoLabel)
294 {
295   m_info = infoLabel;
296 }
297
298 void CGUITextBox::Scroll(unsigned int offset)
299 {
300   ResetAutoScrolling();
301   if (m_lines.size() <= m_itemsPerPage)
302     return; // no need to scroll
303   if (offset > m_lines.size() - m_itemsPerPage)
304     offset = m_lines.size() - m_itemsPerPage; // on last page
305   ScrollToOffset(offset);
306 }
307
308 void CGUITextBox::ScrollToOffset(int offset, bool autoScroll)
309 {
310   m_scrollOffset = m_offset * m_itemHeight;
311   int timeToScroll = autoScroll ? m_autoScrollTime : m_scrollTime;
312   m_scrollSpeed = (offset * m_itemHeight - m_scrollOffset) / timeToScroll;
313   m_offset = offset;
314 }
315
316 void CGUITextBox::SetAutoScrolling(const TiXmlNode *node)
317 {
318   if (!node) return;
319   const TiXmlElement *scroll = node->FirstChildElement("autoscroll");
320   if (scroll)
321   {
322     scroll->Attribute("delay", &m_autoScrollDelay);
323     scroll->Attribute("time", &m_autoScrollTime);
324     if (scroll->FirstChild())
325       m_autoScrollCondition = g_infoManager.Register(scroll->FirstChild()->ValueStr(), GetParentID());
326     int repeatTime;
327     if (scroll->Attribute("repeat", &repeatTime))
328       m_autoScrollRepeatAnim = new CAnimation(CAnimation::CreateFader(100, 0, repeatTime, 1000));
329   }
330 }
331
332 void CGUITextBox::ResetAutoScrolling()
333 {
334   m_autoScrollDelayTime = 0;
335   if (m_autoScrollRepeatAnim)
336     m_autoScrollRepeatAnim->ResetAnimation();
337 }
338
339 unsigned int CGUITextBox::GetRows() const
340 {
341   return m_lines.size();
342 }
343
344 int CGUITextBox::GetCurrentPage() const
345 {
346   if (m_offset + m_itemsPerPage >= GetRows())  // last page
347     return (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage;
348   return m_offset / m_itemsPerPage + 1;
349 }
350
351 CStdString CGUITextBox::GetLabel(int info) const
352 {
353   CStdString label;
354   switch (info)
355   {
356   case CONTAINER_NUM_PAGES:
357     label.Format("%u", (GetRows() + m_itemsPerPage - 1) / m_itemsPerPage);
358     break;
359   case CONTAINER_CURRENT_PAGE:
360     label.Format("%u", GetCurrentPage());
361     break;
362   default:
363     break;
364   }
365   return label;
366 }
367
368 void CGUITextBox::UpdateVisibility(const CGUIListItem *item)
369 {
370   // we have to update the page control when we become visible
371   // as another control may be sharing the same page control when we're
372   // not visible
373   bool wasVisible = IsVisible();
374   CGUIControl::UpdateVisibility(item);
375   if (IsVisible() && !wasVisible)
376     UpdatePageControl();
377 }