MythTV  master
mythuibuttontree.cpp
Go to the documentation of this file.
1 
2 // Own header
3 #include "mythuibuttontree.h"
4 
5 // Qt headers
6 #include <QDomDocument>
7 #include <utility>
8 
9 // Mythdb headers
10 #include "mythlogging.h"
11 
12 // Mythui Headers
13 #include "mythmainwindow.h"
14 #include "mythgesture.h"
15 
17  : MythUIType(parent, name)
18 {
19  SetCanTakeFocus(true);
20 
21  connect(this, SIGNAL(TakingFocus()), this, SLOT(Select()));
22  connect(this, SIGNAL(LosingFocus()), this, SLOT(Deselect()));
23 }
24 
30 {
31  if (!m_listTemplate)
32  m_listTemplate = dynamic_cast<MythUIButtonList *>
33  (GetChild("listtemplate"));
34 
35  if (!m_listTemplate)
36  {
37  LOG(VB_GENERAL, LOG_ERR, QString("(%1) MythUIButtonList listtemplate "
38  "is required in mythuibuttonlist: %2")
39  .arg(GetXMLLocation().arg(objectName())));
40  return;
41  }
42 
43  m_listTemplate->SetVisible(false);
44 
45  int width = (m_Area.width() - (m_listSpacing * (m_numLists - 1))) / m_numLists;
46  int height = m_Area.height();
47 
48  int i = 0;
49 
50  while (i < (int)m_numLists)
51  {
52  QString listname = QString("buttontree list %1").arg(i);
53  MythUIButtonList *list = new MythUIButtonList(this, listname);
54  list->CopyFrom(m_listTemplate);
55  list->SetVisible(false);
56  list->SetActive(false);
57  list->SetCanTakeFocus(false);
58  int x = i * (width + m_listSpacing);
59  MythRect listArea = MythRect(x, 0, width, height);
60  list->SetArea(listArea);
61  m_buttonlists.append(list);
62  i++;
63  }
64 
65  m_initialized = true;
66 }
67 
71 void MythUIButtonTree::SetTreeState(bool refreshAll)
72 {
73  if (!m_initialized)
74  Init();
75 
76  if (!m_rootNode)
77  return;
78 
79  if (!m_currentNode)
81 
82  QList<MythGenericTree *> route = m_currentNode->getRoute();
83 
84  // Sanity Checks
85  if (m_depthOffset >= route.size())
86  m_depthOffset = 0;
87 
88  if (((int)m_currentDepth + m_depthOffset) >= route.size())
89  m_currentDepth = 0;
90 
91  MythGenericTree *node = route.at(m_currentDepth + m_depthOffset);
92 
94  refreshAll = true;
95 
97 
98  m_visibleLists = 0;
99  uint listid = 0;
100 
101  while (listid < (uint)m_buttonlists.count())
102  {
103  MythUIButtonList *list = m_buttonlists.at(listid);
104 
105  list->SetVisible(false);
106  list->SetActive(false);
107 
108  MythGenericTree *selectedNode = nullptr;
109 
110  if (node)
111  selectedNode = node->getSelectedChild(true);
112 
113  if (refreshAll || m_activeListID < listid)
114  {
115  if (!UpdateList(list, node))
116  {
117  listid++;
118  continue;
119  }
120  }
121 
122  if (m_active && (listid == m_activeListID))
123  {
124  m_activeList = list;
125  list->SetActive(true);
126  DoSetCurrentNode(selectedNode);
127  emit itemSelected(list->GetItemCurrent());
128  }
129 
130  listid++;
131 
132  list->SetVisible(true);
133  m_visibleLists++;
134 
135  node = selectedNode;
136  }
137 }
138 
148 {
149  disconnect(list, nullptr, nullptr, nullptr);
150 
151  list->Reset();
152 
153  QList<MythGenericTree *> *nodelist = nullptr;
154 
155  if (node)
156  nodelist = node->getAllChildren();
157 
158  if (!nodelist || nodelist->isEmpty())
159  return false;
160 
161  MythGenericTree *selectedNode = node->getSelectedChild(true);
162 
163  MythUIButtonListItem *selectedItem = nullptr;
164  QList<MythGenericTree *>::iterator it;
165 
166  for (it = nodelist->begin(); it != nodelist->end(); ++it)
167  {
168  MythGenericTree *childnode = *it;
169 
170  if (!childnode->IsVisible())
171  continue;
172 
173  MythUIButtonListItem *item = childnode->CreateListButton(list);
174 
175  if (childnode == selectedNode)
176  selectedItem = item;
177  }
178 
179  if (list->IsEmpty())
180  return false;
181 
182  if (selectedItem)
183  list->SetItemCurrent(selectedItem);
184 
185  connect(list, SIGNAL(itemSelected(MythUIButtonListItem *)),
187  connect(list, SIGNAL(itemClicked(MythUIButtonListItem *)),
189  connect(list, SIGNAL(itemVisible(MythUIButtonListItem *)),
191 
192  return true;
193 }
194 
210 {
211  if (!tree || !tree->visibleChildCount())
212  return false;
213 
214  if (m_rootNode)
215  Reset();
216 
217  m_rootNode = tree;
219  // The node we are given may not be the root node of that tree, we need
220  // to keep track of our depth in the tree so that we can navigate
221  // as though the parent nodes do not exist
223  SetTreeState(true);
224  emit rootChanged(m_rootNode);
225 
226  return true;
227 }
228 
233 {
234  m_rootNode = m_currentNode = nullptr;
235  m_visibleLists = 0;
237  m_activeList = nullptr;
238  m_activeListID = 0;
239  m_active = true;
240 
241  SetTreeState(true);
242  emit rootChanged(m_rootNode);
243 
245 }
246 
255 bool MythUIButtonTree::SetNodeById(QList<int> route)
256 {
257  MythGenericTree *node = m_rootNode->findNode(std::move(route));
258 
259  if (node && node->isSelectable())
260  {
261  DoSetCurrentNode(node);
262  SetTreeState();
263  return true;
264  }
265 
266  return false;
267 }
268 
277 bool MythUIButtonTree::SetNodeByString(QStringList route)
278 {
279  if (!m_rootNode)
280  {
281  DoSetCurrentNode(nullptr);
282  return false;
283  }
284 
285  MythGenericTree *foundNode = m_rootNode;
286 
287  bool foundit = false;
288 
289  if (!route.isEmpty())
290  {
291  if (route[0] == m_rootNode->GetText())
292  {
293  if (route.size() > 1)
294  {
295  for (int i = 1; i < route.size(); i ++)
296  {
297  MythGenericTree *node = foundNode->getChildByName(route[i]);
298 
299  if (node)
300  {
301  node->becomeSelectedChild();
302  foundNode = node;
303  foundit = true;
304  }
305  else
306  {
307  node = foundNode->getChildAt(0);
308 
309  if (node)
310  {
311  node->becomeSelectedChild();
312  foundNode = node;
313  }
314 
315  break;
316  }
317  }
318  }
319  else
320  foundit = true;
321  }
322  }
323 
324  DoSetCurrentNode(foundNode);
325 
326  m_currentDepth = qMax(0, (int)(foundNode->currentDepth() - m_depthOffset - m_numLists));
327  m_activeListID = qMin(foundNode->currentDepth() - m_depthOffset - 1, (int)(m_numLists - 1));
328 
329  SetTreeState(true);
330 
331  return foundit;
332 }
333 
343 {
344  if (!node)
345  return false;
346 
347  if (node == m_currentNode)
348  return true;
349 
350  QStringList route = node->getRouteByString();
351 
352  return SetNodeByString(route);
353 }
354 
356 {
357  if (m_activeList)
359 }
360 
362 {
363  if (node)
364  {
365  if (node == m_currentNode)
366  return true;
367 
368  m_currentNode = node;
369  node->becomeSelectedChild();
371  return true;
372  }
373 
374  return false;
375 }
376 
386 {
387  if (!item || !m_rootNode)
388  return;
389 
390  MythGenericTree *node = item->GetData().value<MythGenericTree *>();
391 
392  if (node && node->getParent())
393  {
394  DoSetCurrentNode(node->getParent());
395 
396  if (deleteNode)
397  node->getParent()->deleteNode(node);
398  else
399  node->SetVisible(false);
400  }
401 
402  MythUIButtonList *list = item->parent();
403 
404  list->RemoveItem(item);
405 
406  if (list->IsEmpty())
407  {
408  if (m_currentDepth > 0)
409  m_currentDepth--;
410  else if (m_activeListID > 1)
411  m_activeListID--;
412 
413  SetTreeState(true);
414  }
415 }
416 
425 {
426  RemoveItem(GetItemCurrent(), deleteNode);
427 }
428 
435 {
436  m_active = active;
437 
438  if (m_initialized)
439  SetTreeState();
440 }
441 
443 {
444  SetActive(true);
445 }
446 
448 {
449  SetActive(false);
450 }
451 
452 
460 {
461  bool doUpdate = false;
462 
463  if (right)
464  {
465  if ((m_activeListID + 1 < m_visibleLists) &&
466  (m_activeListID + 1 < (uint)m_buttonlists.count()))
467  m_activeListID++;
468  else if (m_currentNode && m_currentNode->visibleChildCount() > 0)
469  {
470  m_currentDepth++;
471  doUpdate = true;
472  }
473  else
474  return;
475  }
476  else if (!right)
477  {
478  if (m_activeListID > 0)
479  m_activeListID--;
480  else if (m_currentDepth > 0)
481  {
482  m_currentDepth--;
483  doUpdate = true;
484  }
485  else
486  return;
487  }
488 
489  if (doUpdate)
490  SetTreeState();
491  else
492  {
493  if (m_activeList)
495 
496  if (m_activeListID < (uint)m_buttonlists.count())
497  {
499  m_activeList->Select();
500  }
501  }
502 }
503 
510 {
511  if (!item)
512  return;
513 
514  MythUIButtonList *list = item->parent();
515  QString name = list->objectName();
516 
517  // New list is automatically selected so we just need to deselect the old
518  // list
519  if (m_activeList)
521 
522  m_activeListID = name.section(' ', 2, 2).toInt();
523  m_activeList = list;
524 
525 
526  MythGenericTree *node = item->GetData().value<MythGenericTree *> ();
527  DoSetCurrentNode(node);
528  SetTreeState();
529 }
530 
537 {
538  if (!item)
539  return;
540 
541  MythGenericTree *node = item->GetData().value<MythGenericTree *>();
542 
543  if (DoSetCurrentNode(node))
544  emit itemClicked(item);
545 }
546 
553 {
554  if (m_activeList)
555  return m_activeList->GetItemCurrent();
556 
557  return nullptr;
558 }
559 
566 {
567  if (!item)
568  return;
569 
570  emit itemVisible(item);
571 }
572 
576 bool MythUIButtonTree::keyPressEvent(QKeyEvent *event)
577 {
578  QStringList actions;
579  bool handled = false;
580  handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
581 
582  for (int i = 0; i < actions.size() && !handled; i++)
583  {
584  QString action = actions[i];
585  handled = true;
586 
588  {
589  if (action == "SELECT" && m_currentNode->visibleChildCount() > 0)
590  {
591  SwitchList(true);
592  }
593  else if (action == "ESCAPE" && m_currentDepth > 0)
594  {
595  SwitchList(false);
596  }
597  else
598  handled = false;
599  }
600  else
601  {
602  if (action == "RIGHT" && m_currentNode->visibleChildCount() > 0)
603  {
604  SwitchList(true);
605  }
606  else if (action == "LEFT" && !(m_currentDepth == 0 && m_activeListID == 0))
607  {
608  SwitchList(false);
609  }
610  else
611  handled = false;
612  }
613  }
614 
615  if (!handled && m_activeList)
616  handled = m_activeList->keyPressEvent(event);
617 
618  return handled;
619 }
620 
622 {
623  bool handled = false;
624 
625  if (event->gesture() == MythGestureEvent::Click)
626  {
627  // We want the relative position of the click
628  QPoint position = event->GetPosition() -
629  m_Parent->GetArea().topLeft();
630 
631  MythUIType *type = GetChildAt(position, false, false);
632 
633  if (!type)
634  return false;
635 
636  MythUIButtonList *list = dynamic_cast<MythUIButtonList *>(type);
637 
638  if (list)
639  handled = list->gestureEvent(event);
640  }
641 
642  return handled;
643 }
644 
649  const QString &filename, QDomElement &element, bool showWarnings)
650 {
651  if (element.tagName() == "spacing")
652  {
653  m_listSpacing = NormX(getFirstText(element).toInt());
654  }
655  else if (element.tagName() == "numlists")
656  {
657  m_numLists = getFirstText(element).toInt();
658  }
659  else
660  {
661  return MythUIType::ParseElement(filename, element, showWarnings);
662  }
663 
664  return true;
665 }
666 
671 {
672  MythUIButtonTree *bt = new MythUIButtonTree(parent, objectName());
673  bt->CopyFrom(this);
674 }
675 
680 {
681  MythUIButtonTree *bt = dynamic_cast<MythUIButtonTree *>(base);
682 
683  if (!bt)
684  return;
685 
686  m_numLists = bt->m_numLists;
688  m_active = bt->m_active;
689 
690  MythUIType::CopyFrom(base);
691 
692  m_listTemplate = dynamic_cast<MythUIButtonList *>(GetChild("listtemplate"));
693 
694  m_initialized = false;
695 }
void SetTreeState(bool refreshAll=false)
Update the widget following a change.
int NormX(const int width)
MythGenericTree * getSelectedChild(bool onlyVisible=false) const
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
MythGenericTree * m_currentNode
uint visibleChildCount() const
bool TranslateKeyPress(const QString &context, QKeyEvent *e, QStringList &actions, bool allowJumps=true)
Get a list of actions for a keypress in the given context.
void LosingFocus()
QList< MythGenericTree * > * getAllChildren() const
bool IsVisible() const
bool SetNodeByString(QStringList route)
Using a path based on the node string, set the currently selected node.
void SetActive(bool active)
void deleteNode(MythGenericTree *child)
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
bool DoSetCurrentNode(MythGenericTree *node)
void RemoveCurrentItem(bool deleteNode=false)
Remove the currently selected item from the tree.
unsigned int uint
Definition: compat.h:140
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
Gesture gesture(void) const
Get the gesture type.
Definition: mythgesture.h:107
void handleVisible(MythUIButtonListItem *item)
Handle a list item becoming visible.
bool UpdateList(MythUIButtonList *list, MythGenericTree *node)
Update a list with children of the tree node.
void ShowSearchDialog(void)
void SetVisible(bool visible)
QList< MythGenericTree * > getRoute(void)
The base class on which all widgets and screens are based.
Definition: mythuitype.h:63
void RemoveItem(MythUIButtonListItem *item)
QString GetXMLLocation(void) const
Definition: mythuitype.h:155
void itemVisible(MythUIButtonListItem *item)
QString GetText(const QString &name="") const
MythGenericTree * m_rootNode
virtual void SetVisible(bool visible)
void TakingFocus()
A C++ ripoff of the stroke library for MythTV.
int currentDepth(void)
Establish how deep in the current tree this node lies.
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:863
bool gestureEvent(MythGestureEvent *event) override
Mouse click/movement handler, receives mouse gesture events from the QCoreApplication event loop.
static QString getFirstText(QDomElement &element)
MythUIButtonList * m_listTemplate
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:17
void nodeChanged(MythGenericTree *node)
MythGenericTree * findNode(QList< int > route_of_branches)
MythRect m_Area
Definition: mythuitype.h:249
MythUIType * GetChildAt(const QPoint &p, bool recursive=true, bool focusable=true) const
Return the first MythUIType at the given coordinates.
Definition: mythuitype.cpp:223
void CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
QList< MythUIButtonList * > m_buttonlists
A custom event that represents a mouse gesture.
Definition: mythgesture.h:39
const char * name
Definition: ParseText.cpp:328
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
virtual MythUIButtonListItem * CreateListButton(MythUIButtonList *list)
bool keyPressEvent(QKeyEvent *) override
Key event handler.
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
MythMainWindow * GetMythMainWindow(void)
virtual void SetArea(const MythRect &rect)
Definition: mythuitype.cpp:591
bool SetCurrentNode(MythGenericTree *node)
Set the currently selected node.
void RemoveItem(MythUIButtonListItem *item, bool deleteNode=false)
Remove the item from the tree.
#define LOG(_MASK_, _LEVEL_, _STRING_)
Definition: mythlogging.h:41
virtual void Reset(void)
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitype.cpp:69
bool SetNodeById(QList< int > route)
Using a path based on the node IDs, set the currently selected node.
MythUIButtonList * parent() const
void itemSelected(MythUIButtonListItem *item)
MythGenericTree * getChildByName(const QString &a_name) const
MythUIButtonListItem * GetItemCurrent(void) const
Return the currently selected list item.
bool AssignTree(MythGenericTree *tree)
Assign the root node of the tree to be displayed.
MythPoint topLeft(void) const
Definition: mythrect.cpp:244
bool gestureEvent(MythGestureEvent *event) override
Mouse click/movement handler, receives mouse gesture events from the QCoreApplication event loop.
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
bool isSelectable() const
void handleSelect(MythUIButtonListItem *item)
Handle a list item receiving focus.
void handleClick(MythUIButtonListItem *item)
Handle a list item being clicked.
MythGenericTree * getParent(void) const
MythUIType * m_Parent
Definition: mythuitype.h:270
MythUIButtonTree(MythUIType *parent, const QString &name)
void SetItemCurrent(MythUIButtonListItem *item)
MythGenericTree * getChildAt(uint reference) const
void becomeSelectedChild(void)
void rootChanged(MythGenericTree *node)
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:132
void SetCanTakeFocus(bool set=true)
Set whether this widget can take focus.
Definition: mythuitype.cpp:344
void itemClicked(MythUIButtonListItem *item)
QStringList getRouteByString(void)
void SwitchList(bool right)
Move from list, or one level of the tree, to another.
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
A tree widget for displaying and navigating a MythGenericTree()
bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings) override
Parse the xml definition of this widget setting the state of the object accordingly.
void Init(void)
Initialise the tree having loaded the formatting options from the theme.
MythUIButtonList * m_activeList
MythUIButtonListItem * GetItemCurrent() const
void SetActive(bool active)
Set the widget active/inactive.