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/>.
21 #include "GUIControlGroup.h"
22 #include "GUIControlProfiler.h"
26 CGUIControlGroup::CGUIControlGroup()
29 m_defaultAlways = false;
31 m_renderFocusedLast = false;
32 ControlType = GUICONTROL_GROUP;
35 CGUIControlGroup::CGUIControlGroup(int parentID, int controlID, float posX, float posY, float width, float height)
36 : CGUIControl(parentID, controlID, posX, posY, width, height)
39 m_defaultAlways = false;
41 m_renderFocusedLast = false;
42 ControlType = GUICONTROL_GROUP;
45 CGUIControlGroup::CGUIControlGroup(const CGUIControlGroup &from)
48 m_defaultControl = from.m_defaultControl;
49 m_defaultAlways = from.m_defaultAlways;
50 m_renderFocusedLast = from.m_renderFocusedLast;
52 // run through and add our controls
53 for (ciControls it = from.m_children.begin(); it != from.m_children.end(); ++it)
54 AddControl((*it)->Clone());
58 ControlType = GUICONTROL_GROUP;
61 CGUIControlGroup::~CGUIControlGroup(void)
66 void CGUIControlGroup::AllocResources()
68 CGUIControl::AllocResources();
69 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
71 CGUIControl *control = *it;
72 if (!control->IsDynamicallyAllocated())
73 control->AllocResources();
77 void CGUIControlGroup::FreeResources(bool immediately)
79 CGUIControl::FreeResources(immediately);
80 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
82 CGUIControl *control = *it;
83 control->FreeResources(immediately);
87 void CGUIControlGroup::DynamicResourceAlloc(bool bOnOff)
89 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
91 CGUIControl *control = *it;
92 control->DynamicResourceAlloc(bOnOff);
96 void CGUIControlGroup::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
98 CPoint pos(GetPosition());
99 g_graphicsContext.SetOrigin(pos.x, pos.y);
102 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
104 CGUIControl *control = *it;
105 control->UpdateVisibility();
106 unsigned int oldDirty = dirtyregions.size();
107 control->DoProcess(currentTime, dirtyregions);
108 if (control->IsVisible() || (oldDirty != dirtyregions.size())) // visible or dirty (was visible?)
109 rect.Union(control->GetRenderRegion());
112 g_graphicsContext.RestoreOrigin();
113 CGUIControl::Process(currentTime, dirtyregions);
114 m_renderRegion = rect;
117 void CGUIControlGroup::Render()
119 CPoint pos(GetPosition());
120 g_graphicsContext.SetOrigin(pos.x, pos.y);
121 CGUIControl *focusedControl = NULL;
122 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
124 CGUIControl *control = *it;
125 if (m_renderFocusedLast && control->HasFocus())
126 focusedControl = control;
131 focusedControl->DoRender();
132 CGUIControl::Render();
133 g_graphicsContext.RestoreOrigin();
136 bool CGUIControlGroup::OnAction(const CAction &action)
138 ASSERT(false); // unimplemented
142 bool CGUIControlGroup::HasFocus() const
144 for (ciControls it = m_children.begin(); it != m_children.end(); ++it)
146 CGUIControl *control = *it;
147 if (control->HasFocus())
153 bool CGUIControlGroup::OnMessage(CGUIMessage& message)
155 switch (message.GetMessage() )
157 case GUI_MSG_ITEM_SELECT:
159 if (message.GetControlId() == GetID())
161 m_focusedControl = message.GetParam1();
166 case GUI_MSG_ITEM_SELECTED:
168 if (message.GetControlId() == GetID())
170 message.SetParam1(m_focusedControl);
175 case GUI_MSG_FOCUSED:
176 { // a control has been focused
177 m_focusedControl = message.GetControlId();
179 // tell our parent thatwe have focus
181 m_parentControl->OnMessage(message);
184 case GUI_MSG_SETFOCUS:
186 // first try our last focused control...
187 if (!m_defaultAlways && m_focusedControl)
189 CGUIControl *control = GetFirstFocusableControl(m_focusedControl);
192 CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
193 return control->OnMessage(msg);
196 // ok, no previously focused control, try the default control first
197 if (m_defaultControl)
199 CGUIControl *control = GetFirstFocusableControl(m_defaultControl);
202 CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
203 return control->OnMessage(msg);
206 // no success with the default control, so just find one to focus
207 CGUIControl *control = GetFirstFocusableControl(0);
210 CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
211 return control->OnMessage(msg);
217 case GUI_MSG_LOSTFOCUS:
219 // set all subcontrols unfocused
220 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
221 (*it)->SetFocus(false);
222 if (!HasID(message.GetParam1()))
223 { // we don't have the new id, so unfocus
226 m_parentControl->OnMessage(message);
231 case GUI_MSG_PAGE_CHANGE:
232 case GUI_MSG_REFRESH_THUMBS:
233 case GUI_MSG_REFRESH_LIST:
234 case GUI_MSG_WINDOW_RESIZE:
235 { // send to all child controls (make sure the target is the control id)
236 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
238 CGUIMessage msg(message.GetMessage(), message.GetSenderId(), (*it)->GetID(), message.GetParam1());
239 (*it)->OnMessage(msg);
246 //not intented for any specific control, send to all childs and our base handler.
247 if (message.GetControlId() == 0)
249 for (iControls it = m_children.begin();it != m_children.end(); ++it)
251 CGUIControl* control = *it;
252 handled |= control->OnMessage(message);
254 return CGUIControl::OnMessage(message) || handled;
256 // if it's intended for us, then so be it
257 if (message.GetControlId() == GetID())
258 return CGUIControl::OnMessage(message);
260 return SendControlMessage(message);
263 bool CGUIControlGroup::SendControlMessage(CGUIMessage &message)
265 // see if a child matches, and send to the child control if so
266 for (iControls it = m_children.begin();it != m_children.end(); ++it)
268 CGUIControl* control = *it;
269 if (control->HasVisibleID(message.GetControlId()))
271 if (control->OnMessage(message))
275 // Unhandled - send to all matching invisible controls as well
277 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
279 CGUIControl* control = *it;
280 if (control->HasID(message.GetControlId()))
282 if (control->OnMessage(message))
289 bool CGUIControlGroup::CanFocus() const
291 if (!CGUIControl::CanFocus()) return false;
292 // see if we have any children that can be focused
293 for (ciControls it = m_children.begin(); it != m_children.end(); ++it)
295 if ((*it)->CanFocus())
301 void CGUIControlGroup::SetInitialVisibility()
303 CGUIControl::SetInitialVisibility();
304 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
305 (*it)->SetInitialVisibility();
308 void CGUIControlGroup::QueueAnimation(ANIMATION_TYPE animType)
310 CGUIControl::QueueAnimation(animType);
311 // send window level animations to our children as well
312 if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE)
314 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
315 (*it)->QueueAnimation(animType);
319 void CGUIControlGroup::ResetAnimation(ANIMATION_TYPE animType)
321 CGUIControl::ResetAnimation(animType);
322 // send window level animations to our children as well
323 if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE)
325 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
326 (*it)->ResetAnimation(animType);
330 void CGUIControlGroup::ResetAnimations()
331 { // resets all animations, regardless of condition
332 CGUIControl::ResetAnimations();
333 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
334 (*it)->ResetAnimations();
337 bool CGUIControlGroup::IsAnimating(ANIMATION_TYPE animType)
339 if (CGUIControl::IsAnimating(animType))
344 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
346 if ((*it)->IsAnimating(animType))
353 bool CGUIControlGroup::HasAnimation(ANIMATION_TYPE animType)
355 if (CGUIControl::HasAnimation(animType))
360 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
362 if ((*it)->HasAnimation(animType))
369 EVENT_RESULT CGUIControlGroup::SendMouseEvent(const CPoint &point, const CMouseEvent &event)
371 // transform our position into child coordinates
372 CPoint childPoint(point);
373 m_transform.InverseTransformPosition(childPoint.x, childPoint.y);
375 if (CGUIControl::CanFocus())
377 CPoint pos(GetPosition());
378 // run through our controls in reverse order (so that last rendered is checked first)
379 for (rControls i = m_children.rbegin(); i != m_children.rend(); ++i)
381 CGUIControl *child = *i;
382 EVENT_RESULT ret = child->SendMouseEvent(childPoint - pos, event);
384 { // we've handled the action, and/or have focused an item
388 // none of our children want the event, but we may want it.
390 if (HitTest(childPoint) && (ret = OnMouseEvent(childPoint, event)))
393 m_focusedControl = 0;
394 return EVENT_RESULT_UNHANDLED;
397 void CGUIControlGroup::UnfocusFromPoint(const CPoint &point)
399 CPoint controlCoords(point);
400 m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y);
401 controlCoords -= GetPosition();
402 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
404 CGUIControl *child = *it;
405 child->UnfocusFromPoint(controlCoords);
407 CGUIControl::UnfocusFromPoint(point);
410 bool CGUIControlGroup::HasID(int id) const
412 if (CGUIControl::HasID(id)) return true;
413 for (ciControls it = m_children.begin(); it != m_children.end(); ++it)
415 CGUIControl *child = *it;
416 if (child->HasID(id))
422 bool CGUIControlGroup::HasVisibleID(int id) const
424 // call base class first as the group may be the requested control
425 if (CGUIControl::HasVisibleID(id)) return true;
426 // if the group isn't visible, then none of it's children can be
427 if (!IsVisible()) return false;
428 for (ciControls it = m_children.begin(); it != m_children.end(); ++it)
430 CGUIControl *child = *it;
431 if (child->HasVisibleID(id))
437 const CGUIControl* CGUIControlGroup::GetControl(int iControl) const
439 CGUIControl *pPotential = NULL;
440 LookupMap::const_iterator first = m_lookup.find(iControl);
441 if (first != m_lookup.end())
443 LookupMap::const_iterator last = m_lookup.upper_bound(iControl);
444 for (LookupMap::const_iterator i = first; i != last; i++)
446 CGUIControl *control = i->second;
447 if (control->IsVisible())
449 else if (!pPotential)
450 pPotential = control;
456 int CGUIControlGroup::GetFocusedControlID() const
458 if (m_focusedControl) return m_focusedControl;
459 CGUIControl *control = GetFocusedControl();
460 if (control) return control->GetID();
464 CGUIControl *CGUIControlGroup::GetFocusedControl() const
467 if (m_focusedControl)
469 // we may have multiple controls with same id - we pick first that has focus
470 pair<LookupMap::const_iterator, LookupMap::const_iterator> range = m_lookup.equal_range(m_focusedControl);
471 for (LookupMap::const_iterator i = range.first; i != range.second; ++i)
473 if (i->second->HasFocus())
478 // if lookup didn't find focused control, iterate m_children to find it
479 for (ciControls it = m_children.begin(); it != m_children.end(); ++it)
481 const CGUIControl* control = *it;
482 // Avoid calling HasFocus() on control group as it will (possibly) recursively
483 // traverse entire group tree just to check if there is focused control.
484 // We are recursively traversing it here so no point in doing it twice.
485 if (control->IsGroup())
487 CGUIControl* focusedControl = ((CGUIControlGroup *)control)->GetFocusedControl();
489 return (CGUIControl *)focusedControl;
491 else if (control->HasFocus())
492 return (CGUIControl *)control;
497 // in the case of id == 0, we don't match id
498 CGUIControl *CGUIControlGroup::GetFirstFocusableControl(int id)
500 if (!CanFocus()) return NULL;
501 if (id && id == (int) GetID()) return this; // we're focusable and they want us
502 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
504 CGUIControl* pControl = *it;
505 if (pControl->IsGroup())
507 CGUIControlGroup *group = (CGUIControlGroup *)pControl;
508 CGUIControl *control = group->GetFirstFocusableControl(id);
509 if (control) return control;
511 if ((!id || (int) pControl->GetID() == id) && pControl->CanFocus())
517 void CGUIControlGroup::AddControl(CGUIControl *control, int position /* = -1*/)
519 if (!control) return;
520 if (position < 0 || position > (int)m_children.size())
521 position = (int)m_children.size();
522 m_children.insert(m_children.begin() + position, control);
523 control->SetParentControl(this);
524 control->SetPushUpdates(m_pushedUpdates);
529 void CGUIControlGroup::AddLookup(CGUIControl *control)
531 if (control->IsGroup())
532 { // first add all the subitems of this group (if they exist)
533 const LookupMap map = ((CGUIControlGroup *)control)->GetLookup();
534 for (LookupMap::const_iterator i = map.begin(); i != map.end(); i++)
535 m_lookup.insert(m_lookup.upper_bound(i->first), make_pair(i->first, i->second));
537 if (control->GetID())
538 m_lookup.insert(m_lookup.upper_bound(control->GetID()), make_pair(control->GetID(), control));
539 // ensure that our size is what it should be
541 ((CGUIControlGroup *)m_parentControl)->AddLookup(control);
544 void CGUIControlGroup::RemoveLookup(CGUIControl *control)
546 if (control->IsGroup())
547 { // remove the group's lookup
548 const LookupMap &map = ((CGUIControlGroup *)control)->GetLookup();
549 for (LookupMap::const_iterator i = map.begin(); i != map.end(); i++)
550 { // remove this control
551 for (LookupMap::iterator it = m_lookup.begin(); it != m_lookup.end(); it++)
553 if (i->second == it->second)
561 // remove the actual control
562 if (control->GetID())
564 for (LookupMap::iterator it = m_lookup.begin(); it != m_lookup.end(); it++)
566 if (control == it->second)
574 ((CGUIControlGroup *)m_parentControl)->RemoveLookup(control);
577 bool CGUIControlGroup::IsValidControl(const CGUIControl *control) const
579 if (control->GetID())
581 for (LookupMap::const_iterator it = m_lookup.begin(); it != m_lookup.end(); it++)
583 if (control == it->second)
590 bool CGUIControlGroup::InsertControl(CGUIControl *control, const CGUIControl *insertPoint)
593 for (unsigned int i = 0; i < m_children.size(); i++)
595 CGUIControl *child = m_children[i];
596 if (child->IsGroup() && ((CGUIControlGroup *)child)->InsertControl(control, insertPoint))
598 else if (child == insertPoint)
600 AddControl(control, i);
607 void CGUIControlGroup::SaveStates(vector<CControlState> &states)
609 // save our state, and that of our children
610 states.push_back(CControlState(GetID(), m_focusedControl));
611 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
612 (*it)->SaveStates(states);
615 // Note: This routine doesn't delete the control. It just removes it from the control list
616 bool CGUIControlGroup::RemoveControl(const CGUIControl *control)
618 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
620 CGUIControl *child = *it;
621 if (child->IsGroup() && ((CGUIControlGroup *)child)->RemoveControl(control))
623 if (control == child)
625 m_children.erase(it);
634 void CGUIControlGroup::ClearAll()
636 // first remove from the lookup table
639 for (iControls it = m_children.begin(); it != m_children.end(); it++)
640 ((CGUIControlGroup *)m_parentControl)->RemoveLookup(*it);
642 // and delete all our children
643 for (iControls it = m_children.begin(); it != m_children.end(); it++)
645 CGUIControl *control = *it;
648 m_focusedControl = 0;
654 void CGUIControlGroup::GetContainers(vector<CGUIControl *> &containers) const
656 for (ciControls it = m_children.begin();it != m_children.end(); ++it)
658 if ((*it)->IsContainer())
659 containers.push_back(*it);
660 else if ((*it)->IsGroup())
661 ((CGUIControlGroup *)(*it))->GetContainers(containers);
666 void CGUIControlGroup::DumpTextureUse()
668 for (iControls it = m_children.begin(); it != m_children.end(); ++it)
669 (*it)->DumpTextureUse();