2 * Copyright (C) 2005-2013 Team XBMC
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)
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.
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/>.
22 #include "GUIWindow.h"
23 #include "GUIWindowManager.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"
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"
46 #ifdef HAS_PERFORMANCE_SAMPLE
47 #include "utils/PerformanceSample.h"
52 bool CGUIWindow::icompare::operator()(const CStdString &s1, const CStdString &s2) const
54 return StringUtils::CompareNoCase(s1, s2) < 0;
57 CGUIWindow::CGUIWindow(int id, const CStdString &xmlFile)
60 SetProperty("xmlfile", xmlFile);
62 m_overlayState = OVERLAY_STATE_PARENT_WINDOW; // Use parent or previous window's state
64 m_needsScaling = true;
65 m_windowLoaded = false;
66 m_loadType = LOAD_EVERY_TIME;
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;
79 CGUIWindow::~CGUIWindow(void)
81 delete m_windowXMLRootElement;
84 bool CGUIWindow::Load(const CStdString& strFileName, bool bContainsPath)
86 #ifdef HAS_PERFORMANCE_SAMPLE
87 CPerformanceSample aSample("WindowLoad-" + strFileName, true);
90 if (m_windowLoaded || g_SkinInfo == NULL)
91 return true; // no point loading if it's already there
95 start = CurrentHostCounter();
97 const char* strLoadType;
100 case LOAD_ON_GUI_INIT:
101 strLoadType = "LOAD_ON_GUI_INIT";
104 strLoadType = "KEEP_IN_MEMORY";
106 case LOAD_EVERY_TIME:
108 strLoadType = "LOAD_EVERY_TIME";
111 CLog::Log(LOGINFO, "Loading skin file: %s, load type: %s", strFileName.c_str(), strLoadType);
113 // Find appropriate skin folder + resolution to load from
115 CStdString strLowerPath;
117 strPath = strFileName;
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);
127 bool ret = LoadXML(strPath.c_str(), strLowerPath.c_str());
131 end = CurrentHostCounter();
132 freq = CurrentHostFrequency();
133 CLog::Log(LOGDEBUG,"Load %s: %.2fms", GetProperty("xmlfile").c_str(), 1000.f * (end - start) / freq);
138 bool CGUIWindow::LoadXML(const CStdString &strPath, const CStdString &strLowerPath)
140 // load window xml if we don't have it stored yet
141 if (!m_windowXMLRootElement)
144 std::string strPathLower = strPath;
145 StringUtils::ToLower(strPathLower);
146 if (!xmlDoc.LoadFile(strPath) && !xmlDoc.LoadFile(strPathLower) && !xmlDoc.LoadFile(strLowerPath))
148 CLog::Log(LOGERROR, "unable to load:%s, Line %d\n%s", strPath.c_str(), xmlDoc.ErrorRow(), xmlDoc.ErrorDesc());
149 SetID(WINDOW_INVALID);
152 m_windowXMLRootElement = (TiXmlElement*)xmlDoc.RootElement()->Clone();
155 CLog::Log(LOGDEBUG, "Using already stored xml root node for %s", strPath.c_str());
157 return Load(m_windowXMLRootElement);
160 bool CGUIWindow::Load(TiXmlElement* pRootElement)
165 if (strcmpi(pRootElement->Value(), "window"))
167 CLog::Log(LOGERROR, "file : XML file doesnt contain <window>");
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();
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);
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
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);
189 TiXmlElement *pChild = pRootElement->FirstChildElement();
192 CStdString strValue = pChild->Value();
193 if (strValue == "type" && pChild->FirstChild())
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)
200 else if (strValue == "previouswindow" && pChild->FirstChild())
202 m_previousWindow = CButtonTranslator::TranslateWindow(pChild->FirstChild()->Value());
204 else if (strValue == "defaultcontrol" && pChild->FirstChild())
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());
211 else if (strValue == "visible" && pChild->FirstChild())
213 CStdString condition;
214 CGUIControlFactory::GetConditionalVisibility(pRootElement, condition);
215 m_visibleCondition = g_infoManager.Register(condition, GetID());
217 else if (strValue == "animation" && pChild->FirstChild())
219 CRect rect(0, 0, (float)m_coordsRes.iWidth, (float)m_coordsRes.iHeight);
221 anim.Create(pChild, rect, GetID());
222 m_animations.push_back(anim);
224 else if (strValue == "zorder" && pChild->FirstChild())
226 m_renderOrder = atoi(pChild->FirstChild()->Value());
228 else if (strValue == "coordinates")
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);
235 TiXmlElement *originElement = pChild->FirstChildElement("origin");
236 while (originElement)
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");
247 else if (strValue == "camera")
249 pChild->QueryFloatAttribute("x", &m_camera.x);
250 pChild->QueryFloatAttribute("y", &m_camera.y);
253 else if (strValue == "controls")
255 TiXmlElement *pControl = pChild->FirstChildElement();
258 if (strcmpi(pControl->Value(), "control") == 0)
260 LoadControl(pControl, NULL, CRect(0, 0, (float)m_coordsRes.iWidth, (float)m_coordsRes.iHeight));
262 pControl = pControl->NextSiblingElement();
265 else if (strValue == "allowoverlay")
267 bool overlay = false;
268 if (XMLUtils::GetBoolean(pRootElement, "allowoverlay", overlay))
269 m_overlayState = overlay ? OVERLAY_STATE_SHOWN : OVERLAY_STATE_HIDDEN;
272 pChild = pChild->NextSiblingElement();
274 LoadAdditionalTags(pRootElement);
276 m_windowLoaded = true;
282 void CGUIWindow::LoadControl(TiXmlElement* pControl, CGUIControlGroup *pGroup, const CRect &rect)
285 CGUIControlFactory factory;
287 CGUIControl* pGUIControl = factory.Create(GetID(), rect, pControl);
290 float maxX = pGUIControl->GetXPosition() + pGUIControl->GetWidth();
296 float maxY = pGUIControl->GetYPosition() + pGUIControl->GetHeight();
301 // if we are in a group, add to the group, else add to our window
303 pGroup->AddControl(pGUIControl);
305 AddControl(pGUIControl);
306 // if the new control is a group, then add it's controls
307 if (pGUIControl->IsGroup())
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());
315 LoadControl(pSubControl, grp, grpRect);
316 pSubControl = pSubControl->NextSiblingElement("control");
322 void CGUIWindow::OnWindowLoaded()
324 DynamicResourceAlloc(true);
327 void CGUIWindow::CenterWindow()
329 m_posX = (m_coordsRes.iWidth - GetWidth()) / 2;
330 m_posY = (m_coordsRes.iHeight - GetHeight()) / 2;
333 void CGUIWindow::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
335 g_graphicsContext.SetRenderingResolution(m_coordsRes, m_needsScaling);
336 g_graphicsContext.AddGUITransform();
337 CGUIControlGroup::DoProcess(currentTime, dirtyregions);
338 g_graphicsContext.RemoveTransform();
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);
347 void CGUIWindow::DoRender()
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
353 if (!m_bAllocated) return;
355 g_graphicsContext.SetRenderingResolution(m_coordsRes, m_needsScaling);
357 g_graphicsContext.AddGUITransform();
358 CGUIControlGroup::DoRender();
359 g_graphicsContext.RemoveTransform();
361 if (CGUIControlProfiler::IsRunning()) CGUIControlProfiler::Instance().EndFrame();
364 void CGUIWindow::AfterRender()
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))
375 void CGUIWindow::Close_Internal(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/)
377 CSingleLock lock(g_graphicsContext);
382 forceClose |= (nextWindowID == WINDOW_FULLSCREEN_VIDEO);
383 if (!forceClose && HasAnimation(ANIM_TYPE_WINDOW_CLOSE))
387 if (enableSound && IsSoundEnabled())
388 g_audioManager.PlayWindowSound(GetID(), SOUND_DEINIT);
390 // Perform the window out effect
391 QueueAnimation(ANIM_TYPE_WINDOW_CLOSE);
398 CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0, nextWindowID);
402 void CGUIWindow::Close(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/, bool bWait /* = true */)
404 if (!g_application.IsCurrentThread())
406 // make sure graphics lock is not held
407 CSingleExit leaveIt(g_graphicsContext);
408 CApplicationMessenger::Get().Close(this, forceClose, bWait, nextWindowID, enableSound);
411 Close_Internal(forceClose, nextWindowID, enableSound);
414 bool CGUIWindow::OnAction(const CAction &action)
416 if (action.IsMouse() || action.IsGesture())
417 return EVENT_RESULT_UNHANDLED != OnMouseAction(action);
419 CGUIControl *focusedControl = GetFocusedControl();
422 if (focusedControl->OnAction(action))
427 // no control has focus?
428 // set focus to the default control then
429 CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), m_defaultControl);
433 // default implementations
434 if (action.GetID() == ACTION_NAV_BACK || action.GetID() == ACTION_PREVIOUS_MENU)
435 return OnBack(action.GetID());
440 CPoint CGUIWindow::GetPosition() const
442 for (unsigned int i = 0; i < m_origins.size(); i++)
444 // no condition implies true
445 if (!m_origins[i].condition || m_origins[i].condition->Get())
447 return CPoint(m_origins[i].x, m_origins[i].y);
450 return CGUIControlGroup::GetPosition();
453 // OnMouseAction - called by OnAction()
454 EVENT_RESULT CGUIWindow::OnMouseAction(const CAction &action)
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);
460 // create the mouse event
461 CMouseEvent event(action.GetID(), action.GetHoldTime(), action.GetAmount(2), action.GetAmount(3));
462 if (m_exclusiveMouseControl)
464 CGUIControl *child = (CGUIControl *)GetControl(m_exclusiveMouseControl);
467 CPoint renderPos = child->GetRenderPosition() - CPoint(child->GetXPosition(), child->GetYPosition());
468 return child->OnMouseEvent(mousePoint - renderPos, event);
472 UnfocusFromPoint(mousePoint);
474 return SendMouseEvent(mousePoint, event);
477 EVENT_RESULT CGUIWindow::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
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;
483 return EVENT_RESULT_UNHANDLED;
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()
496 // Play the window specific init sound
497 if (IsSoundEnabled())
498 g_audioManager.PlayWindowSound(GetID(), SOUND_INIT);
500 // set our rendered state
501 m_hasProcessed = false;
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)
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);
518 if (!m_manualRunActions)
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)
529 if (!m_manualRunActions)
538 bool CGUIWindow::OnMessage(CGUIMessage& message)
540 switch ( message.GetMessage() )
542 case GUI_MSG_WINDOW_LOAD:
549 case GUI_MSG_WINDOW_INIT:
551 CLog::Log(LOGDEBUG, "------ Window Init (%s) ------", GetProperty("xmlfile").c_str());
552 if (m_dynamicResourceAlloc || !m_bAllocated) AllocResources();
558 case GUI_MSG_WINDOW_DEINIT:
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();
568 case GUI_MSG_CLICKED:
570 // a specific control was clicked
571 CLICK_EVENT clickEvent = m_mapClickEvents[ message.GetSenderId() ];
573 // determine if there are any handlers for this event
574 if (clickEvent.HasAHandler())
576 // fire the message to all handlers
577 clickEvent.Fire(message);
582 case GUI_MSG_UNFOCUS_ALL:
584 //unfocus the current focused control in this window
585 CGUIControl *control = GetFocusedControl();
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());
596 case GUI_MSG_SELCHANGED:
598 // a selection within a specific control has changed
599 SELECTED_EVENT selectedEvent = m_mapSelectedEvents[ message.GetSenderId() ];
601 // determine if there are any handlers for this event
602 if (selectedEvent.HasAHandler())
604 // fire the message to all handlers
605 selectedEvent.Fire(message);
609 case GUI_MSG_FOCUSED:
610 { // a control has been focused
611 if (HasID(message.GetSenderId()))
613 m_focusedControl = message.GetControlId();
618 case GUI_MSG_LOSTFOCUS:
620 // nothing to do at the window level when we lose focus
625 if (HasID(message.GetSenderId()))
626 return OnMove(message.GetControlId(), message.GetParam1());
629 case GUI_MSG_SETFOCUS:
631 // CLog::Log(LOGDEBUG,"set focus to control:%i window:%i (%i)\n", message.GetControlId(),message.GetSenderId(), GetID());
632 if ( message.GetControlId() )
634 // first unfocus the current control
635 CGUIControl *control = GetFocusedControl();
638 CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), control->GetID(), message.GetControlId());
639 control->OnMessage(msgLostFocus);
642 // get the control to focus
643 CGUIControl* pFocusedControl = GetFirstFocusableControl(message.GetControlId());
644 if (!pFocusedControl) pFocusedControl = (CGUIControl *)GetControl(message.GetControlId());
648 return pFocusedControl->OnMessage(message);
653 case GUI_MSG_EXCLUSIVE_MOUSE:
655 m_exclusiveMouseControl = message.GetSenderId();
659 case GUI_MSG_GESTURE_NOTIFY:
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;
666 case GUI_MSG_ADD_CONTROL:
668 if (message.GetPointer())
670 CGUIControl *control = (CGUIControl *)message.GetPointer();
671 control->AllocResources();
676 case GUI_MSG_REMOVE_CONTROL:
678 if (message.GetPointer())
680 CGUIControl *control = (CGUIControl *)message.GetPointer();
681 RemoveControl(control);
682 control->FreeResources(true);
687 case GUI_MSG_NOTIFY_ALL:
689 // only process those notifications that come from this window, or those intended for every window
690 if (HasID(message.GetSenderId()) || !message.GetSenderId())
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)
699 CGUIControl *control = *it;
700 CGUIMessage msg(message.GetParam1(), message.GetControlId(), control->GetID(), message.GetParam2());
701 control->OnMessage(msg);
709 return SendControlMessage(message);
712 bool CGUIWindow::NeedXMLReload()
714 return !m_windowLoaded || g_infoManager.ConditionsChangedValues(m_xmlIncludeConditions);
717 void CGUIWindow::AllocResources(bool forceLoad /*= FALSE */)
719 CSingleLock lock(g_graphicsContext);
723 start = CurrentHostCounter();
725 // use forceLoad to determine if xml file needs loading
726 forceLoad |= NeedXMLReload() || (m_loadType == LOAD_EVERY_TIME);
728 // if window is loaded and load is forced we have to free window resources first
729 if (m_windowLoaded && forceLoad)
734 CStdString xmlFile = GetProperty("xmlfile").asString();
737 bool bHasPath = xmlFile.find("\\") != std::string::npos || xmlFile.find("/") != std::string::npos;
738 Load(xmlFile,bHasPath);
743 slend = CurrentHostCounter();
745 // and now allocate resources
746 CGUIControlGroup::AllocResources();
750 end = CurrentHostCounter();
751 freq = CurrentHostFrequency();
753 CLog::Log(LOGDEBUG,"Alloc resources: %.2fms (%.2f ms skin load)", 1000.f * (end - start) / freq, 1000.f * (slend - start) / freq);
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);
763 void CGUIWindow::FreeResources(bool forceUnload /*= FALSE */)
765 m_bAllocated = false;
766 CGUIControlGroup::FreeResources();
767 //g_TextureManager.Dump();
769 if (m_loadType == LOAD_EVERY_TIME || forceUnload) ClearAll();
772 delete m_windowXMLRootElement;
773 m_windowXMLRootElement = NULL;
774 m_xmlIncludeConditions.clear();
778 void CGUIWindow::DynamicResourceAlloc(bool bOnOff)
780 m_dynamicResourceAlloc = bOnOff;
781 CGUIControlGroup::DynamicResourceAlloc(bOnOff);
784 void CGUIWindow::ClearAll()
787 CGUIControlGroup::ClearAll();
788 m_windowLoaded = false;
789 m_dynamicResourceAlloc = true;
790 m_visibleCondition.reset();
793 bool CGUIWindow::Initialize()
795 if (!g_windowManager.Initialized())
796 return false; // can't load if we have no skin yet
799 if(g_application.IsCurrentThread())
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);
808 return m_windowLoaded;
811 void CGUIWindow::SetInitialVisibility()
813 // reset our info manager caches
814 g_infoManager.ResetCache();
815 CGUIControlGroup::SetInitialVisibility();
818 bool CGUIWindow::IsActive() const
820 return g_windowManager.IsWindowActive(GetID());
823 bool CGUIWindow::CheckAnimation(ANIMATION_TYPE animType)
825 // special cases first
826 if (animType == ANIM_TYPE_WINDOW_CLOSE)
828 if (!m_bAllocated || !HasProcessed()) // can't process an animation if we aren't allocated or haven't processed
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();
837 bool CGUIWindow::IsAnimating(ANIMATION_TYPE animType)
839 if (!m_animationsEnabled)
841 if (animType == ANIM_TYPE_WINDOW_CLOSE)
843 return CGUIControlGroup::IsAnimating(animType);
846 bool CGUIWindow::Animate(unsigned int currentTime)
848 if (m_animationsEnabled)
849 return CGUIControlGroup::Animate(currentTime);
857 void CGUIWindow::DisableAnimations()
859 m_animationsEnabled = false;
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)
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);
871 if (group && group->IsGroup())
874 { // just want to know if the group is focused
875 return group->HasFocus();
879 CGUIMessage message(GUI_MSG_ITEM_SELECTED, GetID(), group->GetID());
880 group->OnMessage(message);
881 return (controlID == (int) message.GetParam1());
887 void CGUIWindow::SaveControlStates()
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);
896 void CGUIWindow::RestoreControlStates()
898 for (vector<CControlState>::iterator it = m_controlStates.begin(); it != m_controlStates.end(); ++it)
900 CGUIMessage message(GUI_MSG_ITEM_SELECT, GetID(), (*it).m_id, (*it).m_data);
903 int focusControl = (!m_defaultAlways && m_lastControlID) ? m_lastControlID : m_defaultControl;
904 SET_CONTROL_FOCUS(focusControl, 0);
907 void CGUIWindow::ResetControlStates()
910 m_focusedControl = 0;
911 m_controlStates.clear();
914 bool CGUIWindow::OnBack(int actionID)
916 g_windowManager.PreviousWindow();
920 bool CGUIWindow::OnMove(int fromControl, int moveAction)
922 const CGUIControl *control = GetFirstFocusableControl(fromControl);
923 if (!control) control = GetControl(fromControl);
925 { // no current control??
926 CLog::Log(LOGERROR, "Unable to find control %i in window %u",
927 fromControl, GetID());
930 vector<int> moveHistory;
931 int nextControl = fromControl;
933 { // grab the next control direction
934 moveHistory.push_back(nextControl);
936 if (!control->GetNavigationAction(moveAction, action))
938 action.ExecuteActions(nextControl, GetParentID());
939 nextControl = action.GetNavigation();
940 if (!nextControl) // 0 isn't valid control id
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++)
945 if (nextControl == moveHistory[i])
946 return false; // no control to focus so do nothing
948 control = GetFirstFocusableControl(nextControl);
950 break; // found a focusable control
951 control = GetControl(nextControl); // grab the next control and try again
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);
960 void CGUIWindow::SetDefaults()
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();
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);
976 CRect CGUIWindow::GetScaledBounds() const
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);
983 g_graphicsContext.ScaleFinalCoords(rect.x1, rect.y1, z);
984 g_graphicsContext.ScaleFinalCoords(rect.x2, rect.y2, z);
988 void CGUIWindow::OnEditChanged(int id, CStdString &text)
990 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), id);
992 text = msg.GetLabel();
995 bool CGUIWindow::SendMessage(int message, int id, int param1 /* = 0*/, int param2 /* = 0*/)
997 CGUIMessage msg(message, GetID(), id, param1, param2);
998 return OnMessage(msg);
1001 void CGUIWindow::DumpTextureUse()
1004 CLog::Log(LOGDEBUG, "%s for window %u", __FUNCTION__, GetID());
1005 CGUIControlGroup::DumpTextureUse();
1009 void CGUIWindow::ChangeButtonToEdit(int id, bool singleLabel /* = false*/)
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);
1020 InsertControl(edit, name);
1021 RemoveControl(name);
1022 name->FreeResources();
1029 void CGUIWindow::SetProperty(const CStdString &strKey, const CVariant &value)
1031 CSingleLock lock(*this);
1032 m_mapProperties[strKey] = value;
1035 CVariant CGUIWindow::GetProperty(const CStdString &strKey) const
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);
1042 return iter->second;
1045 void CGUIWindow::ClearProperties()
1047 CSingleLock lock(*this);
1048 m_mapProperties.clear();
1051 void CGUIWindow::SetRunActionsManually()
1053 m_manualRunActions = true;
1056 void CGUIWindow::RunLoadActions()
1058 m_loadActions.ExecuteActions(GetID(), GetParentID());
1061 void CGUIWindow::RunUnloadActions()
1063 m_unloadActions.ExecuteActions(GetID(), GetParentID());
1066 void CGUIWindow::ClearBackground()
1068 m_clearBackground.Update();
1069 color_t color = m_clearBackground;
1071 g_graphicsContext.Clear(color);
1074 void CGUIWindow::SetID(int id)
1076 CGUIControlGroup::SetID(id);
1078 m_idRange.push_back(id);
1081 bool CGUIWindow::HasID(int controlID) const
1083 for (std::vector<int>::const_iterator it = m_idRange.begin(); it != m_idRange.end() ; ++it)
1085 if (controlID == *it)