[guilib] fix labelcontrols with auto width always being marked as dirty if they speci...
[vuplus_xbmc] / xbmc / guilib / GUISelectButtonControl.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 "GUISelectButtonControl.h"
22 #include "GUIWindowManager.h"
23 #include "utils/TimeUtils.h"
24 #include "Key.h"
25
26 CGUISelectButtonControl::CGUISelectButtonControl(int parentID, int controlID,
27     float posX, float posY,
28     float width, float height,
29     const CTextureInfo& buttonFocus,
30     const CTextureInfo& button,
31     const CLabelInfo& labelInfo,
32     const CTextureInfo& selectBackground,
33     const CTextureInfo& selectArrowLeft,
34     const CTextureInfo& selectArrowLeftFocus,
35     const CTextureInfo& selectArrowRight,
36     const CTextureInfo& selectArrowRightFocus
37                                                 )
38     : CGUIButtonControl(parentID, controlID, posX, posY, width, height, buttonFocus, button, labelInfo)
39     , m_imgBackground(posX, posY, width, height, selectBackground)
40     , m_imgLeft(posX, posY, 16, 16, selectArrowLeft)
41     , m_imgLeftFocus(posX, posY, 16, 16, selectArrowLeftFocus)
42     , m_imgRight(posX, posY, 16, 16, selectArrowRight)
43     , m_imgRightFocus(posX, posY, 16, 16, selectArrowRightFocus)
44 {
45   m_bShowSelect = false;
46   m_iCurrentItem = -1;
47   m_iDefaultItem = -1;
48   m_iStartFrame = 0;
49   m_bLeftSelected = false;
50   m_bRightSelected = false;
51   m_bMovedLeft = false;
52   m_bMovedRight = false;
53   m_ticks = 0;
54   m_label.SetAlign(m_label.GetLabelInfo().align | XBFONT_CENTER_X);
55   ControlType = GUICONTROL_SELECTBUTTON;
56 }
57
58 CGUISelectButtonControl::~CGUISelectButtonControl(void)
59 {}
60
61 void CGUISelectButtonControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
62 {
63   if (m_bInvalidated)
64   {
65     m_imgBackground.SetWidth(m_width);
66     m_imgBackground.SetHeight(m_height);
67   }
68   // Are we in selection mode
69   if (m_bShowSelect)
70   {
71     // render background, left and right arrow
72     if (m_imgBackground.Process(currentTime))
73       MarkDirtyRegion();
74
75     CGUILabel::COLOR color = CGUILabel::COLOR_TEXT;
76
77     // User has moved left...
78     if (m_bMovedLeft)
79     {
80       m_iStartFrame++;
81       if (m_iStartFrame >= 10)
82       {
83         m_iStartFrame = 0;
84         m_bMovedLeft = false;
85         MarkDirtyRegion();
86       }
87       // If we are moving left
88       // render item text as disabled
89       color = CGUILabel::COLOR_DISABLED;
90     }
91
92     // Update arrow
93     m_imgLeftFocus.Process(currentTime);
94     m_imgLeft.Process(currentTime);
95
96     // User has moved right...
97     if (m_bMovedRight)
98     {
99       m_iStartFrame++;
100       if (m_iStartFrame >= 10)
101       {
102         m_iStartFrame = 0;
103         m_bMovedRight = false;
104         MarkDirtyRegion();
105       }
106       // If we are moving right
107       // render item text as disabled
108       color = CGUILabel::COLOR_DISABLED;
109     }
110
111     // Update arrow
112     m_imgRightFocus.Process(currentTime);
113     m_imgRight.Process(currentTime);
114
115     // Render text if a current item is available
116     if (m_iCurrentItem >= 0 && (unsigned)m_iCurrentItem < m_vecItems.size())
117     {
118       bool changed = m_label.SetMaxRect(m_posX, m_posY, m_width, m_height);
119       changed |= m_label.SetText(m_vecItems[m_iCurrentItem]);
120       changed |= m_label.SetColor(color);
121       changed |= m_label.Process(currentTime);
122       if (changed)
123         MarkDirtyRegion();
124     }
125
126     // Select current item, if user doesn't
127     // move left or right for 1.5 sec.
128     unsigned int ticksSpan = currentTime - m_ticks;
129     if (ticksSpan > 1500)
130     {
131       // User hasn't moved disable selection mode...
132       m_bShowSelect = false;
133       MarkDirtyRegion();
134
135       // ...and send a thread message.
136       // (Sending a message with SendMessage
137       // can result in a GPF.)
138       CGUIMessage message(GUI_MSG_CLICKED, GetID(), GetParentID() );
139       g_windowManager.SendThreadMessage(message);
140     }
141     CGUIControl::Process(currentTime, dirtyregions);
142   } // if (m_bShowSelect)
143   else
144     CGUIButtonControl::Process(currentTime, dirtyregions);
145 }
146
147 void CGUISelectButtonControl::Render()
148 {
149   if (m_bShowSelect)
150   {
151     // render background, left and right arrow
152     m_imgBackground.Render();
153
154     // Render arrows
155     if (m_bLeftSelected || m_bMovedLeft)
156       m_imgLeftFocus.Render();
157     else
158       m_imgLeft.Render();
159
160     if (m_bRightSelected || m_bMovedRight)
161       m_imgRightFocus.Render();
162     else
163       m_imgRight.Render();
164
165     // Render text if a current item is available
166     if (m_iCurrentItem >= 0 && (unsigned)m_iCurrentItem < m_vecItems.size())
167       m_label.Render();
168
169     CGUIControl::Render();
170   } // if (m_bShowSelect)
171   else
172   {
173     // No, render a normal button
174     CGUIButtonControl::Render();
175   }
176 }
177
178 bool CGUISelectButtonControl::OnMessage(CGUIMessage& message)
179 {
180   if ( message.GetControlId() == GetID() )
181   {
182     if (message.GetMessage() == GUI_MSG_LABEL_ADD)
183     {
184       if (m_vecItems.size() <= 0)
185       {
186         m_iCurrentItem = 0;
187         m_iDefaultItem = 0;
188       }
189       m_vecItems.push_back(message.GetLabel());
190       return true;
191     }
192     else if (message.GetMessage() == GUI_MSG_LABEL_RESET)
193     {
194       m_vecItems.erase(m_vecItems.begin(), m_vecItems.end());
195       m_iCurrentItem = -1;
196       m_iDefaultItem = -1;
197       return true;
198     }
199     else if (message.GetMessage() == GUI_MSG_ITEM_SELECTED)
200     {
201       message.SetParam1(m_iCurrentItem);
202       if (m_iCurrentItem >= 0 && m_iCurrentItem < (int)m_vecItems.size())
203         message.SetLabel(m_vecItems[m_iCurrentItem]);
204       return true;
205     }
206     else if (message.GetMessage() == GUI_MSG_ITEM_SELECT)
207     {
208       m_iDefaultItem = m_iCurrentItem = message.GetParam1();
209       return true;
210     }
211   }
212
213   return CGUIButtonControl::OnMessage(message);
214 }
215
216 bool CGUISelectButtonControl::OnAction(const CAction &action)
217 {
218   if (!m_bShowSelect)
219   {
220     if (action.GetID() == ACTION_SELECT_ITEM)
221     {
222       // Enter selection mode
223       m_bShowSelect = true;
224       SetInvalid();
225
226       // Start timer, if user doesn't select an item
227       // or moves left/right. The control will
228       // automatically select the current item.
229       m_ticks = CTimeUtils::GetFrameTime();
230       return true;
231     }
232     else
233       return CGUIButtonControl::OnAction(action);
234   }
235   else
236   {
237     if (action.GetID() == ACTION_SELECT_ITEM)
238     {
239       // User has selected an item, disable selection mode...
240       m_bShowSelect = false;
241       SetInvalid();
242
243       // ...and send a message.
244       CGUIMessage message(GUI_MSG_CLICKED, GetID(), GetParentID() );
245       SendWindowMessage(message);
246       return true;
247     }
248     if (action.GetID() == ACTION_MOVE_UP || action.GetID() == ACTION_MOVE_DOWN )
249     {
250       // Disable selection mode when moving up or down
251       m_bShowSelect = false;
252       m_iCurrentItem = m_iDefaultItem;
253       SetInvalid();
254     }
255     // call the base class
256     return CGUIButtonControl::OnAction(action);
257   }
258 }
259
260 void CGUISelectButtonControl::FreeResources(bool immediately)
261 {
262   CGUIButtonControl::FreeResources(immediately);
263
264   m_imgBackground.FreeResources(immediately);
265
266   m_imgLeft.FreeResources(immediately);
267   m_imgLeftFocus.FreeResources(immediately);
268
269   m_imgRight.FreeResources(immediately);
270   m_imgRightFocus.FreeResources(immediately);
271
272   m_bShowSelect = false;
273 }
274
275 void CGUISelectButtonControl::DynamicResourceAlloc(bool bOnOff)
276 {
277   CGUIControl::DynamicResourceAlloc(bOnOff);
278
279   m_imgBackground.DynamicResourceAlloc(bOnOff);
280
281   m_imgLeft.DynamicResourceAlloc(bOnOff);
282   m_imgLeftFocus.DynamicResourceAlloc(bOnOff);
283
284   m_imgRight.DynamicResourceAlloc(bOnOff);
285   m_imgRightFocus.DynamicResourceAlloc(bOnOff);
286 }
287
288 void CGUISelectButtonControl::AllocResources()
289 {
290   CGUIButtonControl::AllocResources();
291
292   m_imgBackground.AllocResources();
293
294   m_imgLeft.AllocResources();
295   m_imgLeftFocus.AllocResources();
296
297   m_imgRight.AllocResources();
298   m_imgRightFocus.AllocResources();
299
300   // Position right arrow
301   float posX = (m_posX + m_width - 8) - 16;
302   float posY = m_posY + (m_height - 16) / 2;
303   m_imgRight.SetPosition(posX, posY);
304   m_imgRightFocus.SetPosition(posX, posY);
305
306   // Position left arrow
307   posX = m_posX + 8;
308   m_imgLeft.SetPosition(posX, posY);
309   m_imgLeftFocus.SetPosition(posX, posY);
310 }
311
312 void CGUISelectButtonControl::SetInvalid()
313 {
314   CGUIButtonControl::SetInvalid();
315   m_imgBackground.SetInvalid();
316   m_imgLeft.SetInvalid();
317   m_imgLeftFocus.SetInvalid();
318   m_imgRight.SetInvalid();
319   m_imgRightFocus.SetInvalid();
320 }
321
322 void CGUISelectButtonControl::OnLeft()
323 {
324   if (m_bShowSelect)
325   {
326     // Set for visual feedback
327     m_bMovedLeft = true;
328     m_iStartFrame = 0;
329     SetInvalid();
330
331     // Reset timer for automatically selecting
332     // the current item.
333     m_ticks = CTimeUtils::GetFrameTime();
334
335     // Switch to previous item
336     if (m_vecItems.size() > 0)
337     {
338       m_iCurrentItem--;
339       if (m_iCurrentItem < 0)
340         m_iCurrentItem = (int)m_vecItems.size() - 1;
341     }
342   }
343   else
344   { // use the base class
345     CGUIButtonControl::OnLeft();
346   }
347 }
348
349 void CGUISelectButtonControl::OnRight()
350 {
351   if (m_bShowSelect)
352   {
353     // Set for visual feedback
354     m_bMovedRight = true;
355     m_iStartFrame = 0;
356     SetInvalid();
357
358     // Reset timer for automatically selecting
359     // the current item.
360     m_ticks = CTimeUtils::GetFrameTime();
361
362     // Switch to next item
363     if (m_vecItems.size() > 0)
364     {
365       m_iCurrentItem++;
366       if (m_iCurrentItem >= (int)m_vecItems.size())
367         m_iCurrentItem = 0;
368     }
369   }
370   else
371   { // use the base class
372     CGUIButtonControl::OnRight();
373   }
374 }
375
376 bool CGUISelectButtonControl::OnMouseOver(const CPoint &point)
377 {
378   bool ret = CGUIControl::OnMouseOver(point);
379   m_bLeftSelected = false;
380   m_bRightSelected = false;
381   if (m_imgLeft.HitTest(point))
382   { // highlight the left control, but don't start moving until we have clicked
383     m_bLeftSelected = true;
384   }
385   if (m_imgRight.HitTest(point))
386   { // highlight the right control, but don't start moving until we have clicked
387     m_bRightSelected = true;
388   }
389   // reset ticks
390   m_ticks = CTimeUtils::GetFrameTime();
391   return ret;
392 }
393
394 EVENT_RESULT CGUISelectButtonControl::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
395 {
396   if (event.m_id == ACTION_MOUSE_LEFT_CLICK)
397   {
398     if (m_bShowSelect && m_imgLeft.HitTest(point))
399       OnLeft();
400     else if (m_bShowSelect && m_imgRight.HitTest(point))
401       OnRight();
402     else // normal select
403       CGUIButtonControl::OnMouseEvent(point, event);
404     return EVENT_RESULT_HANDLED;
405   }
406   else if (event.m_id == ACTION_MOUSE_WHEEL_UP)
407   {
408     OnLeft();
409     return EVENT_RESULT_HANDLED;
410   }
411   else if (event.m_id == ACTION_MOUSE_WHEEL_DOWN)
412   {
413     OnRight();
414     return EVENT_RESULT_HANDLED;
415   }
416   return EVENT_RESULT_UNHANDLED;
417 }
418
419 void CGUISelectButtonControl::SetPosition(float posX, float posY)
420 {
421   float leftOffX = m_imgLeft.GetXPosition() - m_posX;
422   float leftOffY = m_imgLeft.GetYPosition() - m_posY;
423   float rightOffX = m_imgRight.GetXPosition() - m_posX;
424   float rightOffY = m_imgRight.GetYPosition() - m_posY;
425   float backOffX = m_imgBackground.GetXPosition() - m_posX;
426   float backOffY = m_imgBackground.GetYPosition() - m_posY;
427   CGUIButtonControl::SetPosition(posX, posY);
428   m_imgLeft.SetPosition(posX + leftOffX, posY + leftOffY);
429   m_imgLeftFocus.SetPosition(posX + leftOffX, posY + leftOffY);
430   m_imgRight.SetPosition(posX + rightOffX, posY + rightOffY);
431   m_imgRightFocus.SetPosition(posX + rightOffX, posY + rightOffY);
432   m_imgBackground.SetPosition(posX + backOffX, posY + backOffY);
433 }
434
435 bool CGUISelectButtonControl::UpdateColors()
436 {
437   bool changed = CGUIButtonControl::UpdateColors();
438   changed |= m_imgLeft.SetDiffuseColor(m_diffuseColor);
439   changed |= m_imgLeftFocus.SetDiffuseColor(m_diffuseColor);
440   changed |= m_imgRight.SetDiffuseColor(m_diffuseColor);
441   changed |= m_imgRightFocus.SetDiffuseColor(m_diffuseColor);
442   changed |= m_imgBackground.SetDiffuseColor(m_diffuseColor);
443
444   return changed;
445 }
446