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);
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)
285 CGUIControlFactory factory;
287 CRect rect(0, 0, (float)m_coordsRes.iWidth, (float)m_coordsRes.iHeight);
290 rect.x1 = pGroup->GetXPosition();
291 rect.y1 = pGroup->GetYPosition();
292 rect.x2 = rect.x1 + pGroup->GetWidth();
293 rect.y2 = rect.y1 + pGroup->GetHeight();
295 CGUIControl* pGUIControl = factory.Create(GetID(), rect, pControl);
298 float maxX = pGUIControl->GetXPosition() + pGUIControl->GetWidth();
304 float maxY = pGUIControl->GetYPosition() + pGUIControl->GetHeight();
309 // if we are in a group, add to the group, else add to our window
311 pGroup->AddControl(pGUIControl);
313 AddControl(pGUIControl);
314 // if the new control is a group, then add it's controls
315 if (pGUIControl->IsGroup())
317 TiXmlElement *pSubControl = pControl->FirstChildElement("control");
320 LoadControl(pSubControl, (CGUIControlGroup *)pGUIControl);
321 pSubControl = pSubControl->NextSiblingElement("control");
327 void CGUIWindow::OnWindowLoaded()
329 DynamicResourceAlloc(true);
332 void CGUIWindow::CenterWindow()
334 m_posX = (m_coordsRes.iWidth - GetWidth()) / 2;
335 m_posY = (m_coordsRes.iHeight - GetHeight()) / 2;
338 void CGUIWindow::DoProcess(unsigned int currentTime, CDirtyRegionList &dirtyregions)
340 g_graphicsContext.SetRenderingResolution(m_coordsRes, m_needsScaling);
341 g_graphicsContext.AddGUITransform();
342 CGUIControlGroup::DoProcess(currentTime, dirtyregions);
343 g_graphicsContext.RemoveTransform();
345 // check if currently focused control can have it
346 // and fallback to default control if not
347 CGUIControl* focusedControl = GetFocusedControl();
348 if (focusedControl && !focusedControl->CanFocus())
349 SET_CONTROL_FOCUS(m_defaultControl, 0);
352 void CGUIWindow::DoRender()
354 // If we're rendering from a different thread, then we should wait for the main
355 // app thread to finish AllocResources(), as dynamic resources (images in particular)
356 // will try and be allocated from 2 different threads, which causes nasty things
358 if (!m_bAllocated) return;
360 g_graphicsContext.SetRenderingResolution(m_coordsRes, m_needsScaling);
362 g_graphicsContext.AddGUITransform();
363 CGUIControlGroup::DoRender();
364 g_graphicsContext.RemoveTransform();
366 if (CGUIControlProfiler::IsRunning()) CGUIControlProfiler::Instance().EndFrame();
369 void CGUIWindow::AfterRender()
371 // Check to see if we should close at this point
372 // We check after the controls have finished rendering, as we may have to close due to
373 // the controls rendering after the window has finished it's animation
374 // we call the base class instead of this class so that we can find the change
375 if (m_closing && !CGUIControlGroup::IsAnimating(ANIM_TYPE_WINDOW_CLOSE))
380 void CGUIWindow::Close_Internal(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/)
382 CSingleLock lock(g_graphicsContext);
387 forceClose |= (nextWindowID == WINDOW_FULLSCREEN_VIDEO);
388 if (!forceClose && HasAnimation(ANIM_TYPE_WINDOW_CLOSE))
392 if (enableSound && IsSoundEnabled())
393 g_audioManager.PlayWindowSound(GetID(), SOUND_DEINIT);
395 // Perform the window out effect
396 QueueAnimation(ANIM_TYPE_WINDOW_CLOSE);
403 CGUIMessage msg(GUI_MSG_WINDOW_DEINIT, 0, 0, nextWindowID);
407 void CGUIWindow::Close(bool forceClose /*= false*/, int nextWindowID /*= 0*/, bool enableSound /*= true*/, bool bWait /* = true */)
409 if (!g_application.IsCurrentThread())
411 // make sure graphics lock is not held
412 CSingleExit leaveIt(g_graphicsContext);
413 CApplicationMessenger::Get().Close(this, forceClose, bWait, nextWindowID, enableSound);
416 Close_Internal(forceClose, nextWindowID, enableSound);
419 bool CGUIWindow::OnAction(const CAction &action)
421 if (action.IsMouse() || action.IsGesture())
422 return EVENT_RESULT_UNHANDLED != OnMouseAction(action);
424 CGUIControl *focusedControl = GetFocusedControl();
427 if (focusedControl->OnAction(action))
432 // no control has focus?
433 // set focus to the default control then
434 CGUIMessage msg(GUI_MSG_SETFOCUS, GetID(), m_defaultControl);
438 // default implementations
439 if (action.GetID() == ACTION_NAV_BACK || action.GetID() == ACTION_PREVIOUS_MENU)
440 return OnBack(action.GetID());
445 CPoint CGUIWindow::GetPosition() const
447 for (unsigned int i = 0; i < m_origins.size(); i++)
449 // no condition implies true
450 if (!m_origins[i].condition || m_origins[i].condition->Get())
452 return CPoint(m_origins[i].x, m_origins[i].y);
455 return CGUIControlGroup::GetPosition();
458 // OnMouseAction - called by OnAction()
459 EVENT_RESULT CGUIWindow::OnMouseAction(const CAction &action)
461 g_graphicsContext.SetScalingResolution(m_coordsRes, m_needsScaling);
462 CPoint mousePoint(action.GetAmount(0), action.GetAmount(1));
463 g_graphicsContext.InvertFinalCoords(mousePoint.x, mousePoint.y);
465 // create the mouse event
466 CMouseEvent event(action.GetID(), action.GetHoldTime(), action.GetAmount(2), action.GetAmount(3));
467 if (m_exclusiveMouseControl)
469 CGUIControl *child = (CGUIControl *)GetControl(m_exclusiveMouseControl);
472 CPoint renderPos = child->GetRenderPosition() - CPoint(child->GetXPosition(), child->GetYPosition());
473 return child->OnMouseEvent(mousePoint - renderPos, event);
477 UnfocusFromPoint(mousePoint);
479 return SendMouseEvent(mousePoint, event);
482 EVENT_RESULT CGUIWindow::OnMouseEvent(const CPoint &point, const CMouseEvent &event)
484 if (event.m_id == ACTION_MOUSE_RIGHT_CLICK)
485 { // no control found to absorb this click - go to previous menu
486 return OnAction(CAction(ACTION_PREVIOUS_MENU)) ? EVENT_RESULT_HANDLED : EVENT_RESULT_UNHANDLED;
488 return EVENT_RESULT_UNHANDLED;
491 /// \brief Called on window open.
492 /// * Restores the control state(s)
493 /// * Sets initial visibility of controls
494 /// * Queue WindowOpen animation
495 /// * Set overlay state
496 /// Override this function and do any window-specific initialisation such
497 /// as filling control contents and setting control focus before
498 /// calling the base method.
499 void CGUIWindow::OnInitWindow()
501 // Play the window specific init sound
502 if (IsSoundEnabled())
503 g_audioManager.PlayWindowSound(GetID(), SOUND_INIT);
505 // set our rendered state
506 m_hasProcessed = false;
509 ResetAnimations(); // we need to reset our animations as those windows that don't dynamically allocate
510 // need their anims reset. An alternative solution is turning off all non-dynamic
511 // allocation (which in some respects may be nicer, but it kills hdd spindown and the like)
513 // set our initial control visibility before restoring control state and
514 // focusing the default control, and again afterward to make sure that
515 // any controls that depend on the state of the focused control (and or on
516 // control states) are active.
517 SetInitialVisibility();
518 RestoreControlStates();
519 SetInitialVisibility();
520 QueueAnimation(ANIM_TYPE_WINDOW_OPEN);
521 g_windowManager.ShowOverlay(m_overlayState);
523 if (!m_manualRunActions)
529 // Called on window close.
530 // * Saves control state(s)
531 // Override this function and call the base class before doing any dynamic memory freeing
532 void CGUIWindow::OnDeinitWindow(int nextWindowID)
534 if (!m_manualRunActions)
543 bool CGUIWindow::OnMessage(CGUIMessage& message)
545 switch ( message.GetMessage() )
547 case GUI_MSG_WINDOW_LOAD:
554 case GUI_MSG_WINDOW_INIT:
556 CLog::Log(LOGDEBUG, "------ Window Init (%s) ------", GetProperty("xmlfile").c_str());
557 if (m_dynamicResourceAlloc || !m_bAllocated) AllocResources();
563 case GUI_MSG_WINDOW_DEINIT:
565 CLog::Log(LOGDEBUG, "------ Window Deinit (%s) ------", GetProperty("xmlfile").c_str());
566 OnDeinitWindow(message.GetParam1());
567 // now free the window
568 if (m_dynamicResourceAlloc) FreeResources();
573 case GUI_MSG_CLICKED:
575 // a specific control was clicked
576 CLICK_EVENT clickEvent = m_mapClickEvents[ message.GetSenderId() ];
578 // determine if there are any handlers for this event
579 if (clickEvent.HasAHandler())
581 // fire the message to all handlers
582 clickEvent.Fire(message);
587 case GUI_MSG_UNFOCUS_ALL:
589 //unfocus the current focused control in this window
590 CGUIControl *control = GetFocusedControl();
593 //tell focused control that it has lost the focus
594 CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), control->GetID(), control->GetID());
595 control->OnMessage(msgLostFocus);
596 CLog::Log(LOGDEBUG, "Unfocus WindowID: %i, ControlID: %i",GetID(), control->GetID());
601 case GUI_MSG_SELCHANGED:
603 // a selection within a specific control has changed
604 SELECTED_EVENT selectedEvent = m_mapSelectedEvents[ message.GetSenderId() ];
606 // determine if there are any handlers for this event
607 if (selectedEvent.HasAHandler())
609 // fire the message to all handlers
610 selectedEvent.Fire(message);
614 case GUI_MSG_FOCUSED:
615 { // a control has been focused
616 if (HasID(message.GetSenderId()))
618 m_focusedControl = message.GetControlId();
623 case GUI_MSG_LOSTFOCUS:
625 // nothing to do at the window level when we lose focus
630 if (HasID(message.GetSenderId()))
631 return OnMove(message.GetControlId(), message.GetParam1());
634 case GUI_MSG_SETFOCUS:
636 // CLog::Log(LOGDEBUG,"set focus to control:%i window:%i (%i)\n", message.GetControlId(),message.GetSenderId(), GetID());
637 if ( message.GetControlId() )
639 // first unfocus the current control
640 CGUIControl *control = GetFocusedControl();
643 CGUIMessage msgLostFocus(GUI_MSG_LOSTFOCUS, GetID(), control->GetID(), message.GetControlId());
644 control->OnMessage(msgLostFocus);
647 // get the control to focus
648 CGUIControl* pFocusedControl = GetFirstFocusableControl(message.GetControlId());
649 if (!pFocusedControl) pFocusedControl = (CGUIControl *)GetControl(message.GetControlId());
653 return pFocusedControl->OnMessage(message);
658 case GUI_MSG_EXCLUSIVE_MOUSE:
660 m_exclusiveMouseControl = message.GetSenderId();
664 case GUI_MSG_GESTURE_NOTIFY:
666 CAction action(ACTION_GESTURE_NOTIFY, 0, (float)message.GetParam1(), (float)message.GetParam2(), 0, 0);
667 EVENT_RESULT result = OnMouseAction(action);
668 message.SetParam1(result);
669 return result != EVENT_RESULT_UNHANDLED;
671 case GUI_MSG_ADD_CONTROL:
673 if (message.GetPointer())
675 CGUIControl *control = (CGUIControl *)message.GetPointer();
676 control->AllocResources();
681 case GUI_MSG_REMOVE_CONTROL:
683 if (message.GetPointer())
685 CGUIControl *control = (CGUIControl *)message.GetPointer();
686 RemoveControl(control);
687 control->FreeResources(true);
692 case GUI_MSG_NOTIFY_ALL:
694 // only process those notifications that come from this window, or those intended for every window
695 if (HasID(message.GetSenderId()) || !message.GetSenderId())
697 if (message.GetParam1() == GUI_MSG_PAGE_CHANGE ||
698 message.GetParam1() == GUI_MSG_REFRESH_THUMBS ||
699 message.GetParam1() == GUI_MSG_REFRESH_LIST ||
700 message.GetParam1() == GUI_MSG_WINDOW_RESIZE)
701 { // alter the message accordingly, and send to all controls
702 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
704 CGUIControl *control = *it;
705 CGUIMessage msg(message.GetParam1(), message.GetControlId(), control->GetID(), message.GetParam2());
706 control->OnMessage(msg);
714 return SendControlMessage(message);
717 bool CGUIWindow::NeedXMLReload()
719 return !m_windowLoaded || g_infoManager.ConditionsChangedValues(m_xmlIncludeConditions);
722 void CGUIWindow::AllocResources(bool forceLoad /*= FALSE */)
724 CSingleLock lock(g_graphicsContext);
728 start = CurrentHostCounter();
730 // use forceLoad to determine if xml file needs loading
731 forceLoad |= NeedXMLReload() || (m_loadType == LOAD_EVERY_TIME);
733 // if window is loaded and load is forced we have to free window resources first
734 if (m_windowLoaded && forceLoad)
739 CStdString xmlFile = GetProperty("xmlfile").asString();
742 bool bHasPath = xmlFile.find("\\") != std::string::npos || xmlFile.find("/") != std::string::npos;
743 Load(xmlFile,bHasPath);
748 slend = CurrentHostCounter();
750 // and now allocate resources
751 CGUIControlGroup::AllocResources();
755 end = CurrentHostCounter();
756 freq = CurrentHostFrequency();
758 CLog::Log(LOGDEBUG,"Alloc resources: %.2fms (%.2f ms skin load)", 1000.f * (end - start) / freq, 1000.f * (slend - start) / freq);
761 CLog::Log(LOGDEBUG,"Window %s was already loaded", GetProperty("xmlfile").c_str());
762 CLog::Log(LOGDEBUG,"Alloc resources: %.2fm", 1000.f * (end - start) / freq);
768 void CGUIWindow::FreeResources(bool forceUnload /*= FALSE */)
770 m_bAllocated = false;
771 CGUIControlGroup::FreeResources();
772 //g_TextureManager.Dump();
774 if (m_loadType == LOAD_EVERY_TIME || forceUnload) ClearAll();
777 delete m_windowXMLRootElement;
778 m_windowXMLRootElement = NULL;
779 m_xmlIncludeConditions.clear();
783 void CGUIWindow::DynamicResourceAlloc(bool bOnOff)
785 m_dynamicResourceAlloc = bOnOff;
786 CGUIControlGroup::DynamicResourceAlloc(bOnOff);
789 void CGUIWindow::ClearAll()
792 CGUIControlGroup::ClearAll();
793 m_windowLoaded = false;
794 m_dynamicResourceAlloc = true;
795 m_visibleCondition.reset();
798 bool CGUIWindow::Initialize()
800 if (!g_windowManager.Initialized())
801 return false; // can't load if we have no skin yet
804 if(g_application.IsCurrentThread())
808 // if not app thread, send gui msg via app messenger
809 // and wait for results, so windowLoaded flag would be updated
810 CGUIMessage msg(GUI_MSG_WINDOW_LOAD, 0, 0);
811 CApplicationMessenger::Get().SendGUIMessage(msg, GetID(), true);
813 return m_windowLoaded;
816 void CGUIWindow::SetInitialVisibility()
818 // reset our info manager caches
819 g_infoManager.ResetCache();
820 CGUIControlGroup::SetInitialVisibility();
823 bool CGUIWindow::IsActive() const
825 return g_windowManager.IsWindowActive(GetID());
828 bool CGUIWindow::CheckAnimation(ANIMATION_TYPE animType)
830 // special cases first
831 if (animType == ANIM_TYPE_WINDOW_CLOSE)
833 if (!m_bAllocated || !HasProcessed()) // can't process an animation if we aren't allocated or haven't processed
835 // make sure we update our visibility prior to queuing the window close anim
836 for (unsigned int i = 0; i < m_children.size(); i++)
837 m_children[i]->UpdateVisibility();
842 bool CGUIWindow::IsAnimating(ANIMATION_TYPE animType)
844 if (!m_animationsEnabled)
846 if (animType == ANIM_TYPE_WINDOW_CLOSE)
848 return CGUIControlGroup::IsAnimating(animType);
851 bool CGUIWindow::Animate(unsigned int currentTime)
853 if (m_animationsEnabled)
854 return CGUIControlGroup::Animate(currentTime);
862 void CGUIWindow::DisableAnimations()
864 m_animationsEnabled = false;
867 // returns true if the control group with id groupID has controlID as
868 // its focused control
869 bool CGUIWindow::ControlGroupHasFocus(int groupID, int controlID)
871 // 1. Run through and get control with groupID (assume unique)
872 // 2. Get it's selected item.
873 CGUIControl *group = GetFirstFocusableControl(groupID);
874 if (!group) group = (CGUIControl *)GetControl(groupID);
876 if (group && group->IsGroup())
879 { // just want to know if the group is focused
880 return group->HasFocus();
884 CGUIMessage message(GUI_MSG_ITEM_SELECTED, GetID(), group->GetID());
885 group->OnMessage(message);
886 return (controlID == (int) message.GetParam1());
892 void CGUIWindow::SaveControlStates()
894 ResetControlStates();
895 if (!m_defaultAlways)
896 m_lastControlID = GetFocusedControlID();
897 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
898 (*it)->SaveStates(m_controlStates);
901 void CGUIWindow::RestoreControlStates()
903 for (vector<CControlState>::iterator it = m_controlStates.begin(); it != m_controlStates.end(); ++it)
905 CGUIMessage message(GUI_MSG_ITEM_SELECT, GetID(), (*it).m_id, (*it).m_data);
908 int focusControl = (!m_defaultAlways && m_lastControlID) ? m_lastControlID : m_defaultControl;
909 SET_CONTROL_FOCUS(focusControl, 0);
912 void CGUIWindow::ResetControlStates()
915 m_focusedControl = 0;
916 m_controlStates.clear();
919 bool CGUIWindow::OnBack(int actionID)
921 g_windowManager.PreviousWindow();
925 bool CGUIWindow::OnMove(int fromControl, int moveAction)
927 const CGUIControl *control = GetFirstFocusableControl(fromControl);
928 if (!control) control = GetControl(fromControl);
930 { // no current control??
931 CLog::Log(LOGERROR, "Unable to find control %i in window %u",
932 fromControl, GetID());
935 vector<int> moveHistory;
936 int nextControl = fromControl;
938 { // grab the next control direction
939 moveHistory.push_back(nextControl);
941 if (!control->GetNavigationAction(moveAction, action))
943 action.ExecuteActions(nextControl, GetParentID());
944 nextControl = action.GetNavigation();
945 if (!nextControl) // 0 isn't valid control id
947 // check our history - if the nextControl is in it, we can't focus it
948 for (unsigned int i = 0; i < moveHistory.size(); i++)
950 if (nextControl == moveHistory[i])
951 return false; // no control to focus so do nothing
953 control = GetFirstFocusableControl(nextControl);
955 break; // found a focusable control
956 control = GetControl(nextControl); // grab the next control and try again
959 return false; // no control to focus
960 // if we get here we have our new control so focus it (and unfocus the current control)
961 SET_CONTROL_FOCUS(nextControl, 0);
965 void CGUIWindow::SetDefaults()
968 m_defaultAlways = false;
969 m_defaultControl = 0;
970 m_posX = m_posY = m_width = m_height = 0;
971 m_overlayState = OVERLAY_STATE_PARENT_WINDOW; // Use parent or previous window's state
972 m_previousWindow = WINDOW_INVALID;
973 m_animations.clear();
976 m_animationsEnabled = true;
977 m_clearBackground = 0xff000000; // opaque black -> clear
978 m_hitRect.SetRect(0, 0, (float)m_coordsRes.iWidth, (float)m_coordsRes.iHeight);
981 CRect CGUIWindow::GetScaledBounds() const
983 CSingleLock lock(g_graphicsContext);
984 g_graphicsContext.SetScalingResolution(m_coordsRes, m_needsScaling);
985 CPoint pos(GetPosition());
986 CRect rect(pos.x, pos.y, pos.x + m_width, pos.y + m_height);
988 g_graphicsContext.ScaleFinalCoords(rect.x1, rect.y1, z);
989 g_graphicsContext.ScaleFinalCoords(rect.x2, rect.y2, z);
993 void CGUIWindow::OnEditChanged(int id, CStdString &text)
995 CGUIMessage msg(GUI_MSG_ITEM_SELECTED, GetID(), id);
997 text = msg.GetLabel();
1000 bool CGUIWindow::SendMessage(int message, int id, int param1 /* = 0*/, int param2 /* = 0*/)
1002 CGUIMessage msg(message, GetID(), id, param1, param2);
1003 return OnMessage(msg);
1006 void CGUIWindow::DumpTextureUse()
1009 CLog::Log(LOGDEBUG, "%s for window %u", __FUNCTION__, GetID());
1010 CGUIControlGroup::DumpTextureUse();
1014 void CGUIWindow::ChangeButtonToEdit(int id, bool singleLabel /* = false*/)
1016 #ifdef PRE_SKIN_VERSION_9_10_COMPATIBILITY
1017 CGUIControl *name = (CGUIControl *)GetControl(id);
1018 if (name && name->GetControlType() == CGUIControl::GUICONTROL_BUTTON)
1019 { // change it to an edit control
1020 CGUIEditControl *edit = new CGUIEditControl(*(const CGUIButtonControl *)name);
1025 InsertControl(edit, name);
1026 RemoveControl(name);
1027 name->FreeResources();
1034 void CGUIWindow::SetProperty(const CStdString &strKey, const CVariant &value)
1036 CSingleLock lock(*this);
1037 m_mapProperties[strKey] = value;
1040 CVariant CGUIWindow::GetProperty(const CStdString &strKey) const
1042 CSingleLock lock(*this);
1043 std::map<CStdString, CVariant, icompare>::const_iterator iter = m_mapProperties.find(strKey);
1044 if (iter == m_mapProperties.end())
1045 return CVariant(CVariant::VariantTypeNull);
1047 return iter->second;
1050 void CGUIWindow::ClearProperties()
1052 CSingleLock lock(*this);
1053 m_mapProperties.clear();
1056 void CGUIWindow::SetRunActionsManually()
1058 m_manualRunActions = true;
1061 void CGUIWindow::RunLoadActions()
1063 m_loadActions.ExecuteActions(GetID(), GetParentID());
1066 void CGUIWindow::RunUnloadActions()
1068 m_unloadActions.ExecuteActions(GetID(), GetParentID());
1071 void CGUIWindow::ClearBackground()
1073 m_clearBackground.Update();
1074 color_t color = m_clearBackground;
1076 g_graphicsContext.Clear(color);
1079 void CGUIWindow::SetID(int id)
1081 CGUIControlGroup::SetID(id);
1083 m_idRange.push_back(id);
1086 bool CGUIWindow::HasID(int controlID) const
1088 for (std::vector<int>::const_iterator it = m_idRange.begin(); it != m_idRange.end() ; ++it)
1090 if (controlID == *it)