summaryrefslogtreecommitdiffstats
path: root/xbmc/guilib/GUIControlGroup.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'xbmc/guilib/GUIControlGroup.cpp')
-rw-r--r--xbmc/guilib/GUIControlGroup.cpp537
1 files changed, 537 insertions, 0 deletions
diff --git a/xbmc/guilib/GUIControlGroup.cpp b/xbmc/guilib/GUIControlGroup.cpp
new file mode 100644
index 0000000..f737f9f
--- /dev/null
+++ b/xbmc/guilib/GUIControlGroup.cpp
@@ -0,0 +1,537 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GUIControlGroup.h"
+
+#include "GUIMessage.h"
+
+#include <cassert>
+#include <utility>
+
+CGUIControlGroup::CGUIControlGroup()
+{
+ m_defaultControl = 0;
+ m_defaultAlways = false;
+ m_focusedControl = 0;
+ m_renderFocusedLast = false;
+ ControlType = GUICONTROL_GROUP;
+}
+
+CGUIControlGroup::CGUIControlGroup(int parentID, int controlID, float posX, float posY, float width, float height)
+: CGUIControlLookup(parentID, controlID, posX, posY, width, height)
+{
+ m_defaultControl = 0;
+ m_defaultAlways = false;
+ m_focusedControl = 0;
+ m_renderFocusedLast = false;
+ ControlType = GUICONTROL_GROUP;
+}
+
+CGUIControlGroup::CGUIControlGroup(const CGUIControlGroup &from)
+: CGUIControlLookup(from)
+{
+ m_defaultControl = from.m_defaultControl;
+ m_defaultAlways = from.m_defaultAlways;
+ m_renderFocusedLast = from.m_renderFocusedLast;
+
+ // run through and add our controls
+ for (auto *i : from.m_children)
+ AddControl(i->Clone());
+
+ // defaults
+ m_focusedControl = 0;
+ ControlType = GUICONTROL_GROUP;
+}
+
+CGUIControlGroup::~CGUIControlGroup(void)
+{
+ ClearAll();
+}
+
+void CGUIControlGroup::AllocResources()
+{
+ CGUIControl::AllocResources();
+ for (auto *control : m_children)
+ {
+ if (!control->IsDynamicallyAllocated())
+ control->AllocResources();
+ }
+}
+
+void CGUIControlGroup::FreeResources(bool immediately)
+{
+ CGUIControl::FreeResources(immediately);
+ for (auto *control : m_children)
+ {
+ control->FreeResources(immediately);
+ }
+}
+
+void CGUIControlGroup::DynamicResourceAlloc(bool bOnOff)
+{
+ for (auto *control : m_children)
+ {
+ control->DynamicResourceAlloc(bOnOff);
+ }
+}
+
+void CGUIControlGroup::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
+{
+ CPoint pos(GetPosition());
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos.x, pos.y);
+
+ CRect rect;
+ for (auto *control : m_children)
+ {
+ control->UpdateVisibility(nullptr);
+ unsigned int oldDirty = dirtyregions.size();
+ control->DoProcess(currentTime, dirtyregions);
+ if (control->IsVisible() || (oldDirty != dirtyregions.size())) // visible or dirty (was visible?)
+ rect.Union(control->GetRenderRegion());
+ }
+
+ CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
+ CGUIControl::Process(currentTime, dirtyregions);
+ m_renderRegion = rect;
+}
+
+void CGUIControlGroup::Render()
+{
+ CPoint pos(GetPosition());
+ CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos.x, pos.y);
+ CGUIControl *focusedControl = NULL;
+ for (auto *control : m_children)
+ {
+ if (m_renderFocusedLast && control->HasFocus())
+ focusedControl = control;
+ else
+ control->DoRender();
+ }
+ if (focusedControl)
+ focusedControl->DoRender();
+ CGUIControl::Render();
+ CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
+}
+
+void CGUIControlGroup::RenderEx()
+{
+ for (auto *control : m_children)
+ control->RenderEx();
+ CGUIControl::RenderEx();
+}
+
+bool CGUIControlGroup::OnAction(const CAction &action)
+{
+ assert(false); // unimplemented
+ return false;
+}
+
+bool CGUIControlGroup::HasFocus() const
+{
+ for (auto *control : m_children)
+ {
+ if (control->HasFocus())
+ return true;
+ }
+ return false;
+}
+
+bool CGUIControlGroup::OnMessage(CGUIMessage& message)
+{
+ switch (message.GetMessage() )
+ {
+ case GUI_MSG_ITEM_SELECT:
+ {
+ if (message.GetControlId() == GetID())
+ {
+ m_focusedControl = message.GetParam1();
+ return true;
+ }
+ break;
+ }
+ case GUI_MSG_ITEM_SELECTED:
+ {
+ if (message.GetControlId() == GetID())
+ {
+ message.SetParam1(m_focusedControl);
+ return true;
+ }
+ break;
+ }
+ case GUI_MSG_FOCUSED:
+ { // a control has been focused
+ m_focusedControl = message.GetControlId();
+ SetFocus(true);
+ // tell our parent thatwe have focus
+ if (m_parentControl)
+ m_parentControl->OnMessage(message);
+ return true;
+ }
+ case GUI_MSG_SETFOCUS:
+ {
+ // first try our last focused control...
+ if (!m_defaultAlways && m_focusedControl)
+ {
+ CGUIControl *control = GetFirstFocusableControl(m_focusedControl);
+ if (control)
+ {
+ CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
+ return control->OnMessage(msg);
+ }
+ }
+ // ok, no previously focused control, try the default control first
+ if (m_defaultControl)
+ {
+ CGUIControl *control = GetFirstFocusableControl(m_defaultControl);
+ if (control)
+ {
+ CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
+ return control->OnMessage(msg);
+ }
+ }
+ // no success with the default control, so just find one to focus
+ CGUIControl *control = GetFirstFocusableControl(0);
+ if (control)
+ {
+ CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
+ return control->OnMessage(msg);
+ }
+ // unsuccessful
+ return false;
+ break;
+ }
+ case GUI_MSG_LOSTFOCUS:
+ {
+ // set all subcontrols unfocused
+ for (auto *control : m_children)
+ control->SetFocus(false);
+ if (!GetControl(message.GetParam1()))
+ { // we don't have the new id, so unfocus
+ SetFocus(false);
+ if (m_parentControl)
+ m_parentControl->OnMessage(message);
+ }
+ return true;
+ }
+ break;
+ case GUI_MSG_PAGE_CHANGE:
+ case GUI_MSG_REFRESH_THUMBS:
+ case GUI_MSG_REFRESH_LIST:
+ case GUI_MSG_WINDOW_RESIZE:
+ { // send to all child controls (make sure the target is the control id)
+ for (auto *control : m_children)
+ {
+ CGUIMessage msg(message.GetMessage(), message.GetSenderId(), control->GetID(), message.GetParam1());
+ control->OnMessage(msg);
+ }
+ return true;
+ }
+ break;
+ case GUI_MSG_REFRESH_TIMER:
+ if (!IsVisible() || !IsVisibleFromSkin())
+ return true;
+ break;
+ }
+ bool handled(false);
+ //not intended for any specific control, send to all childs and our base handler.
+ if (message.GetControlId() == 0)
+ {
+ for (auto *control : m_children)
+ {
+ handled |= control->OnMessage(message);
+ }
+ return CGUIControl::OnMessage(message) || handled;
+ }
+ // if it's intended for us, then so be it
+ if (message.GetControlId() == GetID())
+ return CGUIControl::OnMessage(message);
+
+ return SendControlMessage(message);
+}
+
+bool CGUIControlGroup::SendControlMessage(CGUIMessage &message)
+{
+ IDCollector collector(m_idCollector);
+
+ CGUIControl *ctrl(GetControl(message.GetControlId(), collector.m_collector));
+ // see if a child matches, and send to the child control if so
+ if (ctrl && ctrl->OnMessage(message))
+ return true;
+
+ // Unhandled - send to all matching invisible controls as well
+ bool handled(false);
+ for (auto *control : *collector.m_collector)
+ if (control->OnMessage(message))
+ handled = true;
+
+ return handled;
+}
+
+bool CGUIControlGroup::CanFocus() const
+{
+ if (!CGUIControl::CanFocus()) return false;
+ // see if we have any children that can be focused
+ for (auto *control : m_children)
+ {
+ if (control->CanFocus())
+ return true;
+ }
+ return false;
+}
+
+void CGUIControlGroup::SetInitialVisibility()
+{
+ CGUIControl::SetInitialVisibility();
+ for (auto *control : m_children)
+ control->SetInitialVisibility();
+}
+
+void CGUIControlGroup::QueueAnimation(ANIMATION_TYPE animType)
+{
+ CGUIControl::QueueAnimation(animType);
+ // send window level animations to our children as well
+ if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE)
+ {
+ for (auto *control : m_children)
+ control->QueueAnimation(animType);
+ }
+}
+
+void CGUIControlGroup::ResetAnimation(ANIMATION_TYPE animType)
+{
+ CGUIControl::ResetAnimation(animType);
+ // send window level animations to our children as well
+ if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE)
+ {
+ for (auto *control : m_children)
+ control->ResetAnimation(animType);
+ }
+}
+
+void CGUIControlGroup::ResetAnimations()
+{ // resets all animations, regardless of condition
+ CGUIControl::ResetAnimations();
+ for (auto *control : m_children)
+ control->ResetAnimations();
+}
+
+bool CGUIControlGroup::IsAnimating(ANIMATION_TYPE animType)
+{
+ if (CGUIControl::IsAnimating(animType))
+ return true;
+
+ if (IsVisible())
+ {
+ for (auto *control : m_children)
+ {
+ if (control->IsAnimating(animType))
+ return true;
+ }
+ }
+ return false;
+}
+
+bool CGUIControlGroup::HasAnimation(ANIMATION_TYPE animType)
+{
+ if (CGUIControl::HasAnimation(animType))
+ return true;
+
+ if (IsVisible())
+ {
+ for (auto *control : m_children)
+ {
+ if (control->HasAnimation(animType))
+ return true;
+ }
+ }
+ return false;
+}
+
+EVENT_RESULT CGUIControlGroup::SendMouseEvent(const CPoint &point, const CMouseEvent &event)
+{
+ // transform our position into child coordinates
+ CPoint childPoint(point);
+ m_transform.InverseTransformPosition(childPoint.x, childPoint.y);
+
+ if (CGUIControl::CanFocus())
+ {
+ CPoint pos(GetPosition());
+ // run through our controls in reverse order (so that last rendered is checked first)
+ for (rControls i = m_children.rbegin(); i != m_children.rend(); ++i)
+ {
+ CGUIControl *child = *i;
+ EVENT_RESULT ret = child->SendMouseEvent(childPoint - pos, event);
+ if (ret)
+ { // we've handled the action, and/or have focused an item
+ return ret;
+ }
+ }
+ // none of our children want the event, but we may want it.
+ EVENT_RESULT ret;
+ if (HitTest(childPoint) && (ret = OnMouseEvent(childPoint, event)))
+ return ret;
+ }
+ m_focusedControl = 0;
+ return EVENT_RESULT_UNHANDLED;
+}
+
+void CGUIControlGroup::UnfocusFromPoint(const CPoint &point)
+{
+ CPoint controlCoords(point);
+ m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y);
+ controlCoords -= GetPosition();
+ for (auto *child : m_children)
+ {
+ child->UnfocusFromPoint(controlCoords);
+ }
+ CGUIControl::UnfocusFromPoint(point);
+}
+
+int CGUIControlGroup::GetFocusedControlID() const
+{
+ if (m_focusedControl) return m_focusedControl;
+ CGUIControl *control = GetFocusedControl();
+ if (control) return control->GetID();
+ return 0;
+}
+
+CGUIControl *CGUIControlGroup::GetFocusedControl() const
+{
+ // try lookup first
+ if (m_focusedControl)
+ {
+ // we may have multiple controls with same id - we pick first that has focus
+ std::pair<LookupMap::const_iterator, LookupMap::const_iterator> range = GetLookupControls(m_focusedControl);
+
+ for (LookupMap::const_iterator i = range.first; i != range.second; ++i)
+ {
+ if (i->second->HasFocus())
+ return i->second;
+ }
+ }
+
+ // if lookup didn't find focused control, iterate m_children to find it
+ for (auto *control : m_children)
+ {
+ // Avoid calling HasFocus() on control group as it will (possibly) recursively
+ // traverse entire group tree just to check if there is focused control.
+ // We are recursively traversing it here so no point in doing it twice.
+ CGUIControlGroup *groupControl(dynamic_cast<CGUIControlGroup*>(control));
+ if (groupControl)
+ {
+ CGUIControl* focusedControl = groupControl->GetFocusedControl();
+ if (focusedControl)
+ return focusedControl;
+ }
+ else if (control->HasFocus())
+ return control;
+ }
+ return NULL;
+}
+
+// in the case of id == 0, we don't match id
+CGUIControl *CGUIControlGroup::GetFirstFocusableControl(int id)
+{
+ if (!CanFocus()) return NULL;
+ if (id && id == GetID()) return this; // we're focusable and they want us
+ for (auto *pControl : m_children)
+ {
+ CGUIControlGroup *group(dynamic_cast<CGUIControlGroup*>(pControl));
+ if (group)
+ {
+ CGUIControl *control = group->GetFirstFocusableControl(id);
+ if (control) return control;
+ }
+ if ((!id || pControl->GetID() == id) && pControl->CanFocus())
+ return pControl;
+ }
+ return NULL;
+}
+
+void CGUIControlGroup::AddControl(CGUIControl *control, int position /* = -1*/)
+{
+ if (!control) return;
+ if (position < 0 || position > (int)m_children.size())
+ position = (int)m_children.size();
+ m_children.insert(m_children.begin() + position, control);
+ control->SetParentControl(this);
+ control->SetControlStats(m_controlStats);
+ control->SetPushUpdates(m_pushedUpdates);
+ AddLookup(control);
+ SetInvalid();
+}
+
+bool CGUIControlGroup::InsertControl(CGUIControl *control, const CGUIControl *insertPoint)
+{
+ // find our position
+ for (unsigned int i = 0; i < m_children.size(); i++)
+ {
+ CGUIControl *child = m_children[i];
+ CGUIControlGroup *group(dynamic_cast<CGUIControlGroup*>(child));
+ if (group && group->InsertControl(control, insertPoint))
+ return true;
+ else if (child == insertPoint)
+ {
+ AddControl(control, i);
+ return true;
+ }
+ }
+ return false;
+}
+
+void CGUIControlGroup::SaveStates(std::vector<CControlState> &states)
+{
+ // save our state, and that of our children
+ states.emplace_back(GetID(), m_focusedControl);
+ for (auto *control : m_children)
+ control->SaveStates(states);
+}
+
+// Note: This routine doesn't delete the control. It just removes it from the control list
+bool CGUIControlGroup::RemoveControl(const CGUIControl *control)
+{
+ for (iControls it = m_children.begin(); it != m_children.end(); ++it)
+ {
+ CGUIControl *child = *it;
+ CGUIControlGroup *group(dynamic_cast<CGUIControlGroup*>(child));
+ if (group && group->RemoveControl(control))
+ return true;
+ if (control == child)
+ {
+ m_children.erase(it);
+ RemoveLookup(child);
+ SetInvalid();
+ return true;
+ }
+ }
+ return false;
+}
+
+void CGUIControlGroup::ClearAll()
+{
+ // first remove from the lookup table
+ RemoveLookup();
+
+ // and delete all our children
+ for (auto *control : m_children)
+ {
+ delete control;
+ }
+ m_focusedControl = 0;
+ m_children.clear();
+ ClearLookup();
+ SetInvalid();
+}
+
+#ifdef _DEBUG
+void CGUIControlGroup::DumpTextureUse()
+{
+ for (auto *control : m_children)
+ control->DumpTextureUse();
+}
+#endif