PageRenderTime 41ms CodeModel.GetById 1ms app.highlight 35ms RepoModel.GetById 1ms app.codeStats 0ms

/xbmc/guilib/GUIControlGroup.cpp

http://github.com/xbmc/xbmc
C++ | 537 lines | 451 code | 49 blank | 37 comment | 114 complexity | 1709079b3f5c0230776c213081614428 MD5 | raw file
  1/*
  2 *  Copyright (C) 2005-2018 Team Kodi
  3 *  This file is part of Kodi - https://kodi.tv
  4 *
  5 *  SPDX-License-Identifier: GPL-2.0-or-later
  6 *  See LICENSES/README.md for more information.
  7 */
  8
  9#include "GUIControlGroup.h"
 10
 11#include "GUIMessage.h"
 12
 13#include <cassert>
 14#include <utility>
 15
 16CGUIControlGroup::CGUIControlGroup()
 17{
 18  m_defaultControl = 0;
 19  m_defaultAlways = false;
 20  m_focusedControl = 0;
 21  m_renderFocusedLast = false;
 22  ControlType = GUICONTROL_GROUP;
 23}
 24
 25CGUIControlGroup::CGUIControlGroup(int parentID, int controlID, float posX, float posY, float width, float height)
 26: CGUIControlLookup(parentID, controlID, posX, posY, width, height)
 27{
 28  m_defaultControl = 0;
 29  m_defaultAlways = false;
 30  m_focusedControl = 0;
 31  m_renderFocusedLast = false;
 32  ControlType = GUICONTROL_GROUP;
 33}
 34
 35CGUIControlGroup::CGUIControlGroup(const CGUIControlGroup &from)
 36: CGUIControlLookup(from)
 37{
 38  m_defaultControl = from.m_defaultControl;
 39  m_defaultAlways = from.m_defaultAlways;
 40  m_renderFocusedLast = from.m_renderFocusedLast;
 41
 42  // run through and add our controls
 43  for (auto *i : from.m_children)
 44    AddControl(i->Clone());
 45
 46  // defaults
 47  m_focusedControl = 0;
 48  ControlType = GUICONTROL_GROUP;
 49}
 50
 51CGUIControlGroup::~CGUIControlGroup(void)
 52{
 53  ClearAll();
 54}
 55
 56void CGUIControlGroup::AllocResources()
 57{
 58  CGUIControl::AllocResources();
 59  for (auto *control : m_children)
 60  {
 61    if (!control->IsDynamicallyAllocated())
 62      control->AllocResources();
 63  }
 64}
 65
 66void CGUIControlGroup::FreeResources(bool immediately)
 67{
 68  CGUIControl::FreeResources(immediately);
 69  for (auto *control : m_children)
 70  {
 71    control->FreeResources(immediately);
 72  }
 73}
 74
 75void CGUIControlGroup::DynamicResourceAlloc(bool bOnOff)
 76{
 77  for (auto *control : m_children)
 78  {
 79    control->DynamicResourceAlloc(bOnOff);
 80  }
 81}
 82
 83void CGUIControlGroup::Process(unsigned int currentTime, CDirtyRegionList &dirtyregions)
 84{
 85  CPoint pos(GetPosition());
 86  CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos.x, pos.y);
 87
 88  CRect rect;
 89  for (auto *control : m_children)
 90  {
 91    control->UpdateVisibility(nullptr);
 92    unsigned int oldDirty = dirtyregions.size();
 93    control->DoProcess(currentTime, dirtyregions);
 94    if (control->IsVisible() || (oldDirty != dirtyregions.size())) // visible or dirty (was visible?)
 95      rect.Union(control->GetRenderRegion());
 96  }
 97
 98  CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
 99  CGUIControl::Process(currentTime, dirtyregions);
100  m_renderRegion = rect;
101}
102
103void CGUIControlGroup::Render()
104{
105  CPoint pos(GetPosition());
106  CServiceBroker::GetWinSystem()->GetGfxContext().SetOrigin(pos.x, pos.y);
107  CGUIControl *focusedControl = NULL;
108  for (auto *control : m_children)
109  {
110    if (m_renderFocusedLast && control->HasFocus())
111      focusedControl = control;
112    else
113      control->DoRender();
114  }
115  if (focusedControl)
116    focusedControl->DoRender();
117  CGUIControl::Render();
118  CServiceBroker::GetWinSystem()->GetGfxContext().RestoreOrigin();
119}
120
121void CGUIControlGroup::RenderEx()
122{
123  for (auto *control : m_children)
124    control->RenderEx();
125  CGUIControl::RenderEx();
126}
127
128bool CGUIControlGroup::OnAction(const CAction &action)
129{
130  assert(false);  // unimplemented
131  return false;
132}
133
134bool CGUIControlGroup::HasFocus() const
135{
136  for (auto *control : m_children)
137  {
138    if (control->HasFocus())
139      return true;
140  }
141  return false;
142}
143
144bool CGUIControlGroup::OnMessage(CGUIMessage& message)
145{
146  switch (message.GetMessage() )
147  {
148  case GUI_MSG_ITEM_SELECT:
149    {
150      if (message.GetControlId() == GetID())
151      {
152        m_focusedControl = message.GetParam1();
153        return true;
154      }
155      break;
156    }
157  case GUI_MSG_ITEM_SELECTED:
158    {
159      if (message.GetControlId() == GetID())
160      {
161        message.SetParam1(m_focusedControl);
162        return true;
163      }
164      break;
165    }
166  case GUI_MSG_FOCUSED:
167    { // a control has been focused
168      m_focusedControl = message.GetControlId();
169      SetFocus(true);
170      // tell our parent thatwe have focus
171      if (m_parentControl)
172        m_parentControl->OnMessage(message);
173      return true;
174    }
175  case GUI_MSG_SETFOCUS:
176    {
177      // first try our last focused control...
178      if (!m_defaultAlways && m_focusedControl)
179      {
180        CGUIControl *control = GetFirstFocusableControl(m_focusedControl);
181        if (control)
182        {
183          CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
184          return control->OnMessage(msg);
185        }
186      }
187      // ok, no previously focused control, try the default control first
188      if (m_defaultControl)
189      {
190        CGUIControl *control = GetFirstFocusableControl(m_defaultControl);
191        if (control)
192        {
193          CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
194          return control->OnMessage(msg);
195        }
196      }
197      // no success with the default control, so just find one to focus
198      CGUIControl *control = GetFirstFocusableControl(0);
199      if (control)
200      {
201        CGUIMessage msg(GUI_MSG_SETFOCUS, GetParentID(), control->GetID());
202        return control->OnMessage(msg);
203      }
204      // unsuccessful
205      return false;
206      break;
207    }
208  case GUI_MSG_LOSTFOCUS:
209    {
210      // set all subcontrols unfocused
211      for (auto *control : m_children)
212        control->SetFocus(false);
213      if (!GetControl(message.GetParam1()))
214      { // we don't have the new id, so unfocus
215        SetFocus(false);
216        if (m_parentControl)
217          m_parentControl->OnMessage(message);
218      }
219      return true;
220    }
221    break;
222  case GUI_MSG_PAGE_CHANGE:
223  case GUI_MSG_REFRESH_THUMBS:
224  case GUI_MSG_REFRESH_LIST:
225  case GUI_MSG_WINDOW_RESIZE:
226    { // send to all child controls (make sure the target is the control id)
227      for (auto *control : m_children)
228      {
229        CGUIMessage msg(message.GetMessage(), message.GetSenderId(), control->GetID(), message.GetParam1());
230        control->OnMessage(msg);
231      }
232      return true;
233    }
234    break;
235  case GUI_MSG_REFRESH_TIMER:
236    if (!IsVisible() || !IsVisibleFromSkin())
237      return true;
238    break;
239  }
240  bool handled(false);
241  //not intented for any specific control, send to all childs and our base handler.
242  if (message.GetControlId() == 0)
243  {
244    for (auto *control : m_children)
245    {
246      handled |= control->OnMessage(message);
247    }
248    return CGUIControl::OnMessage(message) || handled;
249  }
250  // if it's intended for us, then so be it
251  if (message.GetControlId() == GetID())
252    return CGUIControl::OnMessage(message);
253
254  return SendControlMessage(message);
255}
256
257bool CGUIControlGroup::SendControlMessage(CGUIMessage &message)
258{
259  IDCollector collector(m_idCollector);
260
261  CGUIControl *ctrl(GetControl(message.GetControlId(), collector.m_collector));
262  // see if a child matches, and send to the child control if so
263  if (ctrl && ctrl->OnMessage(message))
264    return true;
265
266  // Unhandled - send to all matching invisible controls as well
267  bool handled(false);
268  for (auto *control : *collector.m_collector)
269    if (control->OnMessage(message))
270      handled = true;
271
272  return handled;
273}
274
275bool CGUIControlGroup::CanFocus() const
276{
277  if (!CGUIControl::CanFocus()) return false;
278  // see if we have any children that can be focused
279  for (auto *control : m_children)
280  {
281    if (control->CanFocus())
282      return true;
283  }
284  return false;
285}
286
287void CGUIControlGroup::SetInitialVisibility()
288{
289  CGUIControl::SetInitialVisibility();
290  for (auto *control : m_children)
291    control->SetInitialVisibility();
292}
293
294void CGUIControlGroup::QueueAnimation(ANIMATION_TYPE animType)
295{
296  CGUIControl::QueueAnimation(animType);
297  // send window level animations to our children as well
298  if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE)
299  {
300    for (auto *control : m_children)
301      control->QueueAnimation(animType);
302  }
303}
304
305void CGUIControlGroup::ResetAnimation(ANIMATION_TYPE animType)
306{
307  CGUIControl::ResetAnimation(animType);
308  // send window level animations to our children as well
309  if (animType == ANIM_TYPE_WINDOW_OPEN || animType == ANIM_TYPE_WINDOW_CLOSE)
310  {
311    for (auto *control : m_children)
312      control->ResetAnimation(animType);
313  }
314}
315
316void CGUIControlGroup::ResetAnimations()
317{ // resets all animations, regardless of condition
318  CGUIControl::ResetAnimations();
319  for (auto *control : m_children)
320    control->ResetAnimations();
321}
322
323bool CGUIControlGroup::IsAnimating(ANIMATION_TYPE animType)
324{
325  if (CGUIControl::IsAnimating(animType))
326    return true;
327
328  if (IsVisible())
329  {
330    for (auto *control : m_children)
331    {
332      if (control->IsAnimating(animType))
333        return true;
334    }
335  }
336  return false;
337}
338
339bool CGUIControlGroup::HasAnimation(ANIMATION_TYPE animType)
340{
341  if (CGUIControl::HasAnimation(animType))
342    return true;
343
344  if (IsVisible())
345  {
346    for (auto *control : m_children)
347    {
348      if (control->HasAnimation(animType))
349        return true;
350    }
351  }
352  return false;
353}
354
355EVENT_RESULT CGUIControlGroup::SendMouseEvent(const CPoint &point, const CMouseEvent &event)
356{
357  // transform our position into child coordinates
358  CPoint childPoint(point);
359  m_transform.InverseTransformPosition(childPoint.x, childPoint.y);
360
361  if (CGUIControl::CanFocus())
362  {
363    CPoint pos(GetPosition());
364    // run through our controls in reverse order (so that last rendered is checked first)
365    for (rControls i = m_children.rbegin(); i != m_children.rend(); ++i)
366    {
367      CGUIControl *child = *i;
368      EVENT_RESULT ret = child->SendMouseEvent(childPoint - pos, event);
369      if (ret)
370      { // we've handled the action, and/or have focused an item
371        return ret;
372      }
373    }
374    // none of our children want the event, but we may want it.
375    EVENT_RESULT ret;
376    if (HitTest(childPoint) && (ret = OnMouseEvent(childPoint, event)))
377      return ret;
378  }
379  m_focusedControl = 0;
380  return EVENT_RESULT_UNHANDLED;
381}
382
383void CGUIControlGroup::UnfocusFromPoint(const CPoint &point)
384{
385  CPoint controlCoords(point);
386  m_transform.InverseTransformPosition(controlCoords.x, controlCoords.y);
387  controlCoords -= GetPosition();
388  for (auto *child : m_children)
389  {
390    child->UnfocusFromPoint(controlCoords);
391  }
392  CGUIControl::UnfocusFromPoint(point);
393}
394
395int CGUIControlGroup::GetFocusedControlID() const
396{
397  if (m_focusedControl) return m_focusedControl;
398  CGUIControl *control = GetFocusedControl();
399  if (control) return control->GetID();
400  return 0;
401}
402
403CGUIControl *CGUIControlGroup::GetFocusedControl() const
404{
405  // try lookup first
406  if (m_focusedControl)
407  {
408    // we may have multiple controls with same id - we pick first that has focus
409    std::pair<LookupMap::const_iterator, LookupMap::const_iterator> range = GetLookupControls(m_focusedControl);
410
411    for (LookupMap::const_iterator i = range.first; i != range.second; ++i)
412    {
413      if (i->second->HasFocus())
414        return i->second;
415    }
416  }
417
418  // if lookup didn't find focused control, iterate m_children to find it
419  for (auto *control : m_children)
420  {
421    // Avoid calling HasFocus() on control group as it will (possibly) recursively
422    // traverse entire group tree just to check if there is focused control.
423    // We are recursively traversing it here so no point in doing it twice.
424    CGUIControlGroup *groupControl(dynamic_cast<CGUIControlGroup*>(control));
425    if (groupControl)
426    {
427      CGUIControl* focusedControl = groupControl->GetFocusedControl();
428      if (focusedControl)
429        return focusedControl;
430    }
431    else if (control->HasFocus())
432      return control;
433  }
434  return NULL;
435}
436
437// in the case of id == 0, we don't match id
438CGUIControl *CGUIControlGroup::GetFirstFocusableControl(int id)
439{
440  if (!CanFocus()) return NULL;
441  if (id && id == GetID()) return this; // we're focusable and they want us
442  for (auto *pControl : m_children)
443  {
444    CGUIControlGroup *group(dynamic_cast<CGUIControlGroup*>(pControl));
445    if (group)
446    {
447      CGUIControl *control = group->GetFirstFocusableControl(id);
448      if (control) return control;
449    }
450    if ((!id || pControl->GetID() == id) && pControl->CanFocus())
451      return pControl;
452  }
453  return NULL;
454}
455
456void CGUIControlGroup::AddControl(CGUIControl *control, int position /* = -1*/)
457{
458  if (!control) return;
459  if (position < 0 || position > (int)m_children.size())
460    position = (int)m_children.size();
461  m_children.insert(m_children.begin() + position, control);
462  control->SetParentControl(this);
463  control->SetControlStats(m_controlStats);
464  control->SetPushUpdates(m_pushedUpdates);
465  AddLookup(control);
466  SetInvalid();
467}
468
469bool CGUIControlGroup::InsertControl(CGUIControl *control, const CGUIControl *insertPoint)
470{
471  // find our position
472  for (unsigned int i = 0; i < m_children.size(); i++)
473  {
474    CGUIControl *child = m_children[i];
475    CGUIControlGroup *group(dynamic_cast<CGUIControlGroup*>(child));
476    if (group && group->InsertControl(control, insertPoint))
477      return true;
478    else if (child == insertPoint)
479    {
480      AddControl(control, i);
481      return true;
482    }
483  }
484  return false;
485}
486
487void CGUIControlGroup::SaveStates(std::vector<CControlState> &states)
488{
489  // save our state, and that of our children
490  states.emplace_back(GetID(), m_focusedControl);
491  for (auto *control : m_children)
492    control->SaveStates(states);
493}
494
495// Note: This routine doesn't delete the control.  It just removes it from the control list
496bool CGUIControlGroup::RemoveControl(const CGUIControl *control)
497{
498  for (iControls it = m_children.begin(); it != m_children.end(); ++it)
499  {
500    CGUIControl *child = *it;
501    CGUIControlGroup *group(dynamic_cast<CGUIControlGroup*>(child));
502    if (group && group->RemoveControl(control))
503      return true;
504    if (control == child)
505    {
506      m_children.erase(it);
507      RemoveLookup(child);
508      SetInvalid();
509      return true;
510    }
511  }
512  return false;
513}
514
515void CGUIControlGroup::ClearAll()
516{
517  // first remove from the lookup table
518  RemoveLookup();
519
520  // and delete all our children
521  for (auto *control : m_children)
522  {
523    delete control;
524  }
525  m_focusedControl = 0;
526  m_children.clear();
527  ClearLookup();
528  SetInvalid();
529}
530
531#ifdef _DEBUG
532void CGUIControlGroup::DumpTextureUse()
533{
534  for (auto *control : m_children)
535    control->DumpTextureUse();
536}
537#endif