Fix keymap.
[vuplus_xbmc] / xbmc / guilib / GUIWindow.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 "system.h"
22 #include "GUIWindow.h"
23 #include "GUIWindowManager.h"
24 #include "Key.h"
25 #include "LocalizeStrings.h"
26 #include "GUIControlFactory.h"
27 #include "GUIControlGroup.h"
28 #include "GUIControlProfiler.h"
29 #ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY
30 #include "GUIEditControl.h"
31 #endif
32
33 #include "addons/Skin.h"
34 #include "GUIInfoManager.h"
35 #include "utils/log.h"
36 #include "threads/SingleLock.h"
37 #include "utils/TimeUtils.h"
38 #include "input/ButtonTranslator.h"
39 #include "utils/XMLUtils.h"
40 #include "GUIAudioManager.h"
41 #include "Application.h"
42 #include "ApplicationMessenger.h"
43 #include "utils/Variant.h"
44 #include "utils/StringUtils.h"
45
46 #ifdef HAS_PERFORMANCE_SAMPLE
47 #include "utils/PerformanceSample.h"
48 #endif
49
50 using namespace std;
51
52 bool CGUIWindow::icompare::operator()(const CStdString &s1, const CStdString &s2) const
53 {
54   return StringUtils::CompareNoCase(s1, s2) < 0;
55 }
56
57 CGUIWindow::CGUIWindow(int id, const CStdString &xmlFile)
58 {
59   SetID(id);
60   SetProperty("xmlfile", xmlFile);
61   m_lastControlID = 0;
62   m_overlayState = OVERLAY_STATE_PARENT_WINDOW;   // Use parent or previous window's state
63   m_isDialog = false;
64   m_needsScaling = true;
65   m_windowLoaded = false;
66   m_loadType = LOAD_EVERY_TIME;
67   m_closing = false;
68   m_active = false;
69   m_renderOrder = 0;
70   m_dynamicResourceAlloc = true;
71   m_previousWindow = WINDOW_INVALID;
72   m_animationsEnabled = true;
73   m_manualRunActions = false;
74   m_exclusiveMouseControl = 0;
75   m_clearBackground = 0xff000000; // opaque black -> always clear
76   m_windowXMLRootElement = NULL;
77 }
78
79 CGUIWindow::~CGUIWindow(void)
80 {
81   delete m_windowXMLRootElement;
82 }
83
84 bool CGUIWindow::Load(const CStdString& strFileName, bool bContainsPath)
85 {
86 #ifdef HAS_PERFORMANCE_SAMPLE
87   CPerformanceSample aSample("WindowLoad-" + strFileName, true);
88 #endif
89
90   if (m_windowLoaded || g_SkinInfo == NULL)
91     return true;      // no point loading if it's already there
92
93 #ifdef _DEBUG
94   int64_t start;
95   start = CurrentHostCounter();
96 #endif
97   const char* strLoadType;
98   switch (m_loadType)
99   {
100   case LOAD_ON_GUI_INIT:
101     strLoadType = "LOAD_ON_GUI_INIT";
102     break;
103   case KEEP_IN_MEMORY:
104     strLoadType = "KEEP_IN_MEMORY";
105     break;
106   case LOAD_EVERY_TIME:
107   default:
108     strLoadType = "LOAD_EVERY_TIME";
109     break;
110   }
111   CLog::Log(LOGINFO, "Loading skin file: %s, load type: %s", strFileName.c_str(), strLoadType);
112   
113   // Find appropriate skin folder + resolution to load from
114   CStdString strPath;
115   CStdString strLowerPath;
116   if (bContainsPath)
117     strPath = strFileName;
118   else
119   {
120     // FIXME: strLowerPath needs to eventually go since resToUse can get incorrectly overridden
121     std::string strFileNameLower = strFileName;
122     StringUtils::ToLower(strFileNameLower);
123     strLowerPath =  g_SkinInfo->GetSkinPath(strFileNameLower, &m_coordsRes);
124     strPath = g_SkinInfo->GetSkinPath(strFileName, &m_coordsRes);
125   }
126
127   bool ret = LoadXML(strPath.c_str(), strLowerPath.c_str());
128
129 #ifdef _DEBUG
130   int64_t end, freq;
131   end = CurrentHostCounter();
132   freq = CurrentHostFrequency();
133   CLog::Log(LOGDEBUG,"Load %s: %.2fms", GetProperty("xmlfile").c_str(), 1000.f * (end - start) / freq);
134 #endif
135   return ret;
136 }
137
138 bool CGUIWindow::LoadXML(const CStdString &strPath, const CStdString &strLowerPath)
139 {
140   // load window xml if we don't have it stored yet
141   if (!m_windowXMLRootElement)
142   {
143     CXBMCTinyXML xmlDoc;
144     std::string strPathLower = strPath;
145     StringUtils::ToLower(strPathLower);
146     if (!xmlDoc.LoadFile(strPath) && !xmlDoc.LoadFile(strPathLower) && !xmlDoc.LoadFile(strLowerPath))
147     {
148       CLog::Log(LOGERROR, "unable to load:%s, Line %d\n%s", strPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
149       SetID(WINDOW_INVALID);
150       return false;
151     }
152     m_windowXMLRootElement = (TiXmlElement*)xmlDoc.RootElement()->Clone();
153   }
154   else
155     CLog::Log(LOGDEBUG, "Using already stored xml root node for %s", strPath.c_str());
156
157   return Load(m_windowXMLRootElement);
158 }
159
160 bool CGUIWindow::Load(TiXmlElement* pRootElement)
161 {
162   if (!pRootElement)
163     return false;
164   
165   if (strcmpi(pRootElement->Value(), "window"))
166   {
167     CLog::Log(LOGERROR, "file : XML file doesnt contain <window>");
168     return false;
169   }
170
171   // we must create copy of root element as we will manipulate it when resolving includes
172   // and we don't want original root element to change
173   pRootElement = (TiXmlElement*)pRootElement->Clone();
174
175   // set the scaling resolution so that any control creation or initialisation can
176   // be done with respect to the correct aspect ratio
177   g_graphicsContext.SetScalingResolution(m_coordsRes, m_needsScaling);
178
179   // Resolve any includes that may be present and save conditions used to do it
180   g_SkinInfo->ResolveIncludes(pRootElement, &m_xmlIncludeConditions);
181   // now load in the skin file
182   SetDefaults();
183
184   CGUIControlFactory::GetInfoColor(pRootElement, "backgroundcolor", m_clearBackground, GetID());
185   CGUIControlFactory::GetActions(pRootElement, "onload", m_loadActions);
186   CGUIControlFactory::GetActions(pRootElement, "onunload", m_unloadActions);
187   CGUIControlFactory::GetHitRect(pRootElement, m_hitRect);
188
189   TiXmlElement *pChild = pRootElement->FirstChildElement();
190   while (pChild)
191   {
192     CStdString strValue = pChild->Value();
193     if (strValue == "type" && pChild->FirstChild())
194     {
195       // if we have are a window type (ie not a dialog), and we have <type>dialog</type>
196       // then make this window act like a dialog
197       if (!IsDialog() && strcmpi(pChild->FirstChild()->Value(), "dialog") == 0)
198         m_isDialog = true;
199     }
200     else if (strValue == "previouswindow" && pChild->FirstChild())
201     {
202       m_previousWindow = CButtonTranslator::TranslateWindow(pChild->FirstChild()->Value());
203     }
204     else if (strValue == "defaultcontrol" && pChild->FirstChild())
205     {
206       const char *always = pChild->Attribute("always");
207       if (always && strcmpi(always, "true") == 0)
208         m_defaultAlways = true;
209       m_defaultControl = atoi(pChild->FirstChild()->Value());
210     }
211     else if (strValue == "visible" && pChild->FirstChild())
212     {
213       CStdString condition;
214       CGUIControlFactory::GetConditionalVisibility(pRootElement, condition);
215       m_visibleCondition = g_infoManager.Register(condition, GetID());
216     }
217     else if (strValue == "animation" && pChild->FirstChild())
218     {
219       CRect rect(0, 0, (float)m_coordsRes.iWidth, (float)m_coordsRes.iHeight);
220       CAnimation anim;
221       anim.Create(pChild, rect, GetID());
222       m_animations.push_back(anim);
223     }
224     else if (strValue == "zorder" && pChild->FirstChild())
225     {
226       m_renderOrder = atoi(pChild->FirstChild()->Value());
227     }
228     else if (strValue == "coordinates")
229     {
230       XMLUtils::GetFloat(pChild, "posx", m_posX);
231       XMLUtils::GetFloat(pChild, "posy", m_posY);
232       XMLUtils::GetFloat(pChild, "left", m_posX);
233       XMLUtils::GetFloat(pChild, "top", m_posY);
234
235       TiXmlElement *originElement = pChild->FirstChildElement("origin");
236       while (originElement)
237       {
238         COrigin origin;
239         originElement->QueryFloatAttribute("x", &origin.x);
240         originElement->QueryFloatAttribute("y", &origin.y);
241         if (originElement->FirstChild())
242           origin.condition = g_infoManager.Register(originElement->FirstChild()->Value(), GetID());
243         m_origins.push_back(origin);
244         originElement = originElement->NextSiblingElement("origin");
245       }
246     }
247     else if (strValue == "camera")
248     { // z is fixed
249       pChild->QueryFloatAttribute("x", &m_camera.x);
250       pChild->QueryFloatAttribute("y", &m_camera.y);
251       m_hasCamera = true;
252     }
253     else if (strValue == "controls")
254     {
255       TiXmlElement *pControl = pChild->FirstChildElement();
256       while (pControl)
257       {
258         if (strcmpi(pControl->Value(), "control") == 0)
259         {
260           LoadControl(pControl, NULL, CRect(0, 0, (float)m_coordsRes.iWidth, (float)m_coordsRes.iHeight));
261         }
262         pControl = pControl->NextSiblingElement();
263       }
264     }
265     else if (strValue == "allowoverlay")
266     {
267       bool overlay = false;
268       if (XMLUtils::GetBoolean(pRootElement, "allowoverlay", overlay))
269         m_overlayState = overlay ? OVERLAY_STATE_SHOWN : OVERLAY_STATE_HIDDEN;
270     }
271
272     pChild = pChild->NextSiblingElement();
273   }
274   LoadAdditionalTags(pRootElement);
275
276   m_windowLoaded = true;
277   OnWindowLoaded();
278   delete pRootElement;
279   return true;
280 }
281
282 void CGUIWindow::LoadControl(TiXmlElement* pControl, CGUIControlGroup *pGroup, const CRect &rect)
283 {
284   // get control type
285   CGUIControlFactory factory;
286
287   CGUIControl* pGUIControl = factory.Create(GetID(), rect, pControl);
288   if (pGUIControl)
289   {
290     float maxX = pGUIControl->GetXPosition() + pGUIControl->GetWidth();
291     if (maxX > m_width)
292     {
293       m_width = maxX;
294     }
295
296     float maxY = pGUIControl->GetYPosition() + pGUIControl->GetHeight();
297     if (maxY > m_height)
298     {
299       m_height = maxY;
300     }
301     // if we are in a group, add to the group, else add to our window
302     if (pGroup)
303       pGroup->AddControl(pGUIControl);
304     else
305       AddControl(pGUIControl);
306     // if the new control is a group, then add it's controls
307     if (pGUIControl->IsGroup())
308     {
309       CGUIControlGroup *grp = (CGUIControlGroup *)pGUIControl;
310       TiXmlElement *pSubControl = pControl->FirstChildElement("control");
311       CRect grpRect(grp->GetXPosition(), grp->GetYPosition(),
312                     grp->GetXPosition() + grp->GetWidth(), grp->GetYPosition() + grp->GetHeight());
313       while (pSubControl)
314       {
315         LoadControl(pSubControl, grp, grpRect);
316         pSubControl = pSubControl->NextSiblingElement("control");
317       }
318     }
319   }
320 }
321
322 void CGUIWindow::OnWindowLoaded()
323 {
324   DynamicResourceAlloc(true);
325 }
326
327 void CGUIWindow::CenterWindow()
328 {
329   m_posX = (m_coordsRes.iWidth - GetWidth()) / 2;
330   m_posY = (m_coordsRes.iHeight - GetHeight()) / 2;
331 }
332
333 void CGUIWindow::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
334 {
335   g_graphicsContext.SetRenderingResolution(m_coordsRes, m_needsScaling);
336   g_graphicsContext.AddGUITransform();
337   CGUIControlGroup::DoProcess(currentTime, dirtyregions);
338   g_graphicsContext.RemoveTransform();
339
340   // check if currently focused control can have it
341   // and fallback to default control if not
342   CGUIControl* focusedControl = GetFocusedControl();
343   if (focusedControl && !focusedControl->CanFocus())
344     SET_CONTROL_FOCUS(m_defaultControl, 0);
345 }
346
347 void CGUIWindow::DoRender()
348 {
349   // If we're rendering from a different thread, then we should wait for the main
350   // app thread to finish AllocResources(), as dynamic resources (images in particular)
351   // will try and be allocated from 2 different threads, which causes nasty things
352   // to occur.
353   if (!m_bAllocated) return;
354
355   g_graphicsContext.SetRenderingResolution(m_coordsRes, m_needsScaling);
356
357   g_graphicsContext.AddGUITransform();
358   CGUIControlGroup::DoRender();
359   g_graphicsContext.RemoveTransform();
360
361   if (CGUIControlProfiler::IsRunning()) CGUIControlProfiler::Instance().EndFrame();
362 }
363
364 void CGUIWindow::AfterRender()
365 {
366   // Check to see if we should close at this point
367   // We check after the controls have finished rendering, as we may have to close due to
368   // the controls rendering after the window has finished it's animation
369   // we call the base class instead of this class so that we can find the change
370   if (m_closing && !CGUIControlGroup::IsAnimating(ANIM_TYPE_WINDOW_CLOSE))
371     Close(true);
372
373 }
374
375 void CGUIWindow::Close_Internal(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/)
376 {
377   CSingleLock lock(g_graphicsContext);
378
379   if (!m_active)
380     return;
381
382   forceClose |= (nextWindowID == WINDOW_FULLSCREEN_VIDEO);
383   if (!forceClose && HasAnimation(ANIM_TYPE_WINDOW_CLOSE))
384   {
385     if (!m_closing)
386     {
387       if (enableSound && IsSoundEnabled())
388         g_audioManager.PlayWindowSound(GetID(), SOUND_DEINIT);
389
390       // Perform the window out effect
391       QueueAnimation(ANIM_TYPE_WINDOW_CLOSE);
392       m_closing = true;
393     }
394     return;
395   }
396
397   m_closing = false;
398   CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0, nextWindowID);
399   OnMessage(msg);
400 }
401
402 void CGUIWindow::Close(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/, bool bWait /* = true */)
403 {
404   if (!g_application.IsCurrentThread())
405   {
406     // make sure graphics lock is not held
407     CSingleExit leaveIt(g_graphicsContext);
408     CApplicationMessenger::Get().Close(this, forceClose, bWait, nextWindowID, enableSound);
409   }
410   else
411     Close_Internal(forceClose, nextWindowID, enableSound);
412 }
413
414 bool CGUIWindow::OnAction(const CAction &action)
415 {
416   if (action.IsMouse() || action.IsGesture())
417     return EVENT_RESULT_UNHANDLED != OnMouseAction(action);
418
419   CGUIControl *focusedControl = GetFocusedControl();
420   if (focusedControl)
421   {
422     if (focusedControl->OnAction(action))
423       return true;
424   }
425   else
426   {
427     // no control has focus?
428     // set focus to the default control then
429     CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), m_defaultControl);
430     OnMessage(msg);
431   }
432
433   // default implementations
434   if (action.GetID() == ACTION_NAV_BACK || action.GetID() == ACTION_PREVIOUS_MENU)
435     return OnBack(action.GetID());
436
437   return false;
438 }
439
440 CPoint CGUIWindow::GetPosition() const
441 {
442   for (unsigned int i = 0; i < m_origins.size(); i++)
443   {
444     // no condition implies true
445     if (!m_origins[i].condition || m_origins[i].condition->Get())
446     { // found origin
447       return CPoint(m_origins[i].x, m_origins[i].y);
448     }
449   }
450   return CGUIControlGroup::GetPosition();
451 }
452
453 // OnMouseAction - called by OnAction()
454 EVENT_RESULT CGUIWindow::OnMouseAction(const CAction &action)
455 {
456   g_graphicsContext.SetScalingResolution(m_coordsRes, m_needsScaling);
457   CPoint mousePoint(action.GetAmount(0), action.GetAmount(1));
458   g_graphicsContext.InvertFinalCoords(mousePoint.x, mousePoint.y);
459
460   // create the mouse event
461   CMouseEvent event(action.GetID(), action.GetHoldTime(), action.GetAmount(2), action.GetAmount(3));
462   if (m_exclusiveMouseControl)
463   {
464     CGUIControl *child = (CGUIControl *)GetControl(m_exclusiveMouseControl);
465     if (child)
466     {
467       CPoint renderPos = child->GetRenderPosition() - CPoint(child->GetXPosition(), child->GetYPosition());
468       return child->OnMouseEvent(mousePoint - renderPos, event);
469     }
470   }
471
472   UnfocusFromPoint(mousePoint);
473
474   return SendMouseEvent(mousePoint, event);
475 }
476
477 EVENT_RESULT CGUIWindow::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
478 {
479   if (event.m_id == ACTION_MOUSE_RIGHT_CLICK)
480   { // no control found to absorb this click - go to previous menu
481     return OnAction(CAction(ACTION_PREVIOUS_MENU)) ? EVENT_RESULT_HANDLED : EVENT_RESULT_UNHANDLED;
482   }
483   return EVENT_RESULT_UNHANDLED;
484 }
485
486 /// \brief Called on window open.
487 ///  * Restores the control state(s)
488 ///  * Sets initial visibility of controls
489 ///  * Queue WindowOpen animation
490 ///  * Set overlay state
491 /// Override this function and do any window-specific initialisation such
492 /// as filling control contents and setting control focus before
493 /// calling the base method.
494 void CGUIWindow::OnInitWindow()
495 {
496   //  Play the window specific init sound
497   if (IsSoundEnabled())
498     g_audioManager.PlayWindowSound(GetID(), SOUND_INIT);
499
500   // set our rendered state
501   m_hasProcessed = false;
502   m_closing = false;
503   m_active = true;
504   ResetAnimations();  // we need to reset our animations as those windows that don't dynamically allocate
505                       // need their anims reset. An alternative solution is turning off all non-dynamic
506                       // allocation (which in some respects may be nicer, but it kills hdd spindown and the like)
507
508   // set our initial control visibility before restoring control state and
509   // focusing the default control, and again afterward to make sure that
510   // any controls that depend on the state of the focused control (and or on
511   // control states) are active.
512   SetInitialVisibility();
513   RestoreControlStates();
514   SetInitialVisibility();
515   QueueAnimation(ANIM_TYPE_WINDOW_OPEN);
516   g_windowManager.ShowOverlay(m_overlayState);
517
518   if (!m_manualRunActions)
519   {
520     RunLoadActions();
521   }
522 }
523
524 // Called on window close.
525 //  * Saves control state(s)
526 // Override this function and call the base class before doing any dynamic memory freeing
527 void CGUIWindow::OnDeinitWindow(int nextWindowID)
528 {
529   if (!m_manualRunActions)
530   {
531     RunUnloadActions();
532   }
533
534   SaveControlStates();
535   m_active = false;
536 }
537
538 bool CGUIWindow::OnMessage(CGUIMessage& message)
539 {
540   switch ( message.GetMessage() )
541   {
542   case GUI_MSG_WINDOW_LOAD:
543     {
544       Initialize();
545       return true;
546     }
547     break;
548       
549   case GUI_MSG_WINDOW_INIT:
550     {
551       CLog::Log(LOGDEBUG, "------ Window Init (%s) ------", GetProperty("xmlfile").c_str());
552       if (m_dynamicResourceAlloc || !m_bAllocated) AllocResources();
553       OnInitWindow();
554       return true;
555     }
556     break;
557
558   case GUI_MSG_WINDOW_DEINIT:
559     {
560       CLog::Log(LOGDEBUG, "------ Window Deinit (%s) ------", GetProperty("xmlfile").c_str());
561       OnDeinitWindow(message.GetParam1());
562       // now free the window
563       if (m_dynamicResourceAlloc) FreeResources();
564       return true;
565     }
566     break;
567
568   case GUI_MSG_CLICKED:
569     {
570       // a specific control was clicked
571       CLICK_EVENT clickEvent = m_mapClickEvents[ message.GetSenderId() ];
572
573       // determine if there are any handlers for this event
574       if (clickEvent.HasAHandler())
575       {
576         // fire the message to all handlers
577         clickEvent.Fire(message);
578       }
579       break;
580     }
581   
582   case GUI_MSG_UNFOCUS_ALL:
583     {
584       //unfocus the current focused control in this window
585       CGUIControl *control = GetFocusedControl();
586       if(control)
587       {
588         //tell focused control that it has lost the focus
589         CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), control->GetID(), control->GetID());
590         control->OnMessage(msgLostFocus);
591         CLog::Log(LOGDEBUG, "Unfocus WindowID: %i, ControlID: %i",GetID(), control->GetID());
592       }
593       return true;
594     }
595
596   case GUI_MSG_SELCHANGED:
597     {
598       // a selection within a specific control has changed
599       SELECTED_EVENT selectedEvent = m_mapSelectedEvents[ message.GetSenderId() ];
600
601       // determine if there are any handlers for this event
602       if (selectedEvent.HasAHandler())
603       {
604         // fire the message to all handlers
605         selectedEvent.Fire(message);
606       }
607       break;
608     }
609   case GUI_MSG_FOCUSED:
610     { // a control has been focused
611       if (HasID(message.GetSenderId()))
612       {
613         m_focusedControl = message.GetControlId();
614         return true;
615       }
616       break;
617     }
618   case GUI_MSG_LOSTFOCUS:
619     {
620       // nothing to do at the window level when we lose focus
621       return true;
622     }
623   case GUI_MSG_MOVE:
624     {
625       if (HasID(message.GetSenderId()))
626         return OnMove(message.GetControlId(), message.GetParam1());
627       break;
628     }
629   case GUI_MSG_SETFOCUS:
630     {
631 //      CLog::Log(LOGDEBUG,"set focus to control:%i window:%i (%i)\n", message.GetControlId(),message.GetSenderId(), GetID());
632       if ( message.GetControlId() )
633       {
634         // first unfocus the current control
635         CGUIControl *control = GetFocusedControl();
636         if (control)
637         {
638           CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), control->GetID(), message.GetControlId());
639           control->OnMessage(msgLostFocus);
640         }
641
642         // get the control to focus
643         CGUIControl* pFocusedControl = GetFirstFocusableControl(message.GetControlId());
644         if (!pFocusedControl) pFocusedControl = (CGUIControl *)GetControl(message.GetControlId());
645
646         // and focus it
647         if (pFocusedControl)
648           return pFocusedControl->OnMessage(message);
649       }
650       return true;
651     }
652     break;
653   case GUI_MSG_EXCLUSIVE_MOUSE:
654     {
655       m_exclusiveMouseControl = message.GetSenderId();
656       return true;
657     }
658     break;
659   case GUI_MSG_GESTURE_NOTIFY:
660     {
661       CAction action(ACTION_GESTURE_NOTIFY, 0, (float)message.GetParam1(), (float)message.GetParam2(), 0, 0);
662       EVENT_RESULT result = OnMouseAction(action);
663       message.SetParam1(result);
664       return result != EVENT_RESULT_UNHANDLED;
665     }
666   case GUI_MSG_ADD_CONTROL:
667     {
668       if (message.GetPointer())
669       {
670         CGUIControl *control = (CGUIControl *)message.GetPointer();
671         control->AllocResources();
672         AddControl(control);
673       }
674       return true;
675     }
676   case GUI_MSG_REMOVE_CONTROL:
677     {
678       if (message.GetPointer())
679       {
680         CGUIControl *control = (CGUIControl *)message.GetPointer();
681         RemoveControl(control);
682         control->FreeResources(true);
683         delete control;
684       }
685       return true;
686     }
687   case GUI_MSG_NOTIFY_ALL:
688     {
689       // only process those notifications that come from this window, or those intended for every window
690       if (HasID(message.GetSenderId()) || !message.GetSenderId())
691       {
692         if (message.GetParam1() == GUI_MSG_PAGE_CHANGE ||
693             message.GetParam1() == GUI_MSG_REFRESH_THUMBS ||
694             message.GetParam1() == GUI_MSG_REFRESH_LIST ||
695             message.GetParam1() == GUI_MSG_WINDOW_RESIZE)
696         { // alter the message accordingly, and send to all controls
697           for (iControls it = m_children.begin(); it != m_children.end(); ++it)
698           {
699             CGUIControl *control = *it;
700             CGUIMessage msg(message.GetParam1(), message.GetControlId(), control->GetID(), message.GetParam2());
701             control->OnMessage(msg);
702           }
703         }
704       }
705     }
706     break;
707   }
708
709   return SendControlMessage(message);
710 }
711
712 bool CGUIWindow::NeedXMLReload()
713 {
714   return !m_windowLoaded || g_infoManager.ConditionsChangedValues(m_xmlIncludeConditions);
715 }
716
717 void CGUIWindow::AllocResources(bool forceLoad /*= FALSE */)
718 {
719   CSingleLock lock(g_graphicsContext);
720
721 #ifdef _DEBUG
722   int64_t start;
723   start = CurrentHostCounter();
724 #endif
725   // use forceLoad to determine if xml file needs loading
726   forceLoad |= NeedXMLReload() || (m_loadType == LOAD_EVERY_TIME);
727
728   // if window is loaded and load is forced we have to free window resources first
729   if (m_windowLoaded && forceLoad)
730     FreeResources(true);
731
732   if (forceLoad)
733   {
734     CStdString xmlFile = GetProperty("xmlfile").asString();
735     if (xmlFile.size())
736     {
737       bool bHasPath = xmlFile.find("\\") != std::string::npos || xmlFile.find("/") != std::string::npos;
738       Load(xmlFile,bHasPath);
739     }
740   }
741
742   int64_t slend;
743   slend = CurrentHostCounter();
744
745   // and now allocate resources
746   CGUIControlGroup::AllocResources();
747
748 #ifdef _DEBUG
749   int64_t end, freq;
750   end = CurrentHostCounter();
751   freq = CurrentHostFrequency();
752   if (forceLoad)
753     CLog::Log(LOGDEBUG,"Alloc resources: %.2fms  (%.2f ms skin load)", 1000.f * (end - start) / freq, 1000.f * (slend - start) / freq);
754   else
755   {
756     CLog::Log(LOGDEBUG,"Window %s was already loaded", GetProperty("xmlfile").c_str());
757     CLog::Log(LOGDEBUG,"Alloc resources: %.2fm", 1000.f * (end - start) / freq);
758   }
759 #endif
760   m_bAllocated = true;
761 }
762
763 void CGUIWindow::FreeResources(bool forceUnload /*= FALSE */)
764 {
765   m_bAllocated = false;
766   CGUIControlGroup::FreeResources();
767   //g_TextureManager.Dump();
768   // unload the skin
769   if (m_loadType == LOAD_EVERY_TIME || forceUnload) ClearAll();
770   if (forceUnload)
771   {
772     delete m_windowXMLRootElement;
773     m_windowXMLRootElement = NULL;
774     m_xmlIncludeConditions.clear();
775   }
776 }
777
778 void CGUIWindow::DynamicResourceAlloc(bool bOnOff)
779 {
780   m_dynamicResourceAlloc = bOnOff;
781   CGUIControlGroup::DynamicResourceAlloc(bOnOff);
782 }
783
784 void CGUIWindow::ClearAll()
785 {
786   OnWindowUnload();
787   CGUIControlGroup::ClearAll();
788   m_windowLoaded = false;
789   m_dynamicResourceAlloc = true;
790   m_visibleCondition.reset();
791 }
792
793 bool CGUIWindow::Initialize()
794 {
795   if (!g_windowManager.Initialized())
796     return false;     // can't load if we have no skin yet
797   if(!NeedXMLReload())
798     return true;
799   if(g_application.IsCurrentThread())
800     AllocResources();
801   else
802   {
803     // if not app thread, send gui msg via app messenger
804     // and wait for results, so windowLoaded flag would be updated
805     CGUIMessage msg(GUI_MSG_WINDOW_LOAD, 0, 0);
806     CApplicationMessenger::Get().SendGUIMessage(msg, GetID(), true);
807   }
808   return m_windowLoaded;
809 }
810
811 void CGUIWindow::SetInitialVisibility()
812 {
813   // reset our info manager caches
814   g_infoManager.ResetCache();
815   CGUIControlGroup::SetInitialVisibility();
816 }
817
818 bool CGUIWindow::IsActive() const
819 {
820   return g_windowManager.IsWindowActive(GetID());
821 }
822
823 bool CGUIWindow::CheckAnimation(ANIMATION_TYPE animType)
824 {
825   // special cases first
826   if (animType == ANIM_TYPE_WINDOW_CLOSE)
827   {
828     if (!m_bAllocated || !HasProcessed()) // can't process an animation if we aren't allocated or haven't processed
829       return false;
830     // make sure we update our visibility prior to queuing the window close anim
831     for (unsigned int i = 0; i < m_children.size(); i++)
832       m_children[i]->UpdateVisibility();
833   }
834   return true;
835 }
836
837 bool CGUIWindow::IsAnimating(ANIMATION_TYPE animType)
838 {
839   if (!m_animationsEnabled)
840     return false;
841   if (animType == ANIM_TYPE_WINDOW_CLOSE)
842     return m_closing;
843   return CGUIControlGroup::IsAnimating(animType);
844 }
845
846 bool CGUIWindow::Animate(unsigned int currentTime)
847 {
848   if (m_animationsEnabled)
849     return CGUIControlGroup::Animate(currentTime);
850   else
851   {
852     m_transform.Reset();
853     return false;
854   }
855 }
856
857 void CGUIWindow::DisableAnimations()
858 {
859   m_animationsEnabled = false;
860 }
861
862 // returns true if the control group with id groupID has controlID as
863 // its focused control
864 bool CGUIWindow::ControlGroupHasFocus(int groupID, int controlID)
865 {
866   // 1.  Run through and get control with groupID (assume unique)
867   // 2.  Get it's selected item.
868   CGUIControl *group = GetFirstFocusableControl(groupID);
869   if (!group) group = (CGUIControl *)GetControl(groupID);
870
871   if (group && group->IsGroup())
872   {
873     if (controlID == 0)
874     { // just want to know if the group is focused
875       return group->HasFocus();
876     }
877     else
878     {
879       CGUIMessage message(GUI_MSG_ITEM_SELECTED, GetID(), group->GetID());
880       group->OnMessage(message);
881       return (controlID == (int) message.GetParam1());
882     }
883   }
884   return false;
885 }
886
887 void CGUIWindow::SaveControlStates()
888 {
889   ResetControlStates();
890   if (!m_defaultAlways)
891     m_lastControlID = GetFocusedControlID();
892   for (iControls it = m_children.begin(); it != m_children.end(); ++it)
893     (*it)->SaveStates(m_controlStates);
894 }
895
896 void CGUIWindow::RestoreControlStates()
897 {
898   for (vector<CControlState>::iterator it = m_controlStates.begin(); it != m_controlStates.end(); ++it)
899   {
900     CGUIMessage message(GUI_MSG_ITEM_SELECT, GetID(), (*it).m_id, (*it).m_data);
901     OnMessage(message);
902   }
903   int focusControl = (!m_defaultAlways && m_lastControlID) ? m_lastControlID : m_defaultControl;
904   SET_CONTROL_FOCUS(focusControl, 0);
905 }
906
907 void CGUIWindow::ResetControlStates()
908 {
909   m_lastControlID = 0;
910   m_focusedControl = 0;
911   m_controlStates.clear();
912 }
913
914 bool CGUIWindow::OnBack(int actionID)
915 {
916   g_windowManager.PreviousWindow();
917   return true;
918 }
919
920 bool CGUIWindow::OnMove(int fromControl, int moveAction)
921 {
922   const CGUIControl *control = GetFirstFocusableControl(fromControl);
923   if (!control) control = GetControl(fromControl);
924   if (!control)
925   { // no current control??
926     CLog::Log(LOGERROR, "Unable to find control %i in window %u",
927               fromControl, GetID());
928     return false;
929   }
930   vector<int> moveHistory;
931   int nextControl = fromControl;
932   while (control)
933   { // grab the next control direction
934     moveHistory.push_back(nextControl);
935     CGUIAction action;
936     if (!control->GetNavigationAction(moveAction, action))
937       return false;
938     action.ExecuteActions(nextControl, GetParentID());
939     nextControl = action.GetNavigation();
940     if (!nextControl) // 0 isn't valid control id
941       return false;
942     // check our history - if the nextControl is in it, we can't focus it
943     for (unsigned int i = 0; i < moveHistory.size(); i++)
944     {
945       if (nextControl == moveHistory[i])
946         return false; // no control to focus so do nothing
947     }
948     control = GetFirstFocusableControl(nextControl);
949     if (control)
950       break;  // found a focusable control
951     control = GetControl(nextControl); // grab the next control and try again
952   }
953   if (!control)
954     return false;   // no control to focus
955   // if we get here we have our new control so focus it (and unfocus the current control)
956   SET_CONTROL_FOCUS(nextControl, 0);
957   return true;
958 }
959
960 void CGUIWindow::SetDefaults()
961 {
962   m_renderOrder = 0;
963   m_defaultAlways = false;
964   m_defaultControl = 0;
965   m_posX = m_posY = m_width = m_height = 0;
966   m_overlayState = OVERLAY_STATE_PARENT_WINDOW;   // Use parent or previous window's state
967   m_previousWindow = WINDOW_INVALID;
968   m_animations.clear();
969   m_origins.clear();
970   m_hasCamera = false;
971   m_animationsEnabled = true;
972   m_clearBackground = 0xff000000; // opaque black -> clear
973   m_hitRect.SetRect(0, 0, (float)m_coordsRes.iWidth, (float)m_coordsRes.iHeight);
974 }
975
976 CRect CGUIWindow::GetScaledBounds() const
977 {
978   CSingleLock lock(g_graphicsContext);
979   g_graphicsContext.SetScalingResolution(m_coordsRes, m_needsScaling);
980   CPoint pos(GetPosition());
981   CRect rect(pos.x, pos.y, pos.x + m_width, pos.y + m_height);
982   float z = 0;
983   g_graphicsContext.ScaleFinalCoords(rect.x1, rect.y1, z);
984   g_graphicsContext.ScaleFinalCoords(rect.x2, rect.y2, z);
985   return rect;
986 }
987
988 void CGUIWindow::OnEditChanged(int id, CStdString &text)
989 {
990   CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), id);
991   OnMessage(msg);
992   text = msg.GetLabel();
993 }
994
995 bool CGUIWindow::SendMessage(int message, int id, int param1 /* = 0*/, int param2 /* = 0*/)
996 {
997   CGUIMessage msg(message, GetID(), id, param1, param2);
998   return OnMessage(msg);
999 }
1000
1001 void CGUIWindow::DumpTextureUse()
1002 {
1003 #ifdef _DEBUG
1004   CLog::Log(LOGDEBUG, "%s for window %u", __FUNCTION__, GetID());
1005   CGUIControlGroup::DumpTextureUse();
1006 #endif
1007 }
1008
1009 void CGUIWindow::ChangeButtonToEdit(int id, bool singleLabel /* = false*/)
1010 {
1011 #ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY
1012   CGUIControl *name = (CGUIControl *)GetControl(id);
1013   if (name && name->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
1014   { // change it to an edit control
1015     CGUIEditControl *edit = new CGUIEditControl(*(const CGUIButtonControl *)name);
1016     if (edit)
1017     {
1018       if (singleLabel)
1019         edit->SetLabel("");
1020       InsertControl(edit, name);
1021       RemoveControl(name);
1022       name->FreeResources();
1023       delete name;
1024     }
1025   }
1026 #endif
1027 }
1028
1029 void CGUIWindow::SetProperty(const CStdString &strKey, const CVariant &value)
1030 {
1031   CSingleLock lock(*this);
1032   m_mapProperties[strKey] = value;
1033 }
1034
1035 CVariant CGUIWindow::GetProperty(const CStdString &strKey) const
1036 {
1037   CSingleLock lock(*this);
1038   std::map<CStdString, CVariant, icompare>::const_iterator iter = m_mapProperties.find(strKey);
1039   if (iter == m_mapProperties.end())
1040     return CVariant(CVariant::VariantTypeNull);
1041
1042   return iter->second;
1043 }
1044
1045 void CGUIWindow::ClearProperties()
1046 {
1047   CSingleLock lock(*this);
1048   m_mapProperties.clear();
1049 }
1050
1051 void CGUIWindow::SetRunActionsManually()
1052 {
1053   m_manualRunActions = true;
1054 }
1055
1056 void CGUIWindow::RunLoadActions()
1057 {
1058   m_loadActions.ExecuteActions(GetID(), GetParentID());
1059 }
1060
1061 void CGUIWindow::RunUnloadActions()
1062 {
1063   m_unloadActions.ExecuteActions(GetID(), GetParentID());
1064 }
1065
1066 void CGUIWindow::ClearBackground()
1067 {
1068   m_clearBackground.Update();
1069   color_t color = m_clearBackground;
1070   if (color)
1071     g_graphicsContext.Clear(color);
1072 }
1073
1074 void CGUIWindow::SetID(int id)
1075 {
1076   CGUIControlGroup::SetID(id);
1077   m_idRange.clear();
1078   m_idRange.push_back(id);
1079 }
1080
1081 bool CGUIWindow::HasID(int controlID) const
1082 {
1083   for (std::vector<int>::const_iterator it = m_idRange.begin(); it != m_idRange.end() ; ++it)
1084   {
1085     if (controlID == *it)
1086       return true;
1087   }
1088   return false;
1089 }