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