[cosmetics] update date in GPL header
[vuplus_xbmc] / xbmc / guilib / GUIControlGroup.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.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 "GUIControlGroup.h"
22 #include "GUIControlProfiler.h"
23
24 using namespace std;
25
26 CGUIControlGroup::CGUIControlGroup()
27 {
28   m_defaultControl = 0;
29   m_defaultAlways = false;
30   m_focusedControl = 0;
31   m_renderFocusedLast = false;
32   ControlType = GUICONTROL_GROUP;
33 }
34
35 CGUIControlGroup::CGUIControlGroup(int parentID, int controlID, float posX, float posY, float width, float height)
36 : CGUIControl(parentID, controlID, posX, posY, width, height)
37 {
38   m_defaultControl = 0;
39   m_defaultAlways = false;
40   m_focusedControl = 0;
41   m_renderFocusedLast = false;
42   ControlType = GUICONTROL_GROUP;
43 }
44
45 CGUIControlGroup::CGUIControlGroup(const CGUIControlGroup &from)
46 : CGUIControl(from)
47 {
48   m_defaultControl = from.m_defaultControl;
49   m_defaultAlways = from.m_defaultAlways;
50   m_renderFocusedLast = from.m_renderFocusedLast;
51
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());
55
56   // defaults
57   m_focusedControl = 0;
58   ControlType = GUICONTROL_GROUP;
59 }
60
61 CGUIControlGroup::~CGUIControlGroup(void)
62 {
63   ClearAll();
64 }
65
66 void CGUIControlGroup::AllocResources()
67 {
68   CGUIControl::AllocResources();
69   for (iControls it = m_children.begin(); it != m_children.end(); ++it)
70   {
71     CGUIControl *control = *it;
72     if (!control->IsDynamicallyAllocated())
73       control->AllocResources();
74   }
75 }
76
77 void CGUIControlGroup::FreeResources(bool immediately)
78 {
79   CGUIControl::FreeResources(immediately);
80   for (iControls it = m_children.begin(); it != m_children.end(); ++it)
81   {
82     CGUIControl *control = *it;
83     control->FreeResources(immediately);
84   }
85 }
86
87 void CGUIControlGroup::DynamicResourceAlloc(bool bOnOff)
88 {
89   for (iControls it = m_children.begin(); it != m_children.end(); ++it)
90   {
91     CGUIControl *control = *it;
92     control->DynamicResourceAlloc(bOnOff);
93   }
94 }
95
96 void CGUIControlGroup::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
97 {
98   CPoint pos(GetPosition());
99   g_graphicsContext.SetOrigin(pos.x, pos.y);
100
101   CRect rect;
102   for (iControls it = m_children.begin(); it != m_children.end(); ++it)
103   {
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());
110   }
111
112   g_graphicsContext.RestoreOrigin();
113   CGUIControl::Process(currentTime, dirtyregions);
114   m_renderRegion = rect;
115 }
116
117 void CGUIControlGroup::Render()
118 {
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)
123   {
124     CGUIControl *control = *it;
125     if (m_renderFocusedLast && control->HasFocus())
126       focusedControl = control;
127     else
128       control->DoRender();
129   }
130   if (focusedControl)
131     focusedControl->DoRender();
132   CGUIControl::Render();
133   g_graphicsContext.RestoreOrigin();
134 }
135
136 bool CGUIControlGroup::OnAction(const CAction &action)
137 {
138   ASSERT(false);  // unimplemented
139   return false;
140 }
141
142 bool CGUIControlGroup::HasFocus() const
143 {
144   for (ciControls it = m_children.begin(); it != m_children.end(); ++it)
145   {
146     CGUIControl *control = *it;
147     if (control->HasFocus())
148       return true;
149   }
150   return false;
151 }
152
153 bool CGUIControlGroup::OnMessage(CGUIMessage& message)
154 {
155   switch (message.GetMessage() )
156   {
157   case GUI_MSG_ITEM_SELECT:
158     {
159       if (message.GetControlId() == GetID())
160       {
161         m_focusedControl = message.GetParam1();
162         return true;
163       }
164       break;
165     }
166   case GUI_MSG_ITEM_SELECTED:
167     {
168       if (message.GetControlId() == GetID())
169       {
170         message.SetParam1(m_focusedControl);
171         return true;
172       }
173       break;
174     }
175   case GUI_MSG_FOCUSED:
176     { // a control has been focused
177       m_focusedControl = message.GetControlId();
178       SetFocus(true);
179       // tell our parent thatwe have focus
180       if (m_parentControl)
181         m_parentControl->OnMessage(message);
182       return true;
183     }
184   case GUI_MSG_SETFOCUS:
185     {
186       // first try our last focused control...
187       if (!m_defaultAlways && m_focusedControl)
188       {
189         CGUIControl *control = GetFirstFocusableControl(m_focusedControl);
190         if (control)
191         {
192           CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
193           return control->OnMessage(msg);
194         }
195       }
196       // ok, no previously focused control, try the default control first
197       if (m_defaultControl)
198       {
199         CGUIControl *control = GetFirstFocusableControl(m_defaultControl);
200         if (control)
201         {
202           CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
203           return control->OnMessage(msg);
204         }
205       }
206       // no success with the default control, so just find one to focus
207       CGUIControl *control = GetFirstFocusableControl(0);
208       if (control)
209       {
210         CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
211         return control->OnMessage(msg);
212       }
213       // unsuccessful
214       return false;
215       break;
216     }
217   case GUI_MSG_LOSTFOCUS:
218     {
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
224         SetFocus(false);
225         if (m_parentControl)
226           m_parentControl->OnMessage(message);
227       }
228       return true;
229     }
230     break;
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)
237       {
238         CGUIMessage msg(message.GetMessage(), message.GetSenderId(), (*it)->GetID(), message.GetParam1());
239         (*it)->OnMessage(msg);
240       }
241       return true;
242     }
243     break;
244   }
245   bool handled(false);
246   //not intented for any specific control, send to all childs and our base handler.
247   if (message.GetControlId() == 0)
248   {
249     for (iControls it = m_children.begin();it != m_children.end(); ++it)
250     {
251       CGUIControl* control = *it;
252       handled |= control->OnMessage(message);
253     }
254     return CGUIControl::OnMessage(message) || handled;
255   }
256   // if it's intended for us, then so be it
257   if (message.GetControlId() == GetID())
258     return CGUIControl::OnMessage(message);
259
260   return SendControlMessage(message);
261 }
262
263 bool CGUIControlGroup::SendControlMessage(CGUIMessage &message)
264 {
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)
267   {
268     CGUIControl* control = *it;
269     if (control->HasVisibleID(message.GetControlId()))
270     {
271       if (control->OnMessage(message))
272         return true;
273     }
274   }
275   // Unhandled - send to all matching invisible controls as well
276   bool handled(false);
277   for (iControls it = m_children.begin(); it != m_children.end(); ++it)
278   {
279     CGUIControl* control = *it;
280     if (control->HasID(message.GetControlId()))
281     {
282       if (control->OnMessage(message))
283         handled = true;
284     }
285   }
286   return handled;
287 }
288
289 bool CGUIControlGroup::CanFocus() const
290 {
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)
294   {
295     if ((*it)->CanFocus())
296       return true;
297   }
298   return false;
299 }
300
301 void CGUIControlGroup::SetInitialVisibility()
302 {
303   CGUIControl::SetInitialVisibility();
304   for (iControls it = m_children.begin(); it != m_children.end(); ++it)
305     (*it)->SetInitialVisibility();
306 }
307
308 void CGUIControlGroup::QueueAnimation(ANIMATION_TYPE animType)
309 {
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)
313   {
314     for (iControls it = m_children.begin(); it != m_children.end(); ++it)
315       (*it)->QueueAnimation(animType);
316   }
317 }
318
319 void CGUIControlGroup::ResetAnimation(ANIMATION_TYPE animType)
320 {
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)
324   {
325     for (iControls it = m_children.begin(); it != m_children.end(); ++it)
326       (*it)->ResetAnimation(animType);
327   }
328 }
329
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();
335 }
336
337 bool CGUIControlGroup::IsAnimating(ANIMATION_TYPE animType)
338 {
339   if (CGUIControl::IsAnimating(animType))
340     return true;
341
342   if (IsVisible())
343   {
344     for (iControls it = m_children.begin(); it != m_children.end(); ++it)
345     {
346       if ((*it)->IsAnimating(animType))
347         return true;
348     }
349   }
350   return false;
351 }
352
353 bool CGUIControlGroup::HasAnimation(ANIMATION_TYPE animType)
354 {
355   if (CGUIControl::HasAnimation(animType))
356     return true;
357
358   if (IsVisible())
359   {
360     for (iControls it = m_children.begin(); it != m_children.end(); ++it)
361     {
362       if ((*it)->HasAnimation(animType))
363         return true;
364     }
365   }
366   return false;
367 }
368
369 EVENT_RESULT CGUIControlGroup::SendMouseEvent(const CPoint &point, const CMouseEvent &event)
370 {
371   // transform our position into child coordinates
372   CPoint childPoint(point);
373   m_transform.InverseTransformPosition(childPoint.x, childPoint.y);
374
375   if (CGUIControl::CanFocus())
376   {
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)
380     {
381       CGUIControl *child = *i;
382       EVENT_RESULT ret = child->SendMouseEvent(childPoint - pos, event);
383       if (ret)
384       { // we've handled the action, and/or have focused an item
385         return ret;
386       }
387     }
388     // none of our children want the event, but we may want it.
389     EVENT_RESULT ret;
390     if (HitTest(childPoint) && (ret = OnMouseEvent(childPoint, event)))
391       return ret;
392   }
393   m_focusedControl = 0;
394   return EVENT_RESULT_UNHANDLED;
395 }
396
397 void CGUIControlGroup::UnfocusFromPoint(const CPoint &point)
398 {
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)
403   {
404     CGUIControl *child = *it;
405     child->UnfocusFromPoint(controlCoords);
406   }
407   CGUIControl::UnfocusFromPoint(point);
408 }
409
410 bool CGUIControlGroup::HasID(int id) const
411 {
412   if (CGUIControl::HasID(id)) return true;
413   for (ciControls it = m_children.begin(); it != m_children.end(); ++it)
414   {
415     CGUIControl *child = *it;
416     if (child->HasID(id))
417       return true;
418   }
419   return false;
420 }
421
422 bool CGUIControlGroup::HasVisibleID(int id) const
423 {
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)
429   {
430     CGUIControl *child = *it;
431     if (child->HasVisibleID(id))
432       return true;
433   }
434   return false;
435 }
436
437 const CGUIControl* CGUIControlGroup::GetControl(int iControl) const
438 {
439   CGUIControl *pPotential = NULL;
440   LookupMap::const_iterator first = m_lookup.find(iControl);
441   if (first != m_lookup.end())
442   {
443     LookupMap::const_iterator last = m_lookup.upper_bound(iControl);
444     for (LookupMap::const_iterator i = first; i != last; i++)
445     {
446       CGUIControl *control = i->second;
447       if (control->IsVisible())
448         return control;
449       else if (!pPotential)
450         pPotential = control;
451     }
452   }
453   return pPotential;
454 }
455
456 int CGUIControlGroup::GetFocusedControlID() const
457 {
458   if (m_focusedControl) return m_focusedControl;
459   CGUIControl *control = GetFocusedControl();
460   if (control) return control->GetID();
461   return 0;
462 }
463
464 CGUIControl *CGUIControlGroup::GetFocusedControl() const
465 {
466   // try lookup first
467   if (m_focusedControl)
468   {
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)
472     {
473       if (i->second->HasFocus())
474         return i->second;
475     }
476   }
477
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)
480   {
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())
486     {
487       CGUIControl* focusedControl = ((CGUIControlGroup *)control)->GetFocusedControl();
488       if (focusedControl)
489         return (CGUIControl *)focusedControl;
490     }
491     else if (control->HasFocus())
492       return (CGUIControl *)control;
493   }
494   return NULL;
495 }
496
497 // in the case of id == 0, we don't match id
498 CGUIControl *CGUIControlGroup::GetFirstFocusableControl(int id)
499 {
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)
503   {
504     CGUIControl* pControl = *it;
505     if (pControl->IsGroup())
506     {
507       CGUIControlGroup *group = (CGUIControlGroup *)pControl;
508       CGUIControl *control = group->GetFirstFocusableControl(id);
509       if (control) return control;
510     }
511     if ((!id || (int) pControl->GetID() == id) && pControl->CanFocus())
512       return pControl;
513   }
514   return NULL;
515 }
516
517 void CGUIControlGroup::AddControl(CGUIControl *control, int position /* = -1*/)
518 {
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);
525   AddLookup(control);
526   SetInvalid();
527 }
528
529 void CGUIControlGroup::AddLookup(CGUIControl *control)
530 {
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));
536   }
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
540   if (m_parentControl)
541     ((CGUIControlGroup *)m_parentControl)->AddLookup(control);
542 }
543
544 void CGUIControlGroup::RemoveLookup(CGUIControl *control)
545 {
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++)
552       {
553         if (i->second == it->second)
554         {
555           m_lookup.erase(it);
556           break;
557         }
558       }
559     }
560   }
561   // remove the actual control
562   if (control->GetID())
563   {
564     for (LookupMap::iterator it = m_lookup.begin(); it != m_lookup.end(); it++)
565     {
566       if (control == it->second)
567       {
568         m_lookup.erase(it);
569         break;
570       }
571     }
572   }
573   if (m_parentControl)
574     ((CGUIControlGroup *)m_parentControl)->RemoveLookup(control);
575 }
576
577 bool CGUIControlGroup::IsValidControl(const CGUIControl *control) const
578 {
579   if (control->GetID())
580   {
581     for (LookupMap::const_iterator it = m_lookup.begin(); it != m_lookup.end(); it++)
582     {
583       if (control == it->second)
584         return true;
585     }
586   }
587   return false;
588 }
589
590 bool CGUIControlGroup::InsertControl(CGUIControl *control, const CGUIControl *insertPoint)
591 {
592   // find our position
593   for (unsigned int i = 0; i < m_children.size(); i++)
594   {
595     CGUIControl *child = m_children[i];
596     if (child->IsGroup() && ((CGUIControlGroup *)child)->InsertControl(control, insertPoint))
597       return true;
598     else if (child == insertPoint)
599     {
600       AddControl(control, i);
601       return true;
602     }
603   }
604   return false;
605 }
606
607 void CGUIControlGroup::SaveStates(vector<CControlState> &states)
608 {
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);
613 }
614
615 // Note: This routine doesn't delete the control.  It just removes it from the control list
616 bool CGUIControlGroup::RemoveControl(const CGUIControl *control)
617 {
618   for (iControls it = m_children.begin(); it != m_children.end(); ++it)
619   {
620     CGUIControl *child = *it;
621     if (child->IsGroup() && ((CGUIControlGroup *)child)->RemoveControl(control))
622       return true;
623     if (control == child)
624     {
625       m_children.erase(it);
626       RemoveLookup(child);
627       SetInvalid();
628       return true;
629     }
630   }
631   return false;
632 }
633
634 void CGUIControlGroup::ClearAll()
635 {
636   // first remove from the lookup table
637   if (m_parentControl)
638   {
639     for (iControls it = m_children.begin(); it != m_children.end(); it++)
640       ((CGUIControlGroup *)m_parentControl)->RemoveLookup(*it);
641   }
642   // and delete all our children
643   for (iControls it = m_children.begin(); it != m_children.end(); it++)
644   {
645     CGUIControl *control = *it;
646     delete control;
647   }
648   m_focusedControl = 0;
649   m_children.clear();
650   m_lookup.clear();
651   SetInvalid();
652 }
653
654 void CGUIControlGroup::GetContainers(vector<CGUIControl *> &containers) const
655 {
656   for (ciControls it = m_children.begin();it != m_children.end(); ++it)
657   {
658     if ((*it)->IsContainer())
659       containers.push_back(*it);
660     else if ((*it)->IsGroup())
661       ((CGUIControlGroup *)(*it))->GetContainers(containers);
662   }
663 }
664
665 #ifdef _DEBUG
666 void CGUIControlGroup::DumpTextureUse()
667 {
668   for (iControls it = m_children.begin(); it != m_children.end(); ++it)
669     (*it)->DumpTextureUse();
670 }
671 #endif