changed: Move pulled control updating via UpdateInfo() to UpdateVisibility() rather...
[xbmc:xbmc-antiquated.git] / XBMC / guilib / GUIControl.cpp
1 /*
2  *      Copyright (C) 2005-2008 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, write to
17  *  the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
18  *  http://www.gnu.org/copyleft/gpl.html
19  *
20  */
21
22 #include "include.h"
23 #include "GUIControl.h"
24
25 #include "utils/GUIInfoManager.h"
26 #include "LocalizeStrings.h"
27 #include "GUIWindowManager.h"
28
29 using namespace std;
30
31 CGUIControl::CGUIControl()
32 {
33   m_hasRendered = false;
34   m_bHasFocus = false;
35   m_dwControlID = 0;
36   m_dwParentID = 0;
37   m_visible = VISIBLE;
38   m_visibleFromSkinCondition = true;
39   m_forceHidden = false;
40   m_visibleCondition = 0;
41   m_enableCondition = 0;
42   m_enabled = true;
43   m_diffuseColor = 0xffffffff;
44   m_posX = 0;
45   m_posY = 0;
46   m_width = 0;
47   m_height = 0;
48   m_dwControlLeft = 0;
49   m_dwControlRight = 0;
50   m_dwControlUp = 0;
51   m_dwControlDown = 0;
52   m_dwControlNext = 0;
53   m_dwControlPrev = 0;
54   ControlType = GUICONTROL_UNKNOWN;
55   m_bInvalidated = true;
56   m_bAllocated=false;
57   m_parentControl = NULL;
58   m_hasCamera = false;
59   m_pushedUpdates = false;
60   m_pulseOnSelect = false;
61 }
62
63 CGUIControl::CGUIControl(DWORD dwParentID, DWORD dwControlId, float posX, float posY, float width, float height)
64 : m_hitRect(posX, posY, posX + width, posY + height)
65 {
66   m_posX = posX;
67   m_posY = posY;
68   m_width = width;
69   m_height = height;
70   m_bHasFocus = false;
71   m_dwControlID = dwControlId;
72   m_dwParentID = dwParentID;
73   m_visible = VISIBLE;
74   m_visibleFromSkinCondition = true;
75   m_diffuseColor = 0xffffffff;
76   m_forceHidden = false;
77   m_visibleCondition = 0;
78   m_enableCondition = 0;
79   m_enabled = true;
80   m_dwControlLeft = 0;
81   m_dwControlRight = 0;
82   m_dwControlUp = 0;
83   m_dwControlDown = 0;
84   m_dwControlNext = 0;
85   m_dwControlPrev = 0;
86   ControlType = GUICONTROL_UNKNOWN;
87   m_bInvalidated = true;
88   m_bAllocated=false;
89   m_hasRendered = false;
90   m_parentControl = NULL;
91   m_hasCamera = false;
92   m_pushedUpdates = false;
93   m_pulseOnSelect = false;
94 }
95
96
97 CGUIControl::~CGUIControl(void)
98 {
99
100 }
101
102 void CGUIControl::AllocResources()
103 {
104   m_hasRendered = false;
105   m_bInvalidated = true;
106   m_bAllocated=true;
107 }
108
109 void CGUIControl::FreeResources()
110 {
111   if (m_bAllocated)
112   {
113     // Reset our animation states - not conditional anims though.
114     // I'm not sure if this is needed for most cases anyway.  I believe it's only here
115     // because some windows aren't loaded on demand
116     for (unsigned int i = 0; i < m_animations.size(); i++)
117     {
118       CAnimation &anim = m_animations[i];
119       if (anim.GetType() != ANIM_TYPE_CONDITIONAL)
120         anim.ResetAnimation();
121     }
122     m_bAllocated=false;
123   }
124   m_hasRendered = false;
125 }
126
127 void CGUIControl::DynamicResourceAlloc(bool bOnOff)
128 {
129
130 }
131
132 // the main render routine.
133 // 1. animate and set the animation transform
134 // 2. if visible, paint
135 // 3. reset the animation transform
136 void CGUIControl::DoRender(DWORD currentTime)
137 {
138   Animate(currentTime);
139   if (m_hasCamera)
140     g_graphicsContext.SetCameraPosition(m_camera);
141   if (IsVisible())
142     Render();
143   if (m_hasCamera)
144     g_graphicsContext.RestoreCameraPosition();
145   g_graphicsContext.RemoveTransform();
146 }
147
148 void CGUIControl::Render()
149 {
150   m_bInvalidated = false;
151   m_hasRendered = true;
152 }
153
154 bool CGUIControl::OnAction(const CAction &action)
155 {
156   switch (action.wID)
157   {
158   case ACTION_MOVE_DOWN:
159     if (!HasFocus()) return false;
160     OnDown();
161     return true;
162     break;
163
164   case ACTION_MOVE_UP:
165     if (!HasFocus()) return false;
166     OnUp();
167     return true;
168     break;
169
170   case ACTION_MOVE_LEFT:
171     if (!HasFocus()) return false;
172     OnLeft();
173     return true;
174     break;
175
176   case ACTION_MOVE_RIGHT:
177     if (!HasFocus()) return false;
178     OnRight();
179     return true;
180     break;
181     
182   case ACTION_NEXT_CONTROL:
183       if (!HasFocus()) return false;
184       OnNextControl();
185       return true;
186       break;
187       
188   case ACTION_PREV_CONTROL:
189       if (!HasFocus()) return false;
190       OnPrevControl();
191       return true;
192       break;
193   }
194   return false;
195 }
196
197 // Movement controls (derived classes can override)
198 void CGUIControl::OnUp()
199 {
200   if (HasFocus())
201   {
202     if (m_upActions.size())
203       ExecuteActions(m_upActions);
204     else if (m_dwControlID != m_dwControlUp)
205     {
206       // Send a message to the window with the sender set as the window
207       CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_MOVE_UP);
208       SendWindowMessage(msg);
209     }
210   }
211 }
212
213 void CGUIControl::OnDown()
214 {
215   if (HasFocus())
216   {
217     if (m_downActions.size())
218       ExecuteActions(m_downActions);
219     else if (m_dwControlID != m_dwControlDown)
220     {
221       // Send a message to the window with the sender set as the window
222       CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_MOVE_DOWN);
223       SendWindowMessage(msg);
224     }
225   }
226 }
227
228 void CGUIControl::OnLeft()
229 {
230   if (HasFocus())
231   {
232     if (m_leftActions.size())
233       ExecuteActions(m_leftActions);
234     else if (m_dwControlID != m_dwControlLeft)
235     {
236       // Send a message to the window with the sender set as the window
237       CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_MOVE_LEFT);
238       SendWindowMessage(msg);
239     }
240   }
241 }
242
243 void CGUIControl::OnRight()
244 {
245   if (HasFocus())
246   {
247     if (m_rightActions.size())
248       ExecuteActions(m_rightActions);
249     else if (m_dwControlID != m_dwControlRight)
250     {
251       // Send a message to the window with the sender set as the window
252       CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_MOVE_RIGHT);
253       SendWindowMessage(msg);
254     }
255   }
256 }
257
258 void CGUIControl::OnNextControl()
259 {
260   if (m_dwControlID != m_dwControlNext)
261   {
262     // Send a message to the window with the sender set as the window
263     CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_NEXT_CONTROL, m_dwControlNext);
264     SendWindowMessage(msg);
265   }
266 }
267
268 void CGUIControl::OnPrevControl()
269 {
270   if (m_dwControlID != m_dwControlPrev)
271   {
272     // Send a message to the window with the sender set as the window
273     CGUIMessage msg(GUI_MSG_MOVE, GetParentID(), GetID(), ACTION_PREV_CONTROL, m_dwControlPrev);
274     SendWindowMessage(msg);
275   }
276 }
277
278 bool CGUIControl::SendWindowMessage(CGUIMessage &message)
279 {
280   CGUIWindow *pWindow = m_gWindowManager.GetWindow(GetParentID());
281   if (pWindow)
282     return pWindow->OnMessage(message);
283   return g_graphicsContext.SendMessage(message);
284 }
285
286 DWORD CGUIControl::GetID(void) const
287 {
288   return m_dwControlID;
289 }
290
291
292 DWORD CGUIControl::GetParentID(void) const
293 {
294   return m_dwParentID;
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       if (m_visibleCondition)
348         m_visible = g_infoManager.GetBool(m_visibleCondition, m_dwParentID) ? VISIBLE : HIDDEN;
349       else
350         m_visible = VISIBLE;
351       m_forceHidden = false;
352       return true;
353       break;
354
355     case GUI_MSG_HIDDEN:
356       m_forceHidden = true;
357       // reset any visible animations that are in process
358       if (IsAnimating(ANIM_TYPE_VISIBLE))
359       {
360 //        CLog::DebugLog("Resetting visible animation on control %i (we are %s)", m_dwControlID, m_visible ? "visible" : "hidden");
361         CAnimation *visibleAnim = GetAnimation(ANIM_TYPE_VISIBLE);
362         if (visibleAnim) visibleAnim->ResetAnimation();
363       }
364       return true;
365
366       // Note that the skin <enable> tag will override these messages
367     case GUI_MSG_ENABLED:
368       SetEnabled(true);
369       return true;
370
371     case GUI_MSG_DISABLED:
372       SetEnabled(false);
373       return true;
374     }
375   }
376   return false;
377 }
378
379 bool CGUIControl::CanFocus() const
380 {
381   if (!IsVisible() && !m_allowHiddenFocus) return false;
382   if (IsDisabled()) return false;
383   return true;
384 }
385
386 bool CGUIControl::IsVisible() const
387 {
388   if (m_forceHidden) return false;
389   return m_visible == VISIBLE;
390 }
391
392 bool CGUIControl::IsDisabled() const
393 {
394   return !m_enabled;
395 }
396
397 void CGUIControl::SetEnabled(bool bEnable)
398 {
399   m_enabled = bEnable;
400 }
401
402 void CGUIControl::SetEnableCondition(int condition)
403 {
404   m_enableCondition = condition;
405 }
406
407 void CGUIControl::SetPosition(float posX, float posY)
408 {
409   if ((m_posX != posX) || (m_posY != posY))
410   {
411     m_hitRect += CPoint(posX - m_posX, posY - m_posY);
412     m_posX = posX;
413     m_posY = posY;
414     SetInvalid();
415   }
416 }
417
418 void CGUIControl::SetColorDiffuse(const CGUIInfoColor &color)
419 {
420   m_diffuseColor = color;
421 }
422
423 float CGUIControl::GetXPosition() const
424 {
425   return m_posX;
426 }
427
428 float CGUIControl::GetYPosition() const
429 {
430   return m_posY;
431 }
432
433 float CGUIControl::GetWidth() const
434 {
435   return m_width;
436 }
437
438 float CGUIControl::GetHeight() const
439 {
440   return m_height;
441 }
442
443 void CGUIControl::SetNavigation(DWORD dwUp, DWORD dwDown, DWORD dwLeft, DWORD dwRight)
444 {
445   m_dwControlUp = dwUp;
446   m_dwControlDown = dwDown;
447   m_dwControlLeft = dwLeft;
448   m_dwControlRight = dwRight;
449 }
450
451 void CGUIControl::SetTabNavigation(DWORD dwNext, DWORD dwPrev)
452 {
453   m_dwControlNext = dwNext;
454   m_dwControlPrev = dwPrev;
455 }
456
457 void CGUIControl::SetNavigationActions(const vector<CGUIActionDescriptor> &up, const vector<CGUIActionDescriptor> &down,
458                                        const vector<CGUIActionDescriptor> &left, const vector<CGUIActionDescriptor> &right)
459 {
460   m_leftActions = left;
461   m_rightActions = right;
462   m_upActions = up;
463   m_downActions = down;
464 }
465
466 void CGUIControl::SetWidth(float width)
467 {
468   if (m_width != width)
469   {
470     m_width = width;
471     m_hitRect.x2 = m_hitRect.x1 + width;
472     SetInvalid();
473   }
474 }
475
476 void CGUIControl::SetHeight(float height)
477 {
478   if (m_height != height)
479   {
480     m_height = height;
481     m_hitRect.y2 = m_hitRect.y1 + height;
482     SetInvalid();
483   }
484 }
485
486 void CGUIControl::SetVisible(bool bVisible)
487 {
488   // just force to hidden if necessary
489   m_forceHidden = !bVisible;
490 /*
491   if (m_visibleCondition)
492     bVisible = g_infoManager.GetBool(m_visibleCondition, m_dwParentID);
493   if (m_bVisible != bVisible)
494   {
495     m_visible = bVisible;
496     m_visibleFromSkinCondition = bVisible;
497     SetInvalid();
498   }*/
499 }
500
501 bool CGUIControl::HitTest(const CPoint &point) const
502 {
503   return m_hitRect.PtInRect(point);
504 }
505
506 // override this function to implement custom mouse behaviour
507 bool CGUIControl::OnMouseOver(const CPoint &point)
508 {
509   if (g_Mouse.GetState() != MOUSE_STATE_DRAG)
510     g_Mouse.SetState(MOUSE_STATE_FOCUS);
511   if (!CanFocus()) return false;
512   CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), GetID());
513   OnMessage(msg);
514   return true;
515 }
516
517 void CGUIControl::UpdateVisibility(const CGUIListItem *item)
518 {
519   if (m_visibleCondition)
520   {
521     bool bWasVisible = m_visibleFromSkinCondition;
522     m_visibleFromSkinCondition = g_infoManager.GetBool(m_visibleCondition, m_dwParentID, item);
523     if (!bWasVisible && m_visibleFromSkinCondition)
524     { // automatic change of visibility - queue the in effect
525   //    CLog::DebugLog("Visibility changed to visible for control id %i", m_dwControlID);
526       QueueAnimation(ANIM_TYPE_VISIBLE);
527     }
528     else if (bWasVisible && !m_visibleFromSkinCondition)
529     { // automatic change of visibility - do the out effect
530   //    CLog::DebugLog("Visibility changed to hidden for control id %i", m_dwControlID);
531       QueueAnimation(ANIM_TYPE_HIDDEN);
532     }
533   }
534   // check for conditional animations
535   for (unsigned int i = 0; i < m_animations.size(); i++)
536   {
537     CAnimation &anim = m_animations[i];
538     if (anim.GetType() == ANIM_TYPE_CONDITIONAL)
539       anim.UpdateCondition(GetParentID(), item);
540   }
541   // and check for conditional enabling - note this overrides SetEnabled() from the code currently
542   // this may need to be reviewed at a later date
543   if (m_enableCondition)
544     m_enabled = g_infoManager.GetBool(m_enableCondition, m_dwParentID, item);
545   m_allowHiddenFocus.Update(m_dwParentID, item);
546   UpdateColors();
547   // and finally, update our control information (if not pushed)
548   if (!m_pushedUpdates)
549     UpdateInfo(item);
550 }
551
552 void CGUIControl::UpdateColors()
553 {
554   m_diffuseColor.Update();
555 }
556
557 void CGUIControl::SetInitialVisibility()
558 {
559   if (m_visibleCondition)
560   {
561     m_visibleFromSkinCondition = g_infoManager.GetBool(m_visibleCondition, m_dwParentID);
562     m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
563   //  CLog::DebugLog("Set initial visibility for control %i: %s", m_dwControlID, m_visible == VISIBLE ? "visible" : "hidden");
564     // no need to enquire every frame if we are always visible or always hidden
565     if (m_visibleCondition == SYSTEM_ALWAYS_TRUE || m_visibleCondition == SYSTEM_ALWAYS_FALSE)
566       m_visibleCondition = 0;
567   }
568   // and handle animation conditions as well
569   for (unsigned int i = 0; i < m_animations.size(); i++)
570   {
571     CAnimation &anim = m_animations[i];
572     if (anim.GetType() == ANIM_TYPE_CONDITIONAL)
573       anim.SetInitialCondition(GetParentID());
574   }
575   m_allowHiddenFocus.Update(m_dwParentID);
576   UpdateColors();
577 }
578
579 void CGUIControl::SetVisibleCondition(int visible, const CGUIInfoBool &allowHiddenFocus)
580 {
581   m_visibleCondition = visible;
582   m_allowHiddenFocus = allowHiddenFocus;
583 }
584
585 void CGUIControl::SetAnimations(const vector<CAnimation> &animations)
586 {
587   m_animations = animations;
588 }
589
590 void CGUIControl::ResetAnimation(ANIMATION_TYPE type)
591 {
592   for (unsigned int i = 0; i < m_animations.size(); i++)
593   {
594     if (m_animations[i].GetType() == type)
595       m_animations[i].ResetAnimation();
596   }
597 }
598
599 void CGUIControl::ResetAnimations()
600 {
601   for (unsigned int i = 0; i < m_animations.size(); i++)
602     m_animations[i].ResetAnimation();
603 }
604
605 bool CGUIControl::CheckAnimation(ANIMATION_TYPE animType)
606 {
607   // rule out the animations we shouldn't perform
608   if (!IsVisible() || !HasRendered())
609   { // hidden or never rendered - don't allow exit or entry animations for this control
610     if (animType == ANIM_TYPE_WINDOW_CLOSE)
611     { // could be animating a (delayed) window open anim, so reset it
612       ResetAnimation(ANIM_TYPE_WINDOW_OPEN);
613       return false;
614     }
615   }
616   if (!IsVisible())
617   { // hidden - only allow hidden anims if we're animating a visible anim
618     if (animType == ANIM_TYPE_HIDDEN && !IsAnimating(ANIM_TYPE_VISIBLE))
619     {
620       // update states to force it hidden
621       UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED);
622       return false;
623     }
624     if (animType == ANIM_TYPE_WINDOW_OPEN)
625       return false;
626   }
627   return true;
628 }
629
630 void CGUIControl::QueueAnimation(ANIMATION_TYPE animType)
631 {
632   if (!CheckAnimation(animType))
633     return;
634   CAnimation *reverseAnim = GetAnimation((ANIMATION_TYPE)-animType, false);
635   CAnimation *forwardAnim = GetAnimation(animType);
636   // we first check whether the reverse animation is in progress (and reverse it)
637   // then we check for the normal animation, and queue it
638   if (reverseAnim && reverseAnim->IsReversible() && (reverseAnim->GetState() == ANIM_STATE_IN_PROCESS || reverseAnim->GetState() == ANIM_STATE_DELAYED))
639   {
640     reverseAnim->QueueAnimation(ANIM_PROCESS_REVERSE);
641     if (forwardAnim) forwardAnim->ResetAnimation();
642   }
643   else if (forwardAnim)
644   {
645     forwardAnim->QueueAnimation(ANIM_PROCESS_NORMAL);
646     if (reverseAnim) reverseAnim->ResetAnimation();
647   }
648   else
649   { // hidden and visible animations delay the change of state.  If there is no animations
650     // to perform, then we should just change the state straightaway
651     if (reverseAnim) reverseAnim->ResetAnimation();
652     UpdateStates(animType, ANIM_PROCESS_NORMAL, ANIM_STATE_APPLIED);
653   }
654 }
655
656 CAnimation *CGUIControl::GetAnimation(ANIMATION_TYPE type, bool checkConditions /* = true */)
657 {
658   for (unsigned int i = 0; i < m_animations.size(); i++)
659   {
660     CAnimation &anim = m_animations[i];
661     if (anim.GetType() == type)
662     {
663       if (!checkConditions || !anim.GetCondition() || g_infoManager.GetBool(anim.GetCondition()))
664         return &anim;
665     }
666   }
667   return NULL;
668 }
669
670 bool CGUIControl::HasAnimation(ANIMATION_TYPE type)
671 {
672   return (NULL != GetAnimation(type, true));
673 }
674
675 void CGUIControl::UpdateStates(ANIMATION_TYPE type, ANIMATION_PROCESS currentProcess, ANIMATION_STATE currentState)
676 {
677   // Make sure control is hidden or visible at the appropriate times
678   // while processing a visible or hidden animation it needs to be visible,
679   // but when finished a hidden operation it needs to be hidden
680   if (type == ANIM_TYPE_VISIBLE)
681   {
682     if (currentProcess == ANIM_PROCESS_REVERSE)
683     {
684       if (currentState == ANIM_STATE_APPLIED)
685         m_visible = HIDDEN;
686     }
687     else if (currentProcess == ANIM_PROCESS_NORMAL)
688     {
689       if (currentState == ANIM_STATE_DELAYED)
690         m_visible = DELAYED;
691       else
692         m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
693     }
694   }
695   else if (type == ANIM_TYPE_HIDDEN)
696   {
697     if (currentProcess == ANIM_PROCESS_NORMAL)  // a hide animation
698     {
699       if (currentState == ANIM_STATE_APPLIED)
700         m_visible = HIDDEN; // finished
701       else
702         m_visible = VISIBLE; // have to be visible until we are finished
703     }
704     else if (currentProcess == ANIM_PROCESS_REVERSE)  // a visible animation
705     { // no delay involved here - just make sure it's visible
706       m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
707     }
708   }
709   else if (type == ANIM_TYPE_WINDOW_OPEN)
710   {
711     if (currentProcess == ANIM_PROCESS_NORMAL)
712     {
713       if (currentState == ANIM_STATE_DELAYED)
714         m_visible = DELAYED; // delayed
715       else
716         m_visible = m_visibleFromSkinCondition ? VISIBLE : HIDDEN;
717     }
718   }
719   else if (type == ANIM_TYPE_FOCUS)
720   {
721     // call the focus function if we have finished a focus animation
722     // (buttons can "click" on focus)
723     if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED)
724       OnFocus();
725   }
726   else if (type == ANIM_TYPE_UNFOCUS)
727   {
728     // call the unfocus function if we have finished a focus animation
729     // (buttons can "click" on focus)
730     if (currentProcess == ANIM_PROCESS_NORMAL && currentState == ANIM_STATE_APPLIED)
731       OnUnFocus();
732   }
733 }
734
735 void CGUIControl::Animate(DWORD currentTime)
736 {
737   // check visible state outside the loop, as it could change
738   GUIVISIBLE visible = m_visible;
739   m_transform.Reset();
740   CPoint center(m_posX + m_width * 0.5f, m_posY + m_height * 0.5f);
741   for (unsigned int i = 0; i < m_animations.size(); i++)
742   {
743     CAnimation &anim = m_animations[i];
744     anim.Animate(currentTime, HasRendered() || visible == DELAYED);
745     // Update the control states (such as visibility)
746     UpdateStates(anim.GetType(), anim.GetProcess(), anim.GetState());
747     // and render the animation effect
748     anim.RenderAnimation(m_transform, center);
749
750 /*    // debug stuff
751     if (anim.currentProcess != ANIM_PROCESS_NONE)
752     {
753       if (anim.effect == EFFECT_TYPE_ZOOM)
754       {
755         if (IsVisible())
756           CLog::DebugLog("Animating control %d with a %s zoom effect %s. Amount is %2.1f, visible=%s", m_dwControlID, 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");
757       }
758       else if (anim.effect == EFFECT_TYPE_FADE)
759       {
760         if (IsVisible())
761           CLog::DebugLog("Animating control %d with a %s fade effect %s. Amount is %2.1f. Visible=%s", m_dwControlID, 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");
762       }
763     }*/
764   }
765   g_graphicsContext.AddTransform(m_transform);
766 }
767
768 bool CGUIControl::IsAnimating(ANIMATION_TYPE animType)
769 {
770   for (unsigned int i = 0; i < m_animations.size(); i++)
771   {
772     CAnimation &anim = m_animations[i];
773     if (anim.GetType() == animType)
774     {
775       if (anim.GetQueuedProcess() == ANIM_PROCESS_NORMAL)
776         return true;
777       if (anim.GetProcess() == ANIM_PROCESS_NORMAL)
778         return true;
779     }
780     else if (anim.GetType() == -animType)
781     {
782       if (anim.GetQueuedProcess() == ANIM_PROCESS_REVERSE)
783         return true;
784       if (anim.GetProcess() == ANIM_PROCESS_REVERSE)
785         return true;
786     }
787   }
788   return false;
789 }
790
791 DWORD CGUIControl::GetNextControl(int direction) const
792 {
793   switch (direction)
794   {
795   case ACTION_MOVE_UP:
796     return m_dwControlUp;
797   case ACTION_MOVE_DOWN:
798     return m_dwControlDown;
799   case ACTION_MOVE_LEFT:
800     return m_dwControlLeft;
801   case ACTION_MOVE_RIGHT:
802     return m_dwControlRight;
803   default:
804     return -1;
805   }
806 }
807
808 // input the point with respect to this control to hit, and return
809 // the control and the point with respect to his control if we have a hit
810 bool CGUIControl::CanFocusFromPoint(const CPoint &point, CGUIControl **control, CPoint &controlPoint) const
811 {
812   controlPoint = point;
813   m_transform.InverseTransformPosition(controlPoint.x, controlPoint.y);
814   if (CanFocus() && HitTest(controlPoint))
815   {
816     *control = (CGUIControl *)this;
817     return true;
818   }
819   *control = NULL;
820   return false;
821 }
822
823 void CGUIControl::UnfocusFromPoint(const CPoint &point)
824 {
825   CPoint controlPoint(point);
826   m_transform.InverseTransformPosition(controlPoint.x, controlPoint.y);
827   if (!HitTest(controlPoint))
828     SetFocus(false);
829 }
830
831 bool CGUIControl::HasID(DWORD dwID) const
832 {
833   return GetID() == dwID;
834 }
835
836 bool CGUIControl::HasVisibleID(DWORD dwID) const
837 {
838   return GetID() == dwID && IsVisible();
839 }
840
841 void CGUIControl::SaveStates(vector<CControlState> &states)
842 {
843   // empty for now - do nothing with the majority of controls
844 }
845
846 void CGUIControl::SetHitRect(const CRect &rect)
847 {
848   m_hitRect = rect;
849 }
850
851 void CGUIControl::SetCamera(const CPoint &camera)
852 {
853   m_camera = camera;
854   m_hasCamera = true;
855 }
856
857 void CGUIControl::ExecuteActions(const vector<CGUIActionDescriptor> &actions)
858 {
859   // we should really save anything we need, as the action may cause the window to close
860   DWORD savedID = GetID();
861   DWORD savedParent = GetParentID();
862   vector<CGUIActionDescriptor> savedActions = actions;
863
864   for (unsigned int i = 0; i < savedActions.size(); i++)
865   {
866     CGUIMessage message(GUI_MSG_EXECUTE, savedID, savedParent);
867     message.SetAction(savedActions[i]);
868     g_graphicsContext.SendMessage(message);
869   }
870 }
871
872 CPoint CGUIControl::GetRenderPosition() const
873 {
874   float z = 0;
875   CPoint point(m_posX, m_posY);
876   m_transform.TransformPosition(point.x, point.y, z);
877   if (m_parentControl)
878     point += m_parentControl->GetRenderPosition();
879   return point;
880 }