fixed: Lists with static content didn't save their focused item.
[xbmc:xbmc-antiquated.git] / guilib / GUIWindow.cpp
1 #include "include.h"
2 #include "GUIWindow.h"
3 #include "GUIWindowManager.h"
4 #include "LocalizeStrings.h"
5 #include "TextureManager.h"
6 #include "../xbmc/Util.h"
7 #include "GuiControlFactory.h"
8 #include "GUIControlGroup.h"
9 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
10 #include "GUIListContainer.h"
11 #include "GUIPanelContainer.h"
12 #endif
13
14 #include "SkinInfo.h"
15 #include "../xbmc/utils/GUIInfoManager.h"
16 #include "../xbmc/utils/SingleLock.h"
17 #include "../xbmc/ButtonTranslator.h"
18 #include "XMLUtils.h"
19
20 CStdString CGUIWindow::CacheFilename = "";
21
22 CGUIWindow::CGUIWindow(DWORD dwID, const CStdString &xmlFile)
23 {
24   m_dwWindowId = dwID;
25   m_xmlFile = xmlFile;
26   m_dwIDRange = 1;
27   m_saveLastControl = false;
28   m_dwDefaultFocusControlID = 0;
29   m_lastControlID = 0;
30   m_focusedControl = 0;
31   m_bRelativeCoords = false;
32   m_posX = m_posY = m_width = m_height = 0;
33   m_overlayState = OVERLAY_STATE_PARENT_WINDOW;   // Use parent or previous window's state
34   m_WindowAllocated = false;
35   m_coordsRes = g_guiSettings.m_LookAndFeelResolution;
36   m_isDialog = false;
37   m_needsScaling = true;
38   m_visibleCondition = 0;
39   m_windowLoaded = false;
40   m_loadOnDemand = true;
41   m_renderOrder = 0;
42   m_dynamicResourceAlloc = true;
43   m_hasRendered = false;
44   m_hasCamera = false;
45   m_previousWindow = WINDOW_INVALID;
46 }
47
48 CGUIWindow::~CGUIWindow(void)
49 {}
50
51 void CGUIWindow::FlushReferenceCache()
52 {
53   CacheFilename.clear();
54 }
55
56 bool CGUIWindow::LoadReferences()
57 {
58   // load references.xml
59   TiXmlDocument xmlDoc;
60   RESOLUTION res;
61   CStdString strReferenceFile = g_SkinInfo.GetSkinPath("references.xml", &res);
62   // check if we've already loaded it previously
63   if (CacheFilename == strReferenceFile)
64     return true;
65
66   // nope - time to load it in
67   if ( !xmlDoc.LoadFile(strReferenceFile.c_str()) )
68   {
69 //    CLog::Log(LOGERROR, "unable to load:%s, Line %d\n%s", strReferenceFile.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
70     return false;
71   }
72
73   CLog::Log(LOGINFO, "Loading references file: %s", strReferenceFile.c_str());
74   TiXmlElement* pRootElement = xmlDoc.RootElement();
75   CStdString strValue = pRootElement->Value();
76   if (strValue != CStdString("controls"))
77   {
78     CLog::Log(LOGERROR, "references.xml doesn't contain <controls>");
79     return false;
80   }
81   RESOLUTION includeRes;
82   g_SkinInfo.GetSkinPath("includes.xml", &includeRes);
83   g_SkinInfo.ResolveIncludes(pRootElement);
84   CGUIControlFactory factory;
85   CStdString strType;
86   TiXmlElement *pControl = pRootElement->FirstChildElement();
87   TiXmlElement includes("includes");
88   while (pControl)
89   {
90     // ok, this is a <control> block, find the type
91     strType = factory.GetType(pControl);
92     if (!strType.IsEmpty())
93     { // we construct a new <default type="type"> block in our includes document
94       TiXmlElement include("default");
95       include.SetAttribute("type", strType.c_str());
96       // and add the rest of the items under this controlblock to it
97       TiXmlElement *child = pControl->FirstChildElement();
98       while (child)
99       {
100         TiXmlElement element(*child);
101         // scale element if necessary
102         factory.ScaleElement(&element, res, includeRes);
103         include.InsertEndChild(element);
104         child = child->NextSiblingElement();
105       }
106       includes.InsertEndChild(include);
107     }
108     pControl = pControl->NextSiblingElement();
109   }
110   CacheFilename = strReferenceFile;
111   // now load our includes
112   g_SkinInfo.LoadIncludes(&includes);
113   return true;
114 }
115
116 bool CGUIWindow::Load(const CStdString& strFileName, bool bContainsPath)
117 {
118   if (m_windowLoaded)
119     return true;      // no point loading if it's already there
120   LARGE_INTEGER start;
121   QueryPerformanceCounter(&start);
122
123   RESOLUTION resToUse = INVALID;
124   CLog::Log(LOGINFO, "Loading skin file: %s", strFileName.c_str());
125   TiXmlDocument xmlDoc;
126   // Find appropriate skin folder + resolution to load from
127   CStdString strPath;
128   if (bContainsPath)
129     strPath = strFileName;
130   else
131     strPath = g_SkinInfo.GetSkinPath(strFileName, &resToUse);
132
133   if ( !xmlDoc.LoadFile(strPath.c_str()) )
134   {
135     CLog::Log(LOGERROR, "unable to load:%s, Line %d\n%s", strPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
136 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
137     if (g_SkinInfo.GetVersion() < 2.1 && GetID() == WINDOW_VIDEO_NAV && m_xmlFile != "myvideotitle.xml")
138     {
139       m_xmlFile = "myvideotitle.xml";
140       return Load(m_xmlFile);
141     }
142 #endif
143     m_dwWindowId = WINDOW_INVALID;
144     return false;
145   }
146   TiXmlElement* pRootElement = xmlDoc.RootElement();
147   if (strcmpi(pRootElement->Value(), "window"))
148   {
149     CLog::Log(LOGERROR, "file :%s doesnt contain <window>", strPath.c_str());
150     return false;
151   }
152   LARGE_INTEGER lend;
153   QueryPerformanceCounter(&lend);
154   if (!bContainsPath)
155     m_coordsRes = resToUse;
156   bool ret = Load(pRootElement);
157   LARGE_INTEGER end, freq;
158   QueryPerformanceCounter(&end);
159   QueryPerformanceFrequency(&freq);
160   CLog::DebugLog("Load %s: %.2fms (%.2f ms xml load)", m_xmlFile.c_str(), 1000.f * (end.QuadPart - start.QuadPart) / freq.QuadPart, 1000.f * (lend.QuadPart - start.QuadPart) / freq.QuadPart);
161   return ret;
162 }
163
164 bool CGUIWindow::Load(TiXmlElement* pRootElement)
165 {
166   // set the scaling resolution so that any control creation or initialisation can
167   // be done with respect to the correct aspect ratio
168   g_graphicsContext.SetScalingResolution(m_coordsRes, 0, 0, m_needsScaling);
169
170   // Resolve any includes that may be present
171   g_SkinInfo.ResolveIncludes(pRootElement);
172   // now load in the skin file
173   SetDefaults();
174
175   LoadReferences();
176   TiXmlElement *pChild = pRootElement->FirstChildElement();
177   while (pChild)
178   {
179     CStdString strValue = pChild->Value();
180     if (strValue == "type" && pChild->FirstChild())
181     {
182       // if we have are a window type (ie not a dialog), and we have <type>dialog</type>
183       // then make this window act like a dialog
184       if (!IsDialog() && strcmpi(pChild->FirstChild()->Value(), "dialog") == 0)
185         m_isDialog = true;
186     }
187     else if (strValue == "previouswindow" && pChild->FirstChild())
188     {
189       m_previousWindow = g_buttonTranslator.TranslateWindowString(pChild->FirstChild()->Value());
190     }
191     else if (strValue == "defaultcontrol" && pChild->FirstChild())
192     {
193       const char *always = pChild->Attribute("always");
194       if (always && strcmpi(always, "true") == 0)
195         m_saveLastControl = false;
196       m_dwDefaultFocusControlID = atoi(pChild->FirstChild()->Value());
197     }
198     else if (strValue == "visible" && pChild->FirstChild())
199     {
200       CGUIControlFactory::GetConditionalVisibility(pRootElement, m_visibleCondition);
201     }
202     else if (strValue == "animation" && pChild->FirstChild())
203     {
204       FRECT rect = { 0, 0, (float)g_settings.m_ResInfo[m_coordsRes].iWidth, (float)g_settings.m_ResInfo[m_coordsRes].iHeight };
205       if (strcmpi(pChild->FirstChild()->Value(), "windowopen") == 0)
206         m_showAnimation.Create(pChild->ToElement(), rect);
207       else if (strcmpi(pChild->FirstChild()->Value(), "windowclose") == 0)
208         m_closeAnimation.Create(pChild->ToElement(), rect);
209     }
210     else if (strValue == "zorder" && pChild->FirstChild())
211     {
212       m_renderOrder = atoi(pChild->FirstChild()->Value());
213     }
214     else if (strValue == "coordinates")
215     {
216       // resolve any includes within coordinates tag (such as multiple origin includes)
217       g_SkinInfo.ResolveIncludes(pChild);
218       TiXmlNode* pSystem = pChild->FirstChild("system");
219       if (pSystem)
220       {
221         int iCoordinateSystem = atoi(pSystem->FirstChild()->Value());
222         m_bRelativeCoords = (iCoordinateSystem == 1);
223       }
224
225       XMLUtils::GetFloat(pChild, "posx", m_posX);
226       XMLUtils::GetFloat(pChild, "posy", m_posY);
227
228       TiXmlElement *originElement = pChild->FirstChildElement("origin");
229       while (originElement)
230       {
231         COrigin origin;
232         originElement->Attribute("x", &origin.x);
233         originElement->Attribute("y", &origin.y);
234         if (originElement->FirstChild())
235           origin.condition = g_infoManager.TranslateString(originElement->FirstChild()->Value());
236         m_origins.push_back(origin);
237         originElement = originElement->NextSiblingElement("origin");
238       }
239     }
240     else if (strValue == "camera")
241     { // z is fixed
242       pChild->Attribute("x", &m_camera.x);
243       pChild->Attribute("y", &m_camera.y);
244       m_hasCamera = true;
245     }
246     else if (strValue == "controls")
247     {
248       // resolve any includes within controls tag (such as whole <control> includes)
249       g_SkinInfo.ResolveIncludes(pChild);
250
251       TiXmlElement *pControl = pChild->FirstChildElement();
252       while (pControl)
253       {
254         if (strcmpi(pControl->Value(), "control") == 0)
255         {
256           LoadControl(pControl, NULL);
257         }
258         else if (strcmpi(pControl->Value(), "controlgroup") == 0)
259         {
260           // backward compatibility...
261           LoadControl(pControl, NULL);
262         }
263         pControl = pControl->NextSiblingElement();
264       }
265     }
266     else if (strValue == "allowoverlay")
267     {
268       bool overlay = false;
269       if (XMLUtils::GetBoolean(pRootElement, "allowoverlay", overlay))
270         m_overlayState = overlay ? OVERLAY_STATE_SHOWN : OVERLAY_STATE_HIDDEN;
271     }
272
273     pChild = pChild->NextSiblingElement();
274   }
275
276   m_windowLoaded = true;
277   OnWindowLoaded();
278   return true;
279 }
280
281 void CGUIWindow::LoadControl(TiXmlElement* pControl, CGUIControlGroup *pGroup)
282 {
283   // get control type
284   CGUIControlFactory factory;
285
286   FRECT rect = { 0, 0, (float)g_settings.m_ResInfo[m_coordsRes].iWidth, (float)g_settings.m_ResInfo[m_coordsRes].iHeight };
287   if (pGroup)
288   {
289     rect.left = pGroup->GetXPosition();
290     rect.top = pGroup->GetYPosition();
291     rect.right = rect.left + pGroup->GetWidth();
292     rect.bottom = rect.top + pGroup->GetHeight();
293   }
294   CGUIControl* pGUIControl = factory.Create(m_dwWindowId, rect, pControl);
295   if (pGUIControl)
296   {
297     float maxX = pGUIControl->GetXPosition() + pGUIControl->GetWidth();
298     if (maxX > m_width)
299     {
300       m_width = maxX;
301     }
302
303     float maxY = pGUIControl->GetYPosition() + pGUIControl->GetHeight();
304     if (maxY > m_height)
305     {
306       m_height = maxY;
307     }
308     // if we are in a group, add to the group, else add to our window
309     pGUIControl->SetParentControl(pGroup);
310     if (pGroup)
311       pGroup->AddControl(pGUIControl);
312     else
313       Add(pGUIControl);
314 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
315     if (pGUIControl->GetControlType() == CGUIControl::GUICONTAINER_LIST)
316     {
317       CGUIListContainer *list = (CGUIListContainer *)pGUIControl;
318       if (list->m_spinControl)
319       {
320         list->m_spinControl->SetParentControl(pGroup);
321         if (pGroup)
322           pGroup->AddControl(list->m_spinControl);
323         else
324           Add(list->m_spinControl);
325         list->m_spinControl = NULL;
326       }
327     }
328     if (pGUIControl->GetControlType() == CGUIControl::GUICONTAINER_PANEL)
329     {
330       CGUIPanelContainer *panel = (CGUIPanelContainer *)pGUIControl;
331       if (panel->m_spinControl)
332       {
333         panel->m_spinControl->SetParentControl(pGroup);
334         if (pGroup)
335           pGroup->AddControl(panel->m_spinControl);
336         else
337           Add(panel->m_spinControl);
338         panel->m_spinControl = NULL;
339       }
340       if (panel->m_largePanel)
341       {
342         panel->m_largePanel->SetParentControl(pGroup);
343         if (pGroup)
344           pGroup->AddControl(panel->m_largePanel);
345         else
346           Add(panel->m_largePanel);
347         panel->m_largePanel = NULL;
348       }
349     }
350 #endif
351     // if the new control is a group, then add it's controls
352     if (pGUIControl->IsGroup())
353     {
354       TiXmlElement *pSubControl = pControl->FirstChildElement("control");
355       while (pSubControl)
356       {
357         LoadControl(pSubControl, (CGUIControlGroup *)pGUIControl);
358         pSubControl = pSubControl->NextSiblingElement("control");
359       }
360     }
361   }
362 }
363
364 void CGUIWindow::OnWindowLoaded() 
365 {
366   DynamicResourceAlloc(true);
367 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
368   // we hook up the controlgroup navigation as desired
369   if (g_SkinInfo.GetVersion() < 2.1)
370   { // run through controls, and check navigation into a controlgroup
371     for (ivecControls i = m_vecControls.begin(); i != m_vecControls.end(); ++i)
372     {
373       CGUIControl *group = *i;
374       if (group->IsGroup())
375       {
376         // first thing first: We have to have a unique id
377         if (!group->GetID())
378         {
379           DWORD id = 9000;
380           while (GetControl(id++) && id < 9100)
381             ;
382           group->SetID(id);
383         }
384       }
385     }
386   }
387 #endif
388 }
389
390 void CGUIWindow::SetPosition(float posX, float posY)
391 {
392   m_posX = posX;
393   m_posY = posY;
394 }
395
396 void CGUIWindow::CenterWindow()
397 {
398   if (m_bRelativeCoords)
399   {
400     m_posX = (g_settings.m_ResInfo[m_coordsRes].iWidth - GetWidth()) / 2;
401     m_posY = (g_settings.m_ResInfo[m_coordsRes].iHeight - GetHeight()) / 2;
402   }
403 }
404
405 void CGUIWindow::Render()
406 {
407   // If we're rendering from a different thread, then we should wait for the main
408   // app thread to finish AllocResources(), as dynamic resources (images in particular)
409   // will try and be allocated from 2 different threads, which causes nasty things
410   // to occur.
411   if (!m_WindowAllocated) return;
412
413   // find our origin point
414   float posX = m_posX;
415   float posY = m_posY;
416   for (unsigned int i = 0; i < m_origins.size(); i++)
417   {
418     // no condition implies true
419     if (!m_origins[i].condition || g_infoManager.GetBool(m_origins[i].condition, GetID()))
420     { // found origin
421       posX = m_origins[i].x;
422       posY = m_origins[i].y;
423       break;
424     }
425   }
426   g_graphicsContext.SetScalingResolution(m_coordsRes, posX, posY, m_needsScaling);
427   if (m_hasCamera)
428     g_graphicsContext.SetCameraPosition(m_camera);
429
430   DWORD currentTime = timeGetTime();
431   // render our window animation - returns false if it needs to stop rendering
432   if (!RenderAnimation(currentTime))
433     return;
434
435   for (int i = 0; i < (int)m_vecControls.size(); i++)
436   {
437     CGUIControl *pControl = m_vecControls[i];
438     if (pControl)
439     {
440       pControl->UpdateVisibility();
441       pControl->DoRender(currentTime);
442     }
443   }
444   m_hasRendered = true;
445 }
446
447 bool CGUIWindow::OnAction(const CAction &action)
448 {
449   if (action.wID == ACTION_MOUSE)
450   {
451     OnMouseAction();
452     return true;
453   }
454   CGUIControl *focusedControl = GetFocusedControl();
455   if (focusedControl)
456     return focusedControl->OnAction(action);
457
458   // no control has focus?
459   // set focus to the default control then
460   CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), m_dwDefaultFocusControlID);
461   OnMessage(msg);
462   return false;
463 }
464
465 // OnMouseAction - called by OnAction()
466 void CGUIWindow::OnMouseAction()
467 {
468   // we need to convert the mouse coordinates to window coordinates
469   g_graphicsContext.SetScalingResolution(m_coordsRes, m_posX, m_posY, m_needsScaling);
470   CPoint mousePoint(g_Mouse.posX, g_Mouse.posY);
471   g_graphicsContext.InvertFinalCoords(mousePoint.x, mousePoint.y);
472
473   bool bHandled = false;
474   // check if we have exclusive access
475   if (g_Mouse.GetExclusiveWindowID() == GetID())
476   { // we have exclusive access to the mouse...
477     CGUIControl *pControl = (CGUIControl *)GetControl(g_Mouse.GetExclusiveControlID());
478     if (pControl)
479     { // this control has exclusive access to the mouse
480       HandleMouse(pControl, mousePoint);
481       return;
482     }
483   }
484
485   // run through the controls, and unfocus all those that aren't under the pointer,
486   for (ivecControls i = m_vecControls.begin(); i != m_vecControls.end(); ++i)
487   {
488     CGUIControl *pControl = *i;
489     pControl->UnfocusFromPoint(mousePoint);
490   }
491   // and find which one is under the pointer
492   // go through in reverse order to make sure we start with the ones on top
493   bool controlUnderPointer(false);
494   for (vector<CGUIControl *>::reverse_iterator i = m_vecControls.rbegin(); i != m_vecControls.rend(); ++i)
495   {
496     CGUIControl *pControl = *i;
497     CGUIControl *focusableControl = NULL;
498     CPoint controlPoint;
499     if (pControl->CanFocusFromPoint(mousePoint, &focusableControl, controlPoint))
500     {
501       controlUnderPointer = true;
502       bHandled = HandleMouse(focusableControl, controlPoint);
503       if (bHandled)
504         break;
505     }
506   }
507   if (!bHandled)
508   { // haven't handled this action - call the window message handlers
509     OnMouse(mousePoint);
510   }
511   // and unfocus everything otherwise
512   if (!controlUnderPointer)
513     m_focusedControl = 0;
514 }
515
516 // Handles any mouse actions that are not handled by a control
517 // default is to go back a window on a right click.
518 // This function should be overridden for other windows
519 bool CGUIWindow::OnMouse(const CPoint &point)
520 {
521   if (g_Mouse.bClick[MOUSE_RIGHT_BUTTON])
522   { // no control found to absorb this click - go to previous menu
523     CAction action;
524     action.wID = ACTION_PREVIOUS_MENU;
525     return OnAction(action);
526   }
527   return false;
528 }
529
530 bool CGUIWindow::HandleMouse(CGUIControl *pControl, const CPoint &point)
531 {
532   // Issue the MouseOver event to highlight the item, and perform any pointer changes
533   bool focused = pControl->OnMouseOver(point);
534   if (g_Mouse.bClick[MOUSE_LEFT_BUTTON])
535   { // Left click
536     return pControl->OnMouseClick(MOUSE_LEFT_BUTTON, point);
537   }
538   else if (g_Mouse.bClick[MOUSE_RIGHT_BUTTON])
539   { // Right click
540     return pControl->OnMouseClick(MOUSE_RIGHT_BUTTON, point);
541   }
542   else if (g_Mouse.bClick[MOUSE_MIDDLE_BUTTON])
543   { // Middle click
544     return pControl->OnMouseClick(MOUSE_MIDDLE_BUTTON, point);
545   }
546   else if (g_Mouse.bDoubleClick[MOUSE_LEFT_BUTTON])
547   { // Left double click
548     return pControl->OnMouseDoubleClick(MOUSE_LEFT_BUTTON, point);
549   }
550   else if (g_Mouse.bHold[MOUSE_LEFT_BUTTON] && (g_Mouse.cMickeyX || g_Mouse.cMickeyY))
551   { // Mouse Drag
552     return pControl->OnMouseDrag(CPoint(g_Mouse.cMickeyX, g_Mouse.cMickeyY), point);
553   }
554   else if (g_Mouse.cWheel)
555   { // Mouse wheel
556     return pControl->OnMouseWheel(g_Mouse.cWheel, point);
557   }
558   // no mouse stuff done other than movement - return indicating whether we've focused or not
559   return focused;
560 }
561
562 DWORD CGUIWindow::GetID(void) const
563 {
564   return m_dwWindowId;
565 }
566
567 void CGUIWindow::SetID(DWORD dwID)
568 {
569   m_dwWindowId = dwID;
570 }
571
572 /// \brief Called on window open.
573 ///  * Restores the control state(s)
574 ///  * Sets initial visibility of controls
575 ///  * Queue WindowOpen animation
576 ///  * Set overlay state
577 /// Override this function and do any window-specific initialisation such
578 /// as filling control contents and setting control focus before
579 /// calling the base method.
580 void CGUIWindow::OnInitWindow()
581 {
582   // set our rendered state
583   m_hasRendered = false;
584   // set our initial control visibility before restoring control state and
585   // focusing the default control, and again afterward to make sure that
586   // any controls that depend on the state of the focused control (and or on
587   // control states) are active.
588   SetControlVisibility();
589   RestoreControlStates();
590   SetControlVisibility();
591   QueueAnimation(ANIM_TYPE_WINDOW_OPEN);
592   m_gWindowManager.ShowOverlay(m_overlayState);
593 }
594
595 // Called on window close.
596 //  * Executes the window close animation(s)
597 //  * Saves control state(s)
598 // Override this function and call the base class before doing any dynamic memory freeing
599 void CGUIWindow::OnDeinitWindow(int nextWindowID)
600 {
601   if (nextWindowID != WINDOW_FULLSCREEN_VIDEO)
602   {
603     // Dialog animations are handled in Close() rather than here
604     if (HasAnimation(ANIM_TYPE_WINDOW_CLOSE) && !IsDialog())
605     {
606       // Perform the window out effect
607       QueueAnimation(ANIM_TYPE_WINDOW_CLOSE);
608       while (IsAnimating(ANIM_TYPE_WINDOW_CLOSE))
609       {
610         m_gWindowManager.Process(true);
611       }
612     }
613   }
614   SaveControlStates();
615 }
616
617 bool CGUIWindow::OnMessage(CGUIMessage& message)
618 {
619   switch ( message.GetMessage() )
620   {
621   case GUI_MSG_WINDOW_INIT:
622     {
623       OutputDebugString("------------------- GUI_MSG_WINDOW_INIT ");
624       OutputDebugString(g_localizeStrings.Get(GetID()).c_str());
625       OutputDebugString("------------------- \n");
626       if (m_dynamicResourceAlloc || !m_WindowAllocated) AllocResources();
627       OnInitWindow();
628       return true;
629     }
630     break;
631
632   case GUI_MSG_WINDOW_DEINIT:
633     {
634       OutputDebugString("------------------- GUI_MSG_WINDOW_DEINIT ");
635       OutputDebugString(g_localizeStrings.Get(GetID()).c_str());
636       OutputDebugString("------------------- \n");
637       OnDeinitWindow(message.GetParam1());
638       // now free the window
639       if (m_dynamicResourceAlloc) FreeResources();
640       return true;
641     }
642     break;
643
644   case GUI_MSG_CLICKED:
645     {
646       // a specific control was clicked
647       CLICK_EVENT clickEvent = m_mapClickEvents[ message.GetSenderId() ];
648
649       // determine if there are any handlers for this event
650       if (clickEvent.HasAHandler())
651       {
652         // fire the message to all handlers
653         clickEvent.Fire(message);
654       }
655       break;
656     }
657
658   case GUI_MSG_SELCHANGED:
659     {
660       // a selection within a specific control has changed
661       SELECTED_EVENT selectedEvent = m_mapSelectedEvents[ message.GetSenderId() ];
662
663       // determine if there are any handlers for this event
664       if (selectedEvent.HasAHandler())
665       {
666         // fire the message to all handlers
667         selectedEvent.Fire(message);
668       }
669       break;
670     }
671   case GUI_MSG_FOCUSED:
672     { // a control has been focused
673       if (HasID(message.GetSenderId()))
674       {
675         m_focusedControl = message.GetControlId();
676         return true;
677       }
678       break;
679     }
680   case GUI_MSG_MOVE:
681     {
682       if (HasID(message.GetSenderId()))
683         return OnMove(message.GetControlId(), message.GetParam1());
684       break;
685     }
686   case GUI_MSG_SETFOCUS:
687     {
688 //      CLog::DebugLog("set focus to control:%i window:%i (%i)\n", message.GetControlId(),message.GetSenderId(), GetID());
689       if ( message.GetControlId() )
690       {
691 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
692         if (g_SkinInfo.GetVersion() < 2.1)
693         { // to support the best backwards compatibility, we reproduce the old method here
694           const CGUIControl *oldGroup = NULL;
695           // first unfocus the current control
696           CGUIControl *control = GetFocusedControl();
697           if (control)
698           {
699             CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), control->GetID(), message.GetControlId());
700             control->OnMessage(msgLostFocus);
701             oldGroup = control->GetParentControl();
702           }
703           // get the control to focus
704           CGUIControl* pFocusedControl = GetFirstFocusableControl(message.GetControlId());
705           if (!pFocusedControl) pFocusedControl = (CGUIControl *)GetControl(message.GetControlId());
706
707           // and focus it
708           if (pFocusedControl)
709           {
710             // check for group changes
711             if (pFocusedControl->GetParentControl() && pFocusedControl->GetParentControl() != oldGroup)
712             { // going to a different group, focus the group instead
713               CGUIControlGroup *group = (CGUIControlGroup *)pFocusedControl->GetParentControl();
714               if (group->GetFocusedControlID())
715                 pFocusedControl = group;
716             }
717             return pFocusedControl->OnMessage(message);
718           }
719         }
720         else
721           {
722 #endif
723         // first unfocus the current control
724         CGUIControl *control = GetFocusedControl();
725         if (control)
726         {
727           CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), control->GetID(), message.GetControlId());
728           control->OnMessage(msgLostFocus);
729         }
730
731         // get the control to focus
732         CGUIControl* pFocusedControl = GetFirstFocusableControl(message.GetControlId());
733         if (!pFocusedControl) pFocusedControl = (CGUIControl *)GetControl(message.GetControlId());
734
735         // and focus it
736         if (pFocusedControl)
737           return pFocusedControl->OnMessage(message);
738 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
739         }
740 #endif
741       }
742       return true;
743     }
744     break;
745   case GUI_MSG_NOTIFY_ALL:
746     {
747       // only process those notifications that come from this window, or those intended for every window
748       if (HasID(message.GetSenderId()) || !message.GetSenderId())
749       {
750         if (message.GetParam1() == GUI_MSG_PAGE_CHANGE ||
751             message.GetParam1() == GUI_MSG_REFRESH_THUMBS ||
752             message.GetParam1() == GUI_MSG_REFRESH_LIST)
753         { // alter the message accordingly, and send to all controls
754           for (ivecControls it = m_vecControls.begin(); it != m_vecControls.end(); ++it)
755           {
756             CGUIControl *control = *it;
757             CGUIMessage msg(message.GetParam1(), message.GetControlId(), control->GetID(), message.GetParam2());
758             control->OnMessage(msg);
759           }
760         }
761       }
762     }
763     break;
764   }
765
766   ivecControls i;
767   // Send to the visible matching control first
768   for (i = m_vecControls.begin();i != m_vecControls.end(); ++i)
769   {
770     CGUIControl* pControl = *i;
771     if (pControl->HasVisibleID(message.GetControlId()))
772     {
773       if (pControl->OnMessage(message))
774         return true;
775     }
776   }
777   // Unhandled - send to all matching invisible controls as well
778   bool handled(false);
779   for (i = m_vecControls.begin();i != m_vecControls.end(); ++i)
780   {
781     CGUIControl* pControl = *i;
782     if (pControl->HasID(message.GetControlId()))
783     {
784       if (pControl->OnMessage(message))
785         handled = true;
786     }
787   }
788   return handled;
789 }
790
791 void CGUIWindow::AllocResources(bool forceLoad /*= FALSE */)
792 {
793   LARGE_INTEGER start;
794   QueryPerformanceCounter(&start);
795
796   // load skin xml file
797   bool bHasPath=false; 
798   if (m_xmlFile.Find("\\") > -1 || m_xmlFile.Find("/") > -1 ) 
799     bHasPath = true; 
800   if (m_xmlFile.size() && (forceLoad || m_loadOnDemand || !m_windowLoaded))
801     Load(m_xmlFile,bHasPath);
802
803   LARGE_INTEGER slend;
804   QueryPerformanceCounter(&slend);
805
806   // and now allocate resources
807   g_TextureManager.StartPreLoad();
808   ivecControls i;
809   for (i = m_vecControls.begin();i != m_vecControls.end(); ++i)
810   {
811     CGUIControl* pControl = *i;
812     if (!pControl->IsDynamicallyAllocated()) 
813       pControl->PreAllocResources();
814   }
815   g_TextureManager.EndPreLoad();
816
817   LARGE_INTEGER plend;
818   QueryPerformanceCounter(&plend);
819
820   for (i = m_vecControls.begin();i != m_vecControls.end(); ++i)
821   {
822     CGUIControl* pControl = *i;
823     if (!pControl->IsDynamicallyAllocated()) 
824       pControl->AllocResources();
825   }
826   g_TextureManager.FlushPreLoad();
827
828   LARGE_INTEGER end, freq;
829   QueryPerformanceCounter(&end);
830   QueryPerformanceFrequency(&freq);
831   m_WindowAllocated = true;
832   CLog::DebugLog("Alloc resources: %.2fms (%.2f ms skin load, %.2f ms preload)", 1000.f * (end.QuadPart - start.QuadPart) / freq.QuadPart, 1000.f * (slend.QuadPart - start.QuadPart) / freq.QuadPart, 1000.f * (plend.QuadPart - slend.QuadPart) / freq.QuadPart);
833 }
834
835 void CGUIWindow::FreeResources(bool forceUnload /*= FALSE */)
836 {
837   m_WindowAllocated = false;
838   ivecControls i;
839   for (i = m_vecControls.begin();i != m_vecControls.end(); ++i)
840   {
841     CGUIControl* pControl = *i;
842     pControl->FreeResources();
843   }
844   //g_TextureManager.Dump();
845   // unload the skin
846   if (m_loadOnDemand || forceUnload) ClearAll();
847 }
848
849 void CGUIWindow::DynamicResourceAlloc(bool bOnOff)
850 {
851   m_dynamicResourceAlloc = bOnOff;
852   for (ivecControls i = m_vecControls.begin();i != m_vecControls.end(); ++i)
853   {
854     CGUIControl* pControl = *i;
855     pControl->DynamicResourceAlloc(bOnOff);
856   }
857 }
858
859 void CGUIWindow::Add(CGUIControl* pControl)
860 {
861   m_vecControls.push_back(pControl);
862 }
863
864 void CGUIWindow::Insert(CGUIControl *control, const CGUIControl *insertPoint)
865 {
866   // get the insertion point
867   ivecControls i = m_vecControls.begin();
868   while (i != m_vecControls.end())
869   {
870     if (*i == insertPoint)
871       break;
872     i++;
873   }
874   m_vecControls.insert(i, control);
875 }
876
877 // Note: This routine doesn't delete the control.  It just removes it from the control list
878 bool CGUIWindow::Remove(DWORD dwId)
879 {
880   ivecControls i = m_vecControls.begin();
881   while (i != m_vecControls.end())
882   {
883     CGUIControl* pControl = *i;
884     if (pControl->IsGroup())
885     {
886       CGUIControlGroup *group = (CGUIControlGroup *)pControl;
887       if (group->RemoveControl(dwId))
888         return true;
889     }
890     if (pControl->GetID() == dwId)
891     {
892       m_vecControls.erase(i);
893       return true;
894     }
895     ++i;
896   }
897   return false;
898 }
899
900 void CGUIWindow::ClearAll()
901 {
902   OnWindowUnload();
903
904   for (int i = 0; i < (int)m_vecControls.size(); ++i)
905   {
906     CGUIControl* pControl = m_vecControls[i];
907     delete pControl;
908   }
909   m_vecControls.erase(m_vecControls.begin(), m_vecControls.end());
910   m_windowLoaded = false;
911   m_dynamicResourceAlloc = true;
912 }
913
914 const CGUIControl* CGUIWindow::GetControl(int iControl) const
915 {
916   const CGUIControl* pPotential=NULL;
917   for (int i = 0;i < (int)m_vecControls.size(); ++i)
918   {
919     const CGUIControl* pControl = m_vecControls[i];
920     if (pControl->IsGroup())
921     {
922       CGUIControlGroup *group = (CGUIControlGroup *)pControl;
923       const CGUIControl *control = group->GetControl(iControl);
924       if (control) pControl = control;
925     }
926     if (pControl->GetID() == iControl) 
927     {
928       if (pControl->IsVisible())
929         return pControl;
930       else if (!pPotential)
931         pPotential = pControl;
932     }
933   }
934   return pPotential;
935 }
936
937 int CGUIWindow::GetFocusedControlID() const
938 {
939   if (m_focusedControl) return m_focusedControl;
940   CGUIControl *control = GetFocusedControl();
941   if (control) return control->GetID();
942   return -1;
943 }
944
945 CGUIControl *CGUIWindow::GetFocusedControl() const
946 {
947   for (vector<CGUIControl *>::const_iterator it = m_vecControls.begin(); it != m_vecControls.end(); ++it)
948   {
949     const CGUIControl* pControl = *it;
950     if (pControl->HasFocus())
951     {
952       if (pControl->IsGroup())
953       {
954         CGUIControlGroup *group = (CGUIControlGroup *)pControl;
955         return group->GetFocusedControl();
956       }
957       return (CGUIControl *)pControl;
958     }
959   }
960   return NULL;
961 }
962
963 bool CGUIWindow::Initialize()
964 {
965   return Load(m_xmlFile);
966 }
967
968 void CGUIWindow::SetControlVisibility()
969 {
970   // reset our info manager caches
971   g_infoManager.ResetCache();
972   for (unsigned int i=0; i < m_vecControls.size(); i++)
973   {
974     CGUIControl *pControl = m_vecControls[i];
975     pControl->SetInitialVisibility();
976   }
977 }
978
979 bool CGUIWindow::IsActive() const
980 {
981   return m_gWindowManager.IsWindowActive(GetID());
982 }
983
984 void CGUIWindow::QueueAnimation(ANIMATION_TYPE animType)
985 {
986   if (animType == ANIM_TYPE_WINDOW_OPEN)
987   {
988     if (m_closeAnimation.GetProcess() == ANIM_PROCESS_NORMAL && m_closeAnimation.IsReversible())
989     {
990       m_closeAnimation.QueueAnimation(ANIM_PROCESS_REVERSE);
991       m_showAnimation.ResetAnimation();
992     }
993     else
994     {
995       if (!m_showAnimation.GetCondition() || g_infoManager.GetBool(m_showAnimation.GetCondition(), GetID()))
996         m_showAnimation.QueueAnimation(ANIM_PROCESS_NORMAL);
997       m_closeAnimation.ResetAnimation();
998     }
999   }
1000   if (animType == ANIM_TYPE_WINDOW_CLOSE)
1001   {
1002     if (!m_WindowAllocated || !m_hasRendered) // can't render an animation if we aren't allocated or haven't rendered
1003       return;
1004     if (m_showAnimation.GetProcess() == ANIM_PROCESS_NORMAL && m_showAnimation.IsReversible())
1005     {
1006       m_showAnimation.QueueAnimation(ANIM_PROCESS_REVERSE);
1007       m_closeAnimation.ResetAnimation();
1008     }
1009     else
1010     {
1011       if (!m_closeAnimation.GetCondition() || g_infoManager.GetBool(m_closeAnimation.GetCondition(), GetID()))
1012         m_closeAnimation.QueueAnimation(ANIM_PROCESS_NORMAL);
1013       m_showAnimation.ResetAnimation();
1014     }
1015   }
1016   for (unsigned int i = 0; i < m_vecControls.size(); i++)
1017   {
1018     CGUIControl *pControl = m_vecControls[i];
1019     pControl->QueueAnimation(animType);
1020   }
1021 }
1022
1023 bool CGUIWindow::IsAnimating(ANIMATION_TYPE animType)
1024 {
1025   if (animType == ANIM_TYPE_WINDOW_OPEN)
1026   {
1027     if (m_showAnimation.GetQueuedProcess() == ANIM_PROCESS_NORMAL) return true;
1028     if (m_showAnimation.GetProcess() == ANIM_PROCESS_NORMAL) return true;
1029     if (m_closeAnimation.GetQueuedProcess() == ANIM_PROCESS_REVERSE) return true;
1030     if (m_closeAnimation.GetProcess() == ANIM_PROCESS_REVERSE) return true;
1031   }
1032   else if (animType == ANIM_TYPE_WINDOW_CLOSE)
1033   {
1034     if (m_closeAnimation.GetQueuedProcess() == ANIM_PROCESS_NORMAL) return true;
1035     if (m_closeAnimation.GetProcess() == ANIM_PROCESS_NORMAL) return true;
1036     if (m_showAnimation.GetQueuedProcess() == ANIM_PROCESS_REVERSE) return true;
1037     if (m_showAnimation.GetProcess() == ANIM_PROCESS_REVERSE) return true;
1038   }
1039   for (unsigned int i = 0; i < m_vecControls.size(); i++)
1040   {
1041     CGUIControl *pControl = m_vecControls[i];
1042     if (pControl->IsAnimating(animType)) return true;
1043   }
1044   return false;
1045 }
1046
1047 bool CGUIWindow::RenderAnimation(DWORD time)
1048 {
1049   TransformMatrix transform;
1050   // show animation
1051   m_showAnimation.Animate(time, true);
1052   UpdateStates(m_showAnimation.GetType(), m_showAnimation.GetProcess(), m_showAnimation.GetState());
1053   m_showAnimation.RenderAnimation(transform);
1054   // close animation
1055   m_closeAnimation.Animate(time, true);
1056   UpdateStates(m_closeAnimation.GetType(), m_closeAnimation.GetProcess(), m_closeAnimation.GetState());
1057   m_closeAnimation.RenderAnimation(transform);
1058   g_graphicsContext.SetWindowTransform(transform);
1059   return true;
1060 }
1061
1062 void CGUIWindow::UpdateStates(ANIMATION_TYPE type, ANIMATION_PROCESS currentProcess, ANIMATION_STATE currentState)
1063 {
1064 }
1065
1066 bool CGUIWindow::HasAnimation(ANIMATION_TYPE animType)
1067 {
1068   if (m_showAnimation.GetType() == animType && (!m_showAnimation.GetCondition() || g_infoManager.GetBool(m_showAnimation.GetCondition())))
1069     return true;
1070   else if (m_closeAnimation.GetType() == animType && (!m_closeAnimation.GetCondition() || g_infoManager.GetBool(m_closeAnimation.GetCondition())))
1071     return true;
1072   // Now check the controls to see if we have this animation
1073   for (unsigned int i = 0; i < m_vecControls.size(); i++)
1074     if (m_vecControls[i]->GetAnimation(animType)) return true;
1075   return false;
1076 }
1077
1078 // returns true if the control group with id groupID has controlID as
1079 // its focused control
1080 bool CGUIWindow::ControlGroupHasFocus(int groupID, int controlID)
1081 {
1082   // 1.  Run through and get control with groupID (assume unique)
1083   // 2.  Get it's selected item.
1084 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
1085   if (g_SkinInfo.GetVersion() < 2.1)
1086     groupID += 9000;
1087 #endif
1088   CGUIControl *group = GetFirstFocusableControl(groupID);
1089   if (!group) group = (CGUIControl *)GetControl(groupID);
1090
1091   if (group && group->IsGroup())
1092   {
1093     if (controlID == 0)
1094     { // just want to know if the group is focused
1095       return group->HasFocus();
1096     }
1097     else
1098     {
1099       CGUIMessage message(GUI_MSG_ITEM_SELECTED, GetID(), group->GetID());
1100       group->OnMessage(message);
1101       return (controlID == message.GetParam1());
1102     }
1103   }
1104   return false;
1105 }
1106
1107 void CGUIWindow::SaveControlStates()
1108 {
1109   ResetControlStates();
1110   if (m_saveLastControl)
1111     m_lastControlID = GetFocusedControlID();
1112   for (ivecControls it = m_vecControls.begin(); it != m_vecControls.end(); ++it)
1113     (*it)->SaveStates(m_controlStates);
1114 }
1115
1116 void CGUIWindow::RestoreControlStates()
1117 {
1118   for (vector<CControlState>::iterator it = m_controlStates.begin(); it != m_controlStates.end(); ++it)
1119   {
1120     CGUIMessage message(GUI_MSG_ITEM_SELECT, GetID(), (*it).m_id, (*it).m_data);
1121     OnMessage(message);
1122   }
1123   int focusControl = (m_saveLastControl && m_lastControlID) ? m_lastControlID : m_dwDefaultFocusControlID;
1124 #ifdef PRE_SKIN_VERSION_2_1_COMPATIBILITY
1125   if (g_SkinInfo.GetVersion() < 2.1)
1126   { // skins such as mc360 focus a control in a group by default.
1127     // In 2.1 they should set the focus to the group, rather than the control in the group
1128     CGUIControl *control = GetFirstFocusableControl(focusControl);
1129     if (!control) control = (CGUIControl *)GetControl(focusControl);
1130     if (control && control->GetParentControl())
1131     {
1132       CGUIControlGroup *group = (CGUIControlGroup *)control->GetParentControl();
1133       if (group->GetFocusedControlID())
1134         focusControl = group->GetID();
1135     }
1136   }
1137 #endif
1138   SET_CONTROL_FOCUS(focusControl, 0);
1139 }
1140
1141 void CGUIWindow::ResetControlStates()
1142 {
1143   m_lastControlID = 0;
1144   m_focusedControl = 0;
1145   m_controlStates.clear();
1146 }
1147
1148 // find the first focusable control with this id.
1149 // if no focusable control exists with this id, return NULL
1150 CGUIControl *CGUIWindow::GetFirstFocusableControl(int id)
1151 {
1152   for (ivecControls i = m_vecControls.begin();i != m_vecControls.end(); ++i)
1153   {
1154     CGUIControl* pControl = *i;
1155     if (pControl->IsGroup())
1156     {
1157       CGUIControlGroup *group = (CGUIControlGroup *)pControl;
1158       CGUIControl *control = group->GetFirstFocusableControl(id);
1159       if (control) return control;
1160     }
1161     if (pControl->GetID() == id && pControl->CanFocus())
1162       return pControl;
1163   }
1164   return NULL;
1165 }
1166
1167 bool CGUIWindow::OnMove(int fromControl, int moveAction)
1168 {
1169   const CGUIControl *control = GetFirstFocusableControl(fromControl);
1170   if (!control) control = GetControl(fromControl);
1171   if (!control)
1172   { // no current control??
1173     CLog::Log(LOGERROR, "Unable to find control %i in window %i", fromControl, GetID());
1174     return false;
1175   }
1176   vector<int> moveHistory;
1177   int nextControl = fromControl;
1178   while (control)
1179   { // grab the next control direction
1180     moveHistory.push_back(nextControl);
1181     nextControl = control->GetNextControl(moveAction);
1182     // check our history - if the nextControl is in it, we can't focus it
1183     for (unsigned int i = 0; i < moveHistory.size(); i++)
1184     {
1185       if (nextControl == moveHistory[i])
1186         return false; // no control to focus so do nothing
1187     }
1188     control = GetFirstFocusableControl(nextControl);
1189     if (control)
1190       break;  // found a focusable control
1191     control = GetControl(nextControl); // grab the next control and try again
1192   }
1193   if (!control)
1194     return false;   // no control to focus
1195   // if we get here we have our new control so focus it (and unfocus the current control)
1196   SET_CONTROL_FOCUS(nextControl, 0);
1197   return true;
1198 }
1199
1200 void CGUIWindow::SetDefaults()
1201 {
1202   m_renderOrder = 0;
1203   m_saveLastControl = true;
1204   m_dwDefaultFocusControlID = 0;
1205   m_bRelativeCoords = false;
1206   m_posX = m_posY = m_width = m_height = 0;
1207   m_overlayState = OVERLAY_STATE_PARENT_WINDOW;   // Use parent or previous window's state
1208   m_visibleCondition = 0;
1209   m_previousWindow = WINDOW_INVALID;
1210   m_showAnimation.Reset();
1211   m_closeAnimation.Reset();
1212   m_origins.clear();
1213   m_hasCamera = false;
1214 }
1215
1216 FRECT CGUIWindow::GetScaledBounds() const
1217 {
1218   CSingleLock lock(g_graphicsContext);
1219   g_graphicsContext.SetScalingResolution(m_coordsRes, m_posX, m_posY, m_needsScaling);
1220   FRECT rect = {0, 0, m_width, m_height};
1221   float z = 0;
1222   g_graphicsContext.ScaleFinalCoords(rect.left, rect.top, z);
1223   g_graphicsContext.ScaleFinalCoords(rect.right, rect.bottom, z);
1224   return rect;
1225 }
1226
1227 void CGUIWindow::GetContainers(vector<CGUIControl *> &containers) const
1228 {
1229   for (ciControls it = m_vecControls.begin();it != m_vecControls.end(); ++it)
1230   {
1231     if ((*it)->IsContainer())
1232       containers.push_back(*it);
1233     else if ((*it)->IsGroup())
1234       ((CGUIControlGroup *)(*it))->GetContainers(containers);
1235   }
1236 }
1237
1238 #ifdef _DEBUG
1239 void CGUIWindow::DumpTextureUse()
1240 {
1241   CLog::Log(LOGDEBUG, __FUNCTION__" for window %i", GetID());
1242   for (ivecControls it = m_vecControls.begin();it != m_vecControls.end(); ++it)
1243   {
1244     (*it)->DumpTextureUse();
1245   }
1246 }
1247 #endif