MythTV master
mythuibuttontree.cpp
Go to the documentation of this file.
1// Own header
2#include "mythuibuttontree.h"
3
4// C++
5#include <algorithm>
6#include <utility>
7
8// Qt headers
9#include <QDomDocument>
10
11// Mythbase headers
13
14// Mythui Headers
15#include "mythmainwindow.h"
16#include "mythgesture.h"
17
19 : MythUIType(parent, name)
20{
21 SetCanTakeFocus(true);
22
25}
26
32{
33 if (!m_listTemplate)
34 m_listTemplate = dynamic_cast<MythUIButtonList *>
35 (GetChild("listtemplate"));
36
37 if (!m_listTemplate)
38 {
39 LOG(VB_GENERAL, LOG_ERR, QString("(%1) MythUIButtonList listtemplate "
40 "is required in mythuibuttonlist: %2")
41 .arg(GetXMLLocation().arg(objectName())));
42 return;
43 }
44
46
47 int width = (m_area.width() - (m_listSpacing * (m_numLists - 1))) / m_numLists;
48 int height = m_area.height();
49
50 int i = 0;
51
52 while (i < (int)m_numLists)
53 {
54 QString listname = QString("buttontree list %1").arg(i);
55 auto *list = new MythUIButtonList(this, listname);
56 list->CopyFrom(m_listTemplate);
57 list->SetVisible(false);
58 list->SetActive(false);
59 list->SetCanTakeFocus(false);
60 int x = i * (width + m_listSpacing);
61 MythRect listArea = MythRect(x, 0, width, height);
62 list->SetArea(listArea);
63 m_buttonlists.append(list);
64 i++;
65 }
66
67 m_initialized = true;
68}
69
73void MythUIButtonTree::SetTreeState(bool refreshAll)
74{
75 if (!m_initialized)
76 Init();
77
78 if (!m_rootNode)
79 return;
80
81 if (!m_currentNode)
83 if (!m_currentNode)
84 return;
85
86 QList<MythGenericTree *> route = m_currentNode->getRoute();
87
88 // Sanity Checks
89 if (m_depthOffset >= route.size())
90 m_depthOffset = 0;
91
92 if (((int)m_currentDepth + m_depthOffset) >= route.size())
94
96
98 refreshAll = true;
99
101
102 m_visibleLists = 0;
103 uint listid = 0;
104
105 while (listid < (uint)m_buttonlists.count())
106 {
107 MythUIButtonList *list = m_buttonlists.at(listid);
108
109 list->SetVisible(false);
110 list->SetActive(false);
111
112 MythGenericTree *selectedNode = nullptr;
113
114 if (node)
115 selectedNode = node->getSelectedChild(true);
116
117 if (refreshAll || m_activeListID < listid)
118 {
119 if (!UpdateList(list, node))
120 {
121 listid++;
122 continue;
123 }
124 }
125
126 if (m_active && (listid == m_activeListID))
127 {
128 m_activeList = list;
129 list->SetActive(true);
130 DoSetCurrentNode(selectedNode);
131 emit itemSelected(list->GetItemCurrent());
132 }
133
134 listid++;
135
136 list->SetVisible(true);
138
139 node = selectedNode;
140 }
141}
142
152{
153 disconnect(list, nullptr, nullptr, nullptr);
154
155 list->Reset();
156
157 QList<MythGenericTree *> *nodelist = nullptr;
158
159 if (node)
160 nodelist = node->getAllChildren();
161
162 if (!nodelist || nodelist->isEmpty())
163 return false;
164
165 MythGenericTree *selectedNode = node->getSelectedChild(true);
166
167 MythUIButtonListItem *selectedItem = nullptr;
168 QList<MythGenericTree *>::iterator it;
169
170 for (it = nodelist->begin(); it != nodelist->end(); ++it)
171 {
172 MythGenericTree *childnode = *it;
173
174 if (!childnode->IsVisible())
175 continue;
176
177 MythUIButtonListItem *item = childnode->CreateListButton(list);
178
179 if (childnode == selectedNode)
180 selectedItem = item;
181 }
182
183 if (list->IsEmpty())
184 return false;
185
186 if (selectedItem)
187 list->SetItemCurrent(selectedItem);
188
189 connect(list, &MythUIButtonList::itemSelected,
191 connect(list, &MythUIButtonList::itemClicked,
193 connect(list, &MythUIButtonList::itemVisible,
195
196 return true;
197}
198
214{
215 if (!tree || !tree->visibleChildCount())
216 return false;
217
218 if (m_rootNode)
219 Reset();
220
221 m_rootNode = tree;
223 // The node we are given may not be the root node of that tree, we need
224 // to keep track of our depth in the tree so that we can navigate
225 // as though the parent nodes do not exist
227 SetTreeState(true);
229
230 return true;
231}
232
237{
238 m_rootNode = m_currentNode = nullptr;
239 m_visibleLists = 0;
241 m_activeList = nullptr;
242 m_activeListID = 0;
243 m_active = true;
244
245 SetTreeState(true);
247
249}
250
259bool MythUIButtonTree::SetNodeById(QList<int> route)
260{
261 MythGenericTree *node = m_rootNode->findNode(std::move(route));
262
263 if (node && node->isSelectable())
264 {
265 DoSetCurrentNode(node);
266 SetTreeState();
267 return true;
268 }
269
270 return false;
271}
272
282{
283 if (!m_rootNode)
284 {
285 DoSetCurrentNode(nullptr);
286 return false;
287 }
288
289 MythGenericTree *foundNode = m_rootNode;
290
291 bool foundit = false;
292
293 if (!route.isEmpty())
294 {
295 if (route[0] == m_rootNode->GetText())
296 {
297 if (route.size() > 1)
298 {
299 for (int i = 1; i < route.size(); i ++)
300 {
301 MythGenericTree *node = foundNode->getChildByName(route[i]);
302
303 if (node)
304 {
305 node->becomeSelectedChild();
306 foundNode = node;
307 foundit = true;
308 }
309 else
310 {
311 node = foundNode->getChildAt(0);
312
313 if (node)
314 {
315 node->becomeSelectedChild();
316 foundNode = node;
317 }
318
319 break;
320 }
321 }
322 }
323 else
324 {
325 foundit = true;
326 }
327 }
328 }
329
330 DoSetCurrentNode(foundNode);
331
332 m_currentDepth = std::max(0, (int)(foundNode->currentDepth() - m_depthOffset - m_numLists));
333 m_activeListID = std::min(foundNode->currentDepth() - m_depthOffset - 1, (int)(m_numLists - 1));
334
335 SetTreeState(true);
336
337 return foundit;
338}
339
349{
350 if (!node)
351 return false;
352
353 if (node == m_currentNode)
354 return true;
355
356 QStringList route = node->getRouteByString();
357
358 return SetNodeByString(route);
359}
360
362{
363 if (m_activeList)
365}
366
368{
369 if (node)
370 {
371 if (node == m_currentNode)
372 return true;
373
374 m_currentNode = node;
375 node->becomeSelectedChild();
377 return true;
378 }
379
380 return false;
381}
382
392{
393 if (!item || !m_rootNode)
394 return;
395
396 auto *node = item->GetData().value<MythGenericTree *>();
397
398 if (node && node->getParent())
399 {
400 DoSetCurrentNode(node->getParent());
401
402 if (deleteNode)
403 node->getParent()->deleteNode(node);
404 else
405 node->SetVisible(false);
406 }
407
408 MythUIButtonList *list = item->parent();
409
410 list->RemoveItem(item);
411
412 if (list->IsEmpty())
413 {
414 if (m_currentDepth > 0)
416 else if (m_activeListID > 1)
418
419 SetTreeState(true);
420 }
421}
422
431{
432 RemoveItem(GetItemCurrent(), deleteNode);
433}
434
441{
442 m_active = active;
443
444 if (m_initialized)
445 SetTreeState();
446}
447
449{
450 SetActive(true);
451}
452
454{
455 SetActive(false);
456}
457
458
466{
467 bool doUpdate = false;
468
469 if (right)
470 {
471 if ((m_activeListID + 1 < m_visibleLists) &&
472 (m_activeListID + 1 < (uint)m_buttonlists.count()))
475 {
477 doUpdate = true;
478 }
479 else
480 {
481 return;
482 }
483 }
484 else if (!right)
485 {
486 if (m_activeListID > 0)
488 else if (m_currentDepth > 0)
489 {
491 doUpdate = true;
492 }
493 else
494 {
495 return;
496 }
497 }
498
499 if (doUpdate)
500 SetTreeState();
501 else
502 {
503 if (m_activeList)
505
506 if (m_activeListID < (uint)m_buttonlists.count())
507 {
510 }
511 }
512}
513
520{
521 if (!item)
522 return;
523
524 MythUIButtonList *list = item->parent();
525 QString name = list->objectName();
526
527 // New list is automatically selected so we just need to deselect the old
528 // list
529 if (m_activeList)
531
532 m_activeListID = name.section(' ', 2, 2).toInt();
533 m_activeList = list;
534
535 auto *node = item->GetData().value<MythGenericTree *> ();
536 DoSetCurrentNode(node);
537 SetTreeState();
538}
539
546{
547 if (!item)
548 return;
549
550 auto *node = item->GetData().value<MythGenericTree *>();
551
552 if (DoSetCurrentNode(node))
553 emit itemClicked(item);
554}
555
562{
563 if (m_activeList)
565
566 return nullptr;
567}
568
575{
576 if (!item)
577 return;
578
579 emit itemVisible(item);
580}
581
586{
587 QStringList actions;
588 bool handled = false;
589 handled = GetMythMainWindow()->TranslateKeyPress("Global", event, actions);
590
591 for (int i = 0; i < actions.size() && !handled; i++)
592 {
593 const QString& action = actions[i];
594 handled = true;
595
597 {
598 if (action == "SELECT" && m_currentNode->visibleChildCount() > 0)
599 {
600 SwitchList(true);
601 }
602 else if (action == "ESCAPE" && m_currentDepth > 0)
603 {
604 SwitchList(false);
605 }
606 else
607 {
608 handled = false;
609 }
610 }
611 else
612 {
613 if (action == "RIGHT" && m_currentNode->visibleChildCount() > 0)
614 {
615 SwitchList(true);
616 }
617 else if (action == "LEFT" && (m_currentDepth != 0 || m_activeListID != 0))
618 {
619 SwitchList(false);
620 }
621 else
622 {
623 handled = false;
624 }
625 }
626 }
627
628 if (!handled && m_activeList)
629 handled = m_activeList->keyPressEvent(event);
630
631 return handled;
632}
633
635{
636 bool handled = false;
637
638 if (event->GetGesture() == MythGestureEvent::Click)
639 {
640 // We want the relative position of the click
641 QPoint position = event->GetPosition() -
643
644 MythUIType *type = GetChildAt(position, false, false);
645
646 if (!type)
647 return false;
648
649 auto *list = dynamic_cast<MythUIButtonList *>(type);
650
651 if (list)
652 handled = list->gestureEvent(event);
653 }
654
655 return handled;
656}
657
662 const QString &filename, QDomElement &element, bool showWarnings)
663{
664 if (element.tagName() == "spacing")
665 {
666 m_listSpacing = NormX(getFirstText(element).toInt());
667 }
668 else if (element.tagName() == "numlists")
669 {
670 m_numLists = getFirstText(element).toInt();
671 }
672 else
673 {
674 return MythUIType::ParseElement(filename, element, showWarnings);
675 }
676
677 return true;
678}
679
684{
685 auto *bt = new MythUIButtonTree(parent, objectName());
686 bt->CopyFrom(this);
687}
688
693{
694 auto *bt = dynamic_cast<MythUIButtonTree *>(base);
695
696 if (!bt)
697 return;
698
699 m_numLists = bt->m_numLists;
700 m_listSpacing = bt->m_listSpacing;
701 m_active = bt->m_active;
702
704
705 m_listTemplate = dynamic_cast<MythUIButtonList *>(GetChild("listtemplate"));
706
707 m_initialized = false;
708}
bool isSelectable() const
QString GetText(const QString &name="") const
int currentDepth(void)
Establish how deep in the current tree this node lies.
QStringList getRouteByString(void)
virtual MythUIButtonListItem * CreateListButton(MythUIButtonList *list)
MythGenericTree * getChildAt(uint reference) const
MythGenericTree * findNode(QList< int > route_of_branches)
bool IsVisible() const
MythGenericTree * getSelectedChild(bool onlyVisible=false) const
uint visibleChildCount() const
void becomeSelectedChild(void)
MythGenericTree * getChildByName(const QString &a_name) const
QList< MythGenericTree * > getRoute(void)
QList< MythGenericTree * > * getAllChildren() const
A custom event that represents a mouse gesture.
Definition: mythgesture.h:40
Gesture GetGesture() const
Definition: mythgesture.h:85
bool TranslateKeyPress(const QString &Context, QKeyEvent *Event, QStringList &Actions, bool AllowJumps=true)
Get a list of actions for a keypress in the given context.
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:18
MythPoint topLeft(void) const
Definition: mythrect.cpp:288
MythUIButtonList * parent() const
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
MythUIButtonListItem * GetItemCurrent() const
void itemVisible(MythUIButtonListItem *item)
void SetItemCurrent(MythUIButtonListItem *item)
void RemoveItem(MythUIButtonListItem *item)
void Reset() override
Reset the widget to it's original state, should not reset changes made by the theme.
void SetActive(bool active)
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
void itemClicked(MythUIButtonListItem *item)
void itemSelected(MythUIButtonListItem *item)
bool gestureEvent(MythGestureEvent *event) override
Mouse click/movement handler, receives mouse gesture events from the QCoreApplication event loop.
A tree widget for displaying and navigating a MythGenericTree()
void Init(void)
Initialise the tree having loaded the formatting options from the theme.
void SwitchList(bool right)
Move from list, or one level of the tree, to another.
void handleSelect(MythUIButtonListItem *item)
Handle a list item receiving focus.
MythUIButtonTree(MythUIType *parent, const QString &name)
void Reset(void) override
Reset the widget to it's original state, should not reset changes made by the theme.
void RemoveCurrentItem(bool deleteNode=false)
Remove the currently selected item from the tree.
void rootChanged(MythGenericTree *node)
bool SetCurrentNode(MythGenericTree *node)
Set the currently selected node.
void handleClick(MythUIButtonListItem *item)
Handle a list item being clicked.
void itemVisible(MythUIButtonListItem *item)
void nodeChanged(MythGenericTree *node)
bool keyPressEvent(QKeyEvent *event) override
Key event handler.
void ShowSearchDialog(void)
bool SetNodeByString(QStringList route)
Using a path based on the node string, set the currently selected node.
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
bool UpdateList(MythUIButtonList *list, MythGenericTree *node) const
Update a list with children of the tree node.
void itemSelected(MythUIButtonListItem *item)
MythUIButtonList * m_activeList
void itemClicked(MythUIButtonListItem *item)
bool SetNodeById(QList< int > route)
Using a path based on the node IDs, set the currently selected node.
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 CreateCopy(MythUIType *parent) override
Copy the state of this widget to the one given, it must be of the same type.
void SetTreeState(bool refreshAll=false)
Update the widget following a change.
bool gestureEvent(MythGestureEvent *event) override
Mouse click/movement handler, receives mouse gesture events from the QCoreApplication event loop.
void RemoveItem(MythUIButtonListItem *item, bool deleteNode=false)
Remove the item from the tree.
MythGenericTree * m_rootNode
void SetActive(bool active)
Set the widget active/inactive.
bool AssignTree(MythGenericTree *tree)
Assign the root node of the tree to be displayed.
QList< MythUIButtonList * > m_buttonlists
MythUIButtonListItem * GetItemCurrent(void) const
Return the currently selected list item.
void handleVisible(MythUIButtonListItem *item)
Handle a list item becoming visible.
MythGenericTree * m_currentNode
MythUIButtonList * m_listTemplate
bool DoSetCurrentNode(MythGenericTree *node)
The base class on which all widgets and screens are based.
Definition: mythuitype.h:86
void SetCanTakeFocus(bool set=true)
Set whether this widget can take focus.
Definition: mythuitype.cpp:362
void TakingFocus(void)
virtual void SetVisible(bool visible)
QString GetXMLLocation(void) const
Definition: mythuitype.h:182
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
MythUIType * GetChildAt(QPoint p, bool recursive=true, bool focusable=true) const
Return the first MythUIType at the given coordinates.
Definition: mythuitype.cpp:241
virtual MythRect GetArea(void) const
If the object has a minimum area defined, return it, other wise return the default area.
Definition: mythuitype.cpp:885
MythUIType * m_parent
Definition: mythuitype.h:297
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:138
virtual void Reset(void)
Reset the widget to it's original state, should not reset changes made by the theme.
Definition: mythuitype.cpp:73
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
static int NormX(int width)
MythRect m_area
Definition: mythuitype.h:277
void LosingFocus(void)
static QString getFirstText(QDomElement &element)
unsigned int uint
Definition: freesurround.h:24
A C++ ripoff of the stroke library for MythTV.
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)