reset initial visible state to visible if it has been set to delayed during a previou...
[vuplus_xbmc] / xbmc / guilib / GUIControl.cpp
1 /*
2  *      Copyright (C) 2005-2012 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 "GUIControl.h"
22
23 #include "GUIInfoManager.h"
24 #include "utils/log.h"
25 #include "LocalizeStrings.h"
26 #include "GUIWindowManager.h"
27 #include "GUIControlProfiler.h"
28 #include "input/MouseStat.h"
29 #include "Key.h"
30
31 using namespace std;
32
33 CGUIControl::CGUIControl()
34 {
35   m_hasProcessed = false;
36   m_bHasFocus = false;
37   m_controlID = 0;
38   m_parentID = 0;
39   m_visible = VISIBLE;
40   m_visibleFromSkinCondition = true;
41   m_forceHidden = false;
42   m_visibleCondition = 0;
43   m_enableCondition = 0;
44   m_enabled = true;
45   m_diffuseColor = 0xffffffff;
46   m_posX = 0;
47   m_posY = 0;
48   m_width = 0;
49   m_height = 0;
50   ControlType = GUICONTROL_UNKNOWN;
51   m_bInvalidated = true;
52   m_bAllocated=false;
53   m_parentControl = NULL;
54   m_hasCamera = false;
55   m_pushedUpdates = false;
56   m_pulseOnSelect = false;
57   m_controlIsDirty = true;
58 }
59
60 CGUIControl::CGUIControl(int parentID, int controlID, float posX, float posY, float width, float height)
61 : m_hitRect(posX, posY, posX + width, posY + height)
62 {
63   m_posX = posX;
64   m_posY = posY;
65   m_width = width;
66   m_height = height;
67   m_bHasFocus = false;
68   m_controlID = controlID;
69   m_parentID = parentID;
70   m_visible = VISIBLE;
71   m_visibleFromSkinCondition = true;
72   m_diffuseColor = 0xffffffff;
73   m_forceHidden = false;
74   m_visibleCondition = 0;
75   m_enableCondition = 0;
76   m_enabled = true;
77   ControlType = GUICONTROL_UNKNOWN;
78   m_bInvalidated = true;
79   m_bAllocated=false;
80   m_hasProcessed = false;
81   m_parentControl = NULL;
82   m_hasCamera = false;
83   m_pushedUpdates = false;
84   m_pulseOnSelect = false;
85   m_controlIsDirty = false;
86 }
87
88
89 CGUIControl::~CGUIControl(void)
90 {
91
92 }
93
94 void CGUIControl::AllocResources()
95 {
96   m_hasProcessed = false;
97   m_bInvalidated = true;
98   m_bAllocated=true;
99 }
100
101 void CGUIControl::FreeResources(bool immediately)
102 {
103   if (m_bAllocated)
104   {
105     // Reset our animation states - not conditional anims though.
106     // I'm not sure if this is needed for most cases anyway.  I believe it's only here
107     // because some windows aren't loaded on demand
108     for (unsigned int i = 0; i < m_animations.size(); i++)
109     {
110       CAnimation &anim = m_animations[i];
111       if (anim.GetType() != ANIM_TYPE_CONDITIONAL)
112         anim.ResetAnimation();
113     }
114     m_bAllocated=false;
115   }
116   m_hasProcessed = false;
117 }
118
119 void CGUIControl::DynamicResourceAlloc(bool bOnOff)
120 {
121
122 }
123
124 // the main processing routine.
125 // 1. animate and set animation transform
126 // 2. if visible, process
127 // 3. reset the animation transform
128 void CGUIControl::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
129 {
130   CRect dirtyRegion = m_renderRegion;
131
132   bool changed = m_bInvalidated && IsVisible();
133
134   changed |= Animate(currentTime);
135
136   m_cachedTransform = g_graphicsContext.AddTransform(m_transform);
137   if (m_hasCamera)
138     g_graphicsContext.SetCameraPosition(m_camera);
139
140   if (IsVisible())
141   {
142     Process(currentTime, dirtyregions);
143     m_bInvalidated = false;
144   }
145
146   changed |=  m_controlIsDirty;
147
148   if (changed || dirtyRegion != m_renderRegion)
149   {
150     dirtyRegion.Union(m_renderRegion);
151     dirtyregions.push_back(dirtyRegion);
152   }
153
154   if (m_hasCamera)
155     g_graphicsContext.RestoreCameraPosition();
156   g_graphicsContext.RemoveTransform();
157
158   m_controlIsDirty = false;
159 }
160
161 void CGUIControl::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
162 {
163   // update our render region
164   m_renderRegion = g_graphicsContext.generateAABB(CalcRenderRegion());
165   m_hasProcessed = true;
166 }
167
168 // the main render routine.
169 // 1. set the animation transform
170 // 2. if visible, paint
171 // 3. reset the animation transform
172 void CGUIControl::DoRender()
173 {
174   g_graphicsContext.SetTransform(m_cachedTransform);
175   if (m_hasCamera)
176     g_graphicsContext.SetCameraPosition(m_camera);
177   if (IsVisible())
178   {
179     GUIPROFILER_RENDER_BEGIN(this);
180     Render();
181     GUIPROFILER_RENDER_END(this);
182   }
183   if (m_hasCamera)
184     g_graphicsContext.RestoreCameraPosition();
185   g_graphicsContext.RemoveTransform();
186 }
187
188 void CGUIControl::Render()
189 {
190 }
191
192 bool CGUIControl::OnAction(const CAction &action)
193 {
194   if (HasFocus())
195   {
196     switch (action.GetID())
197     {
198     case ACTION_MOVE_DOWN:
199       OnDown();
200       return true;
201
202     case ACTION_MOVE_UP:
203       OnUp();
204       return true;
205
206     case ACTION_MOVE_LEFT:
207       OnLeft();
208       return true;
209
210     case ACTION_MOVE_RIGHT:
211       OnRight();
212       return true;
213
214     case ACTION_NAV_BACK:
215       return OnBack();
216
217     case ACTION_NEXT_CONTROL:
218       OnNextControl();
219       return true;
220
221     case ACTION_PREV_CONTROL:
222       OnPrevControl();
223       return true;
224     }
225   }
226   return false;
227 }
228
229 bool CGUIControl::Navigate(int direction)
230 {
231   if (HasFocus())
232   {
233     CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), direction);
234     return SendWindowMessage(msg);
235   }
236   return false;
237 }
238
239 // Movement controls (derived classes can override)
240 void CGUIControl::OnUp()
241 {
242   Navigate(ACTION_MOVE_UP);
243 }
244
245 void CGUIControl::OnDown()
246 {
247   Navigate(ACTION_MOVE_DOWN);
248 }
249
250 void CGUIControl::OnLeft()
251 {
252   Navigate(ACTION_MOVE_LEFT);
253 }
254
255 void CGUIControl::OnRight()
256 {
257   Navigate(ACTION_MOVE_RIGHT);
258 }
259
260 bool CGUIControl::OnBack()
261 {
262   return Navigate(ACTION_NAV_BACK);
263 }
264
265 void CGUIControl::OnNextControl()
266 {
267   Navigate(ACTION_NEXT_CONTROL);
268 }
269
270 void CGUIControl::OnPrevControl()
271 {
272   Navigate(ACTION_PREV_CONTROL);
273 }
274
275 bool CGUIControl::SendWindowMessage(CGUIMessage &message)
276 {
277   CGUIWindow *pWindow = g_windowManager.GetWindow(GetParentID());
278   if (pWindow)
279     return pWindow->OnMessage(message);
280   return g_windowManager.SendMessage(message);
281 }
282
283 int CGUIControl::GetID(void) const
284 {
285   return m_controlID;
286 }
287
288
289 int CGUIControl::GetParentID(void) const
290 {
291   return m_parentID;
292 }
293
294 bool CGUIControl::HasFocus(void) const
295 {
296   return m_bHasFocus;
297 }
298
299 void CGUIControl::SetFocus(bool focus)
300 {
301   if (m_bHasFocus && !focus)
302     QueueAnimation(ANIM_TYPE_UNFOCUS);
303   else if (!m_bHasFocus && focus)
304     QueueAnimation(ANIM_TYPE_FOCUS);
305   m_bHasFocus = focus;
306 }
307
308 bool CGUIControl::OnMessage(CGUIMessage& message)
309 {
310   if ( message.GetControlId() == GetID() )
311   {
312     switch (message.GetMessage() )
313     {
314     case GUI_MSG_SETFOCUS:
315       // if control is disabled then move 2 the next control
316       if ( !CanFocus() )
317       {
318         CLog::Log(LOGERROR, "Control %u in window %u has been asked to focus, "
319                             "but it can't",
320                   GetID(), GetParentID());
321         return false;
322       }
323       SetFocus(true);
324       {
325         // inform our parent window that this has happened
326         CGUIMessage message(GUI_MSG_FOCUSED, GetParentID(), GetID());
327         if (m_parentControl)
328           m_parentControl->OnMessage(message);
329       }
330       return true;
331       break;
332
333     case GUI_MSG_LOSTFOCUS:
334       {
335         SetFocus(false);
336         // and tell our parent so it can unfocus
337         if (m_parentControl)
338           m_parentControl->OnMessage(message);
339         return true;
340       }
341       break;
342
343     case GUI_MSG_VISIBLE:
344       SetVisible(true, true);
345       return true;
346       break;
347
348     case GUI_MSG_HIDDEN:
349       SetVisible(false);
350       return true;
351
352       // Note that the skin <enable> tag will override these messages
353     case GUI_MSG_ENABLED:
354       SetEnabled(true);
355       return true;
356
357     case GUI_MSG_DISABLED:
358       SetEnabled(false);
359       return true;
360
361     case GUI_MSG_WINDOW_RESIZE:
362       // invalidate controls to get them to recalculate sizing information
363       SetInvalid();
364       return true;
365     }
366   }
367   return false;
368 }
369
370 bool CGUIControl::CanFocus() const
371 {
372   if (!IsVisible() && !m_allowHiddenFocus) return false;
373   if (IsDisabled()) return false;
374   return true;
375 }
376
377 bool CGUIControl::IsVisible() const
378 {
379   if (m_forceHidden) return false;
380   return m_visible == VISIBLE;
381 }
382
383 bool CGUIControl::IsDisabled() const
384 {
385   return !m_enabled;
386 }
387
388 void CGUIControl::SetEnabled(bool bEnable)
389 {
390   if (bEnable != m_enabled)
391   {
392     m_enabled = bEnable;
393     SetInvalid();
394   }
395 }
396
397 void CGUIControl::SetEnableCondition(const CStdString &expression)
398 {
399   if (expression == "true")
400     m_enabled = true;
401   else if (expression == "false")
402     m_enabled = false;
403   else
404     m_enableCondition = g_infoManager.Register(expression, GetParentID());
405 }
406
407 void CGUIControl::SetPosition(float posX, float posY)
408 {
409   if ((m_posX != posX) || (m_posY != posY))
410   {
411     MarkDirtyRegion();
412
413     m_hitRect += CPoint(posX - m_posX, posY - m_posY);
414     m_posX = posX;
415     m_posY = posY;
416
417     SetInvalid();
418   }
419 }
420
421 bool CGUIControl::SetColorDiffuse(const CGUIInfoColor &color)
422 {
423   bool changed = m_diffuseColor != color;
424   m_diffuseColor = color;
425   return changed;
426 }
427
428 float CGUIControl::GetXPosition() const
429 {
430   return m_posX;
431 }
432
433 float CGUIControl::GetYPosition() const
434 {
435   return m_posY;
436 }
437
438 float CGUIControl::GetWidth() const
439 {
440   return m_width;
441 }
442
443 float CGUIControl::GetHeight() const
444 {
445   return m_height;
446 }
447
448 void CGUIControl::MarkDirtyRegion()
449 {
450   m_controlIsDirty = true;
451 }
452
453 CRect CGUIControl::CalcRenderRegion() const
454 {
455   CPoint tl(GetXPosition(), GetYPosition());
456   CPoint br(tl.x + GetWidth(), tl.y + GetHeight());
457
458   return CRect(tl.x, tl.y, br.x, br.y);
459 }
460
461 void CGUIControl::SetNavigation(int up, int down, int left, int right, int back)
462 {
463   m_actionUp.SetNavigation(up);
464   m_actionDown.SetNavigation(down);
465   m_actionLeft.SetNavigation(left);
466   m_actionRight.SetNavigation(right);
467   m_actionBack.SetNavigation(back);
468 }
469
470 void CGUIControl::SetTabNavigation(int next, int prev)
471 {
472   m_actionNext.SetNavigation(next);
473   m_actionPrev.SetNavigation(prev);
474 }
475
476 void CGUIControl::SetNavigationActions(const CGUIAction &up, const CGUIAction &down,
477                                        const CGUIAction &left, const CGUIAction &right,
478                                        const CGUIAction &back, bool replace)
479 {
480   if (!m_actionLeft.HasAnyActions()  || replace) m_actionLeft  = left;
481   if (!m_actionRight.HasAnyActions() || replace) m_actionRight = right;
482   if (!m_actionUp.HasAnyActions()    || replace) m_actionUp    = up;
483   if (!m_actionDown.HasAnyActions()  || replace) m_actionDown  = down;
484   if (!m_actionBack.HasAnyActions()  || replace) m_actionBack  = back;
485 }
486
487 void CGUIControl::SetNavigationAction(int direction, const CGUIAction &action, bool replace /*= true*/)
488 {
489   switch (direction)
490   {
491   case ACTION_MOVE_UP:
492     if (!m_actionUp.HasAnyActions() || replace)
493       m_actionUp = action;
494     break;
495   case ACTION_MOVE_DOWN:
496     if (!m_actionDown.HasAnyActions() || replace)
497       m_actionDown = action;
498     break;
499   case ACTION_MOVE_LEFT:
500     if (!m_actionLeft.HasAnyActions() || replace)
501       m_actionLeft = action;
502     break;
503   case ACTION_MOVE_RIGHT:
504     if (!m_actionRight.HasAnyActions() || replace)
505       m_actionRight = action;
506     break;
507   case ACTION_NAV_BACK:
508     if (!m_actionBack.HasAnyActions() || replace)
509       m_actionBack = action;
510     break;
511   }
512 }
513
514 void CGUIControl::SetWidth(float width)
515 {
516   if (m_width != width)
517   {
518     MarkDirtyRegion();
519     m_width = width;
520     m_hitRect.x2 = m_hitRect.x1 + width;
521     SetInvalid();
522   }
523 }
524
525 void CGUIControl::SetHeight(float height)
526 {
527   if (m_height != height)
528   {
529     MarkDirtyRegion();
530     m_height = height;
531     m_hitRect.y2 = m_hitRect.y1 + height;
532     SetInvalid();
533   }
534 }
535
536 void CGUIControl::SetVisible(bool bVisible, bool setVisState)
537 {
538   if (bVisible && setVisState)
539   {  // TODO: currently we only update m_visible from GUI_MSG_VISIBLE (SET_CONTROL_VISIBLE)
540      //       otherwise we just set m_forceHidden
541     GUIVISIBLE visible = m_visible;
542     if (m_visibleCondition)
543       visible = g_infoManager.GetBoolValue(m_visibleCondition) ? VISIBLE : HIDDEN;
544     else
545       visible = VISIBLE;
546     if (visible != m_visible)
547     {
548       m_visible = visible;
549       SetInvalid();
550     }
551   }
552   if (m_forceHidden == bVisible)
553   {
554     m_forceHidden = !bVisible;
555     SetInvalid();
556   }
557   if (m_forceHidden)
558   { // reset any visible animations that are in process
559     if (IsAnimating(ANIM_TYPE_VISIBLE))
560     {
561 //        CLog::Log(LOGDEBUG, "Resetting visible animation on control %i (we are %s)", m_controlID, m_visible ? "visible" : "hidden");
562       CAnimation *visibleAnim = GetAnimation(ANIM_TYPE_VISIBLE);
563       if (visibleAnim) visibleAnim->ResetAnimation();
564     }
565   }
566 }
567
568 bool CGUIControl::HitTest(const CPoint &point) const
569 {
570   return m_hitRect.PtInRect(point);
571 }
572
573 EVENT_RESULT CGUIControl::SendMouseEvent(const CPoint &point, const CMouseEvent &event)
574 {
575   CPoint childPoint(point);
576   m_transform.InverseTransformPosition(childPoint.x, childPoint.y);
577   if (!CanFocusFromPoint(childPoint))
578     return EVENT_RESULT_UNHANDLED;
579
580   bool handled = OnMouseOver(childPoint);
581   EVENT_RESULT ret = OnMouseEvent(childPoint, event);
582   if (ret)
583     return ret;
584   return (handled && (event.m_id == ACTION_MOUSE_MOVE)) ? EVENT_RESULT_HANDLED : EVENT_RESULT_UNHANDLED;
585 }
586
587 // override this function to implement custom mouse behaviour
588 bool CGUIControl::OnMouseOver(const CPoint &point)
589 {
590   if (g_Mouse.GetState() != MOUSE_STATE_DRAG)
591     g_Mouse.SetState(MOUSE_STATE_FOCUS);
592   if (!CanFocus()) return false;
593   CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), GetID());
594   OnMessage(msg);
595   return true;
596 }
597
598 void CGUIControl::UpdateVisibility(const CGUIListItem *item)
599 {
600   if (m_visibleCondition)
601   {
602     bool bWasVisible = m_visibleFromSkinCondition;
603     m_visibleFromSkinCondition = g_infoManager.GetBoolValue(m_visibleCondition, item);
604     if (!bWasVisible && m_visibleFromSkinCondition)
605     { // automatic change of visibility - queue the in effect
606   //    CLog::Log(LOGDEBUG, "Visibility changed to visible for control id %i", m_controlID);
607       QueueAnimation(ANIM_TYPE_VISIBLE);
608     }
609     else if (bWasVisible && !m_visibleFromSkinCondition)
610     { // automatic change of visibility - do the out effect
611   //    CLog::Log(LOGDEBUG, "Visibility changed to hidden for control id %i", m_controlID);
612       QueueAnimation(ANIM_TYPE_HIDDEN);
613     }
614   }
615   // check for conditional animations
616   for (unsigned int i = 0; i < m_animations.size(); i++)
617   {
618     CAnimation &anim = m_animations[i];
619     if (anim.GetType() == ANIM_TYPE_CONDITIONAL)
620       anim.UpdateCondition(item);
621   }
622   // and check for conditional enabling - note this overrides SetEnabled() from the code currently
623   // this may need to be reviewed at a later date
624   bool enabled = m_enabled;
625   if (m_enableCondition)
626     m_enabled = g_infoManager.GetBoolValue(m_enableCondition, item);
627
628   if (m_enabled != enabled)
629     MarkDirtyRegion();
630
631   m_allowHiddenFocus.Update(item);
632   if (UpdateColors())
633     MarkDirtyRegion();
634   // and finally, update our control information (if not pushed)
635   if (!m_pushedUpdates)
636     UpdateInfo(item);
637 }
638
639 bool CGUIControl::UpdateColors()
640 {
641   return m_diffuseColor.Update();
642 }
643
644 void CGUIControl::SetInitialVisibility()
645 {
646   if (m_visibleCondition)
647   {
648     m_visibleFromSkinCondition = g_infoManager.GetBoolValue(m_visibleCondition);
649     m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
650   //  CLog::Log(LOGDEBUG, "Set initial visibility for control %i: %s", m_controlID, m_visible == VISIBLE ? "visible" : "hidden");
651   }
652   else if (m_visible == DELAYED)
653     m_visible = VISIBLE;
654   // and handle animation conditions as well
655   for (unsigned int i = 0; i < m_animations.size(); i++)
656   {
657     CAnimation &anim = m_animations[i];
658     if (anim.GetType() == ANIM_TYPE_CONDITIONAL)
659       anim.SetInitialCondition();
660   }
661   // and check for conditional enabling - note this overrides SetEnabled() from the code currently
662   // this may need to be reviewed at a later date
663   if (m_enableCondition)
664     m_enabled = g_infoManager.GetBoolValue(m_enableCondition);
665   m_allowHiddenFocus.Update();
666   UpdateColors();
667
668   MarkDirtyRegion();
669 }
670
671 void CGUIControl::SetVisibleCondition(const CStdString &expression, const CStdString &allowHiddenFocus)
672 {
673   if (expression == "true")
674     m_visible = VISIBLE;
675   else if (expression == "false")
676     m_visible = HIDDEN;
677   else  // register with the infomanager for updates
678     m_visibleCondition = g_infoManager.Register(expression, GetParentID());
679   m_allowHiddenFocus.Parse(allowHiddenFocus, GetParentID());
680 }
681
682 void CGUIControl::SetAnimations(const vector<CAnimation> &animations)
683 {
684   m_animations = animations;
685   MarkDirtyRegion();
686 }
687
688 void CGUIControl::ResetAnimation(ANIMATION_TYPE type)
689 {
690   MarkDirtyRegion();
691
692   for (unsigned int i = 0; i < m_animations.size(); i++)
693   {
694     if (m_animations[i].GetType() == type)
695       m_animations[i].ResetAnimation();
696   }
697 }
698
699 void CGUIControl::ResetAnimations()
700 {
701   MarkDirtyRegion();
702
703   for (unsigned int i = 0; i < m_animations.size(); i++)
704     m_animations[i].ResetAnimation();
705
706   MarkDirtyRegion();
707 }
708
709 bool CGUIControl::CheckAnimation(ANIMATION_TYPE animType)
710 {
711   // rule out the animations we shouldn't perform
712   if (!IsVisible() || !HasProcessed())
713   { // hidden or never processed - don't allow exit or entry animations for this control
714     if (animType == ANIM_TYPE_WINDOW_CLOSE)
715     { // could be animating a (delayed) window open anim, so reset it
716       ResetAnimation(ANIM_TYPE_WINDOW_OPEN);
717       return false;
718     }
719   }
720   if (!IsVisible())
721   { // hidden - only allow hidden anims if we're animating a visible anim
722     if (animType == ANIM_TYPE_HIDDEN && !IsAnimating(ANIM_TYPE_VISIBLE))
723     {
724       // update states to force it hidden
725       UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED);
726       return false;
727     }
728     if (animType == ANIM_TYPE_WINDOW_OPEN)
729       return false;
730   }
731   return true;
732 }
733
734 void CGUIControl::QueueAnimation(ANIMATION_TYPE animType)
735 {
736   MarkDirtyRegion();
737   if (!CheckAnimation(animType))
738     return;
739   CAnimation *reverseAnim = GetAnimation((ANIMATION_TYPE)-animType, false);
740   CAnimation *forwardAnim = GetAnimation(animType);
741   // we first check whether the reverse animation is in progress (and reverse it)
742   // then we check for the normal animation, and queue it
743   if (reverseAnim && reverseAnim->IsReversible() && (reverseAnim->GetState() == ANIM_STATE_IN_PROCESS || reverseAnim->GetState() == ANIM_STATE_DELAYED))
744   {
745     reverseAnim->QueueAnimation(ANIM_PROCESS_REVERSE);
746     if (forwardAnim) forwardAnim->ResetAnimation();
747   }
748   else if (forwardAnim)
749   {
750     forwardAnim->QueueAnimation(ANIM_PROCESS_NORMAL);
751     if (reverseAnim) reverseAnim->ResetAnimation();
752   }
753   else
754   { // hidden and visible animations delay the change of state.  If there is no animations
755     // to perform, then we should just change the state straightaway
756     if (reverseAnim) reverseAnim->ResetAnimation();
757     UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED);
758   }
759 }
760
761 CAnimation *CGUIControl::GetAnimation(ANIMATION_TYPE type, bool checkConditions /* = true */)
762 {
763   for (unsigned int i = 0; i < m_animations.size(); i++)
764   {
765     CAnimation &anim = m_animations[i];
766     if (anim.GetType() == type)
767     {
768       if (!checkConditions || anim.CheckCondition())
769         return &anim;
770     }
771   }
772   return NULL;
773 }
774
775 bool CGUIControl::HasAnimation(ANIMATION_TYPE type)
776 {
777   return (NULL != GetAnimation(type, true));
778 }
779
780 void CGUIControl::UpdateStates(ANIMATION_TYPE type, ANIMATION_PROCESS currentProcess, ANIMATION_STATE currentState)
781 {
782   // Make sure control is hidden or visible at the appropriate times
783   // while processing a visible or hidden animation it needs to be visible,
784   // but when finished a hidden operation it needs to be hidden
785   if (type == ANIM_TYPE_VISIBLE)
786   {
787     if (currentProcess == ANIM_PROCESS_REVERSE)
788     {
789       if (currentState == ANIM_STATE_APPLIED)
790         m_visible = HIDDEN;
791     }
792     else if (currentProcess == ANIM_PROCESS_NORMAL)
793     {
794       if (currentState == ANIM_STATE_DELAYED)
795         m_visible = DELAYED;
796       else
797         m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
798     }
799   }
800   else if (type == ANIM_TYPE_HIDDEN)
801   {
802     if (currentProcess == ANIM_PROCESS_NORMAL)  // a hide animation
803     {
804       if (currentState == ANIM_STATE_APPLIED)
805         m_visible = HIDDEN; // finished
806       else
807         m_visible = VISIBLE; // have to be visible until we are finished
808     }
809     else if (currentProcess == ANIM_PROCESS_REVERSE)  // a visible animation
810     { // no delay involved here - just make sure it's visible
811       m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
812     }
813   }
814   else if (type == ANIM_TYPE_WINDOW_OPEN)
815   {
816     if (currentProcess == ANIM_PROCESS_NORMAL)
817     {
818       if (currentState == ANIM_STATE_DELAYED)
819         m_visible = DELAYED; // delayed
820       else
821         m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
822     }
823   }
824   else if (type == ANIM_TYPE_FOCUS)
825   {
826     // call the focus function if we have finished a focus animation
827     // (buttons can "click" on focus)
828     if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED)
829       OnFocus();
830   }
831   else if (type == ANIM_TYPE_UNFOCUS)
832   {
833     // call the unfocus function if we have finished a focus animation
834     // (buttons can "click" on focus)
835     if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED)
836       OnUnFocus();
837   }
838 }
839
840 bool CGUIControl::Animate(unsigned int currentTime)
841 {
842   // check visible state outside the loop, as it could change
843   GUIVISIBLE visible = m_visible;
844
845   m_transform.Reset();
846   bool changed = false;
847
848   CPoint center(GetXPosition() + GetWidth() * 0.5f, GetYPosition() + GetHeight() * 0.5f);
849   for (unsigned int i = 0; i < m_animations.size(); i++)
850   {
851     CAnimation &anim = m_animations[i];
852     anim.Animate(currentTime, HasProcessed() || visible == DELAYED);
853     // Update the control states (such as visibility)
854     UpdateStates(anim.GetType(), anim.GetProcess(), anim.GetState());
855     // and render the animation effect
856     changed |= (anim.GetProcess() != ANIM_PROCESS_NONE);
857     anim.RenderAnimation(m_transform, center);
858
859 /*    // debug stuff
860     if (anim.currentProcess != ANIM_PROCESS_NONE)
861     {
862       if (anim.effect == EFFECT_TYPE_ZOOM)
863       {
864         if (IsVisible())
865           CLog::Log(LOGDEBUG, "Animating control %d with a %s zoom effect %s. Amount is %2.1f, visible=%s", m_controlID, anim.type == ANIM_TYPE_CONDITIONAL ? (anim.lastCondition ? "conditional_on" : "conditional_off") : (anim.type == ANIM_TYPE_VISIBLE ? "visible" : "hidden"), anim.currentProcess == ANIM_PROCESS_NORMAL ? "normal" : "reverse", anim.amount, IsVisible() ? "true" : "false");
866       }
867       else if (anim.effect == EFFECT_TYPE_FADE)
868       {
869         if (IsVisible())
870           CLog::Log(LOGDEBUG, "Animating control %d with a %s fade effect %s. Amount is %2.1f. Visible=%s", m_controlID, anim.type == ANIM_TYPE_CONDITIONAL ? (anim.lastCondition ? "conditional_on" : "conditional_off") : (anim.type == ANIM_TYPE_VISIBLE ? "visible" : "hidden"), anim.currentProcess == ANIM_PROCESS_NORMAL ? "normal" : "reverse", anim.amount, IsVisible() ? "true" : "false");
871       }
872     }*/
873   }
874
875   return changed;
876 }
877
878 bool CGUIControl::IsAnimating(ANIMATION_TYPE animType)
879 {
880   for (unsigned int i = 0; i < m_animations.size(); i++)
881   {
882     CAnimation &anim = m_animations[i];
883     if (anim.GetType() == animType)
884     {
885       if (anim.GetQueuedProcess() == ANIM_PROCESS_NORMAL)
886         return true;
887       if (anim.GetProcess() == ANIM_PROCESS_NORMAL)
888         return true;
889     }
890     else if (anim.GetType() == -animType)
891     {
892       if (anim.GetQueuedProcess() == ANIM_PROCESS_REVERSE)
893         return true;
894       if (anim.GetProcess() == ANIM_PROCESS_REVERSE)
895         return true;
896     }
897   }
898   return false;
899 }
900
901 bool CGUIControl::GetNavigationAction(int direction, CGUIAction& action) const
902 {
903   switch (direction)
904   {
905   case ACTION_MOVE_UP:
906     action = m_actionUp;
907     return true;
908   case ACTION_MOVE_DOWN:
909     action = m_actionDown;
910     return true;
911   case ACTION_MOVE_LEFT:
912     action = m_actionLeft;
913     return true;
914   case ACTION_MOVE_RIGHT:
915     action = m_actionRight;
916     return true;
917   case ACTION_NAV_BACK:
918     action = m_actionBack;
919     return true;
920   default:
921     return false;
922   }
923 }
924
925 bool CGUIControl::CanFocusFromPoint(const CPoint &point) const
926 {
927   return CanFocus() && HitTest(point);
928 }
929
930 void CGUIControl::UnfocusFromPoint(const CPoint &point)
931 {
932   CPoint controlPoint(point);
933   m_transform.InverseTransformPosition(controlPoint.x, controlPoint.y);
934   if (!HitTest(controlPoint))
935     SetFocus(false);
936 }
937
938 bool CGUIControl::HasID(int id) const
939 {
940   return GetID() == id;
941 }
942
943 bool CGUIControl::HasVisibleID(int id) const
944 {
945   return GetID() == id && IsVisible();
946 }
947
948 void CGUIControl::SaveStates(vector<CControlState> &states)
949 {
950   // empty for now - do nothing with the majority of controls
951 }
952
953 void CGUIControl::SetHitRect(const CRect &rect)
954 {
955   m_hitRect = rect;
956 }
957
958 void CGUIControl::SetCamera(const CPoint &camera)
959 {
960   m_camera = camera;
961   m_hasCamera = true;
962 }
963
964 CPoint CGUIControl::GetRenderPosition() const
965 {
966   float z = 0;
967   CPoint point(GetPosition());
968   m_transform.TransformPosition(point.x, point.y, z);
969   if (m_parentControl)
970     point += m_parentControl->GetRenderPosition();
971   return point;
972 }