MythTV master
mythgenerictree.cpp
Go to the documentation of this file.
1
2// Mythui headers
3#include "mythgenerictree.h"
4#include "mythuibuttonlist.h"
5
6// Myth headers
9
10// QT headers
11#include <algorithm>
12
13class SortableMythGenericTreeList : public QList<MythGenericTree*>
14{
15 public:
17 enum SortType : std::uint8_t {SORT_STRING=0, SORT_SELECTABLE=1};
18
19 void SetSortType(SortType stype) { m_sortType = stype; }
20 void SetAttributeIndex(int index)
21 { m_attributeIndex = (index >= 0) ? index : 0; }
22
23 static bool sortByString(const MythGenericTree *one, const MythGenericTree *two)
24 {
25 return one->GetSortText() < two->GetSortText();
26 }
27
28 static int sortBySelectable(const MythGenericTree *one, const MythGenericTree *two)
29 {
30 bool onesel = one->isSelectable();
31 bool twosel = two->isSelectable();
32
33 if (onesel == twosel)
34 return 0;
35 if (onesel && !twosel)
36 return 1;
37 return -1;
38 }
39
40 void Sort(SortType stype, int attributeIndex = 0)
41 {
42 m_sortType = stype;
43 m_attributeIndex = attributeIndex;
44 switch (m_sortType)
45 {
46 case SORT_STRING:
47 // QList doesn't always play well with std::ranges
48 // NOLINTNEXTLINE(modernize-use-ranges)
49 std::sort(begin(), end(), sortByString);
50 break;
51 case SORT_SELECTABLE:
52 // QList doesn't always play well with std::ranges
53 // NOLINTNEXTLINE(modernize-use-ranges)
54 std::sort(begin(), end(), sortBySelectable);
55 break;
56 }
57 }
58
59 private:
61 int m_attributeIndex {0}; // for getAttribute
62};
63
65
66MythGenericTree::MythGenericTree(QString a_string, int an_int,
67 bool selectable_flag)
68 : m_text(std::move(a_string)),
69 m_int(an_int),
70 m_subnodes(new SortableMythGenericTreeList),
71 m_selectable(selectable_flag)
72{
74}
75
77{
79 delete m_subnodes;
80}
81
83{
84 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
85 if (m_sortText.isEmpty() and not m_text.isEmpty())
86 m_sortText = sh->doTitle(m_text);
87}
88
89MythGenericTree* MythGenericTree::addNode(const QString &a_string, int an_int,
90 bool selectable_flag, bool visible)
91{
92 auto *new_node = new MythGenericTree(a_string.simplified(),
93 an_int, selectable_flag);
94 new_node->SetVisible(visible);
95 return addNode(new_node);
96}
97
99 const QString &sortText, int an_int, bool
100 selectable_flag, bool visible)
101{
102 auto *new_node = new MythGenericTree(a_string.simplified(),
103 an_int, selectable_flag);
104 new_node->SetVisible(visible);
105 new_node->SetSortText(sortText);
106
107 return addNode(new_node);
108}
109
111{
112 child->setParent(this);
113 m_subnodes->append(child);
114 if (child->IsVisible())
116
117 return child;
118}
119
121{
122 if (!m_parent)
123 return;
124
125 m_parent->removeNode(this);
126}
127
129{
130 if (!child)
131 return;
132
133 if (m_selectedSubnode == child)
134 m_selectedSubnode = nullptr;
135
136 m_subnodes->removeAll(child);
137 child->setParent(nullptr);
138
139 if (child && child->IsVisible())
141}
142
144{
145 if (!child)
146 return;
147
148 removeNode(child);
149 delete child;
150}
151
153{
154 if (m_subnodes->count() > 0)
155 return m_subnodes->first()->findLeaf();
156
157 return this;
158}
159
160MythGenericTree* MythGenericTree::findNode(QList<int> route_of_branches)
161{
162 // Starting from *this* node (which will often be root) find a set of
163 // branches that have id's that match the collection passed in
164 // route_of_branches. Return the end point of those branches.
165 //
166 // In practical terms, mythmusic will use this to force the playback
167 // screen's ManagedTreeList to move to a given track in a given playlist
168
169 MythGenericTree *node = nullptr;
170 for (int i = 0; i < route_of_branches.count(); i++)
171 {
172 if (!node)
173 node = this;
174
175 bool foundit = false;
176 QList<MythGenericTree*>::iterator it;
177 QList<MythGenericTree*> *children = node->getAllChildren();
178
179 if (!children)
180 break;
181
182 MythGenericTree *child = nullptr;
183
184 for (it = children->begin(); it != children->end(); ++it)
185 {
186 child = *it;
187 if (!child)
188 continue;
189 if (child->getInt() == route_of_branches[i])
190 {
191 node = child;
192 foundit = true;
193 break;
194 }
195 }
196
197 if (!foundit)
198 break;
199 }
200
201 return nullptr;
202}
203
205{
206 return m_subnodes->indexOf(child);
207}
208
210{
211 if (m_parent)
212 return m_parent->getChildPosition(this);
213 return 0;
214}
215
217{
218 QList<int> routeByID;
219
220 routeByID.push_front(getInt());
221
222 MythGenericTree *parent = this;
223 while( (parent = parent->getParent()) )
224 {
225 routeByID.push_front(parent->getInt());
226 }
227 return routeByID;
228}
229
231{
232 QStringList routeByString;
233
234 routeByString.push_front(GetText());
235
236 MythGenericTree *parent = this;
237 while( (parent = parent->getParent()) )
238 {
239 routeByString.push_front(parent->GetText());
240 }
241 return routeByString;
242}
243
244QList<MythGenericTree*> MythGenericTree::getRoute(void)
245{
246 QList<MythGenericTree*> route;
247
248 route.push_front(this);
249
250 MythGenericTree *parent = this;
251 while( (parent = parent->getParent()) )
252 {
253 route.push_front(parent);
254 }
255 return route;
256}
257
259{
260 return m_subnodes->count();
261}
262
264{
265 if (m_parent)
266 return m_parent->childCount();
267 return 1;
268}
269
274{
275 QList<MythGenericTree *> route = getRoute();
276
277 return (route.size() - 1);
278}
279
280QList<MythGenericTree*> *MythGenericTree::getAllChildren() const
281{
282 return m_subnodes;
283}
284
286{
287 if (reference >= (uint)m_subnodes->count())
288 return nullptr;
289
290 return m_subnodes->at(reference);
291}
292
294{
295 if (reference >= (uint)m_subnodes->count())
296 return nullptr;
297
298 QList<MythGenericTree*> *list = m_subnodes;
299
300 uint n = 0;
301 for (auto *child : std::as_const(*list))
302 {
303 if (child->IsVisible())
304 {
305 if (n == reference)
306 return child;
307 n++;
308 }
309 }
310
311 return nullptr;
312}
313
315{
316 MythGenericTree *selectedChild = nullptr;
317
319 selectedChild = m_selectedSubnode;
320 else if (onlyVisible)
321 selectedChild = getVisibleChildAt(0);
322 else
323 selectedChild = getChildAt(0);
324
325 return selectedChild;
326}
327
329{
330 if (m_parent)
332 else
333 LOG(VB_GENERAL, LOG_ERR, "Top level can't become selected child");
334}
335
337{
338 if (!m_parent)
339 {
340 // I'm root = no siblings
341 return nullptr;
342 }
343
344 int position = m_parent->getChildPosition(this);
345
346 if (position < number_up)
347 {
348 // not enough siblings "above" me
349 return nullptr;
350 }
351
352 return m_parent->getChildAt(position - number_up);
353}
354
356{
357 if (!m_parent)
358 {
359 // I'm root = no siblings
360 return nullptr;
361 }
362
363 int position = m_parent->getChildPosition(this);
364
365 if (position + number_down >= m_parent->childCount())
366 {
367 // not enough siblings "below" me
368 return nullptr;
369 }
370
371 return m_parent->getChildAt(position + number_down);
372}
373
375{
376 if (m_parent)
377 return m_parent;
378 return nullptr;
379}
380
382{
383 QList<MythGenericTree*> *children = getAllChildren();
384 if (children && children->count() > 0)
385 {
386 SortableMythGenericTreeList::Iterator it;
387 MythGenericTree *child = nullptr;
388
389 for (it = children->begin(); it != children->end(); ++it)
390 {
391 child = *it;
392 if (!child)
393 continue;
394 if (child->GetText() == a_name)
395 return child;
396 }
397 }
398
399 return nullptr;
400}
401
403{
404 QList<MythGenericTree*> *children = getAllChildren();
405 if (children && children->count() > 0)
406 {
407 SortableMythGenericTreeList::Iterator it;
408 MythGenericTree *child = nullptr;
409
410 for (it = children->begin(); it != children->end(); ++it)
411 {
412 child = *it;
413 if (!child)
414 continue;
415 if (child->getInt() == an_int)
416 return child;
417 }
418 }
419
420 return nullptr;
421}
422
424{
426
427 QList<MythGenericTree*> *children = getAllChildren();
428 if (children && children->count() > 0)
429 {
430 SortableMythGenericTreeList::Iterator it;
431 MythGenericTree *child = nullptr;
432
433 for (it = children->begin(); it != children->end(); ++it)
434 {
435 child = *it;
436 if (!child)
437 continue;
438 child->sortByString();
439 }
440 }
441}
442
444{
446
447 QList<MythGenericTree*>::iterator it;
448 it = m_subnodes->begin();
449 MythGenericTree *child = nullptr;
450 while ((child = *it) != nullptr)
451 {
452 child->sortBySelectable();
453 ++it;
454 }
455}
456
458{
459 m_selectedSubnode = nullptr;
460 while (!m_subnodes->isEmpty())
461 {
462 MythGenericTree *child = m_subnodes->takeFirst();
463 delete child;
464 child = nullptr;
465 }
466 m_subnodes->clear();
467}
468
470{
471 if (item == m_subnodes->first() && flag)
472 return;
473 if (item == m_subnodes->last() && !flag)
474 return;
475
476 int num = m_subnodes->indexOf(item);
477
478 int insertat = 0;
479 if (flag)
480 insertat = num - 1;
481 else
482 insertat = num + 1;
483
484 m_subnodes->removeAt(num);
485 m_subnodes->insert(insertat, item);
486}
487
489{
490 if (m_visible == visible)
491 return;
492
493 m_visible = visible;
494
495 if (!m_parent)
496 return;
497
498 if (visible)
500 else
502}
503
505{
506 auto *item = new MythUIButtonListItem(list, GetText());
507 item->SetData(QVariant::fromValue(this));
508 item->SetTextFromMap(m_strings);
509 item->SetImageFromMap(m_imageFilenames);
510 item->SetStatesFromMap(m_states);
511 item->SetTextCb(m_textCb.fn, m_textCb.data);
512 item->SetImageCb(m_imageCb.fn, m_imageCb.data);
513 item->SetStateCb(m_stateCb.fn, m_stateCb.data);
514
515 if (visibleChildCount() > 0)
516 item->setDrawArrow(true);
517
518 return item;
519}
520
521void MythGenericTree::SetText(const QString &text, const QString &name,
522 const QString &state)
523{
524 if (!name.isEmpty())
525 {
526 TextProperties textprop;
527 textprop.text = text;
528 textprop.state = state;
529 m_strings.insert(name, textprop);
530 }
531 else
532 {
533 m_text = text;
534 m_sortText = nullptr;
536 }
537}
538
540 const QString &state)
541{
542 InfoMap::const_iterator map_it = infoMap.begin();
543 while (map_it != infoMap.end())
544 {
545 TextProperties textprop;
546 textprop.text = (*map_it);
547 textprop.state = state;
548 m_strings[map_it.key()] = textprop;
549 ++map_it;
550 }
551}
552
554{
555 m_textCb.fn = fn;
556 m_textCb.data = data;
557}
558
559QString MythGenericTree::GetText(const QString &name) const
560{
561 if (name.isEmpty())
562 return m_text;
563
564 if (m_textCb.fn != nullptr)
565 {
566 QString result = m_textCb.fn(name, m_textCb.data);
567 if (!result.isEmpty())
568 return result;
569 }
570
571 if (m_strings.contains(name))
572 return m_strings[name].text;
573 return {};
574}
575
576void MythGenericTree::SetImage(const QString &filename, const QString &name)
577{
578 if (!name.isEmpty())
579 m_imageFilenames.insert(name, filename);
580}
581
583{
584 m_imageFilenames.clear();
585 m_imageFilenames = infoMap;
586}
587
589{
590 m_imageCb.fn = fn;
591 m_imageCb.data = data;
592}
593
594QString MythGenericTree::GetImage(const QString &name) const
595{
596 if (name.isEmpty())
597 return {};
598
599 if (m_imageCb.fn != nullptr)
600 {
601 QString result = m_imageCb.fn(name, m_imageCb.data);
602 if (!result.isEmpty())
603 return result;
604 }
605
606 InfoMap::const_iterator it = m_imageFilenames.find(name);
607 if (it != m_imageFilenames.end())
608 return *it;
609
610 return {};
611}
612
614{
615 m_states.clear();
616 m_states = infoMap;
617}
618
620{
621 m_stateCb.fn = fn;
622 m_stateCb.data = data;
623}
624
625void MythGenericTree::DisplayState(const QString &state, const QString &name)
626{
627 if (!name.isEmpty())
628 m_states.insert(name, state);
629}
630
631QString MythGenericTree::GetState(const QString &name) const
632{
633 if (name.isEmpty())
634 return {};
635
636 if (m_stateCb.fn != nullptr)
637 {
638 QString result = m_stateCb.fn(name, m_stateCb.data);
639 if (!result.isEmpty())
640 return result;
641 }
642
643 InfoMap::const_iterator it = m_states.find(name);
644 if (it != m_states.end())
645 return *it;
646
647 return {};
648}
MythGenericTree * getVisibleChildAt(uint reference) const
MythGenericTree * m_parent
int getInt() const
bool isSelectable() const
MythGenericTree * findLeaf()
QString GetText(const QString &name="") const
void setParent(MythGenericTree *a_parent)
void SetImageFromMap(const InfoMap &infoMap)
int currentDepth(void)
Establish how deep in the current tree this node lies.
void setSelectedChild(MythGenericTree *a_node)
MythGenericTree * getParent(void) const
void SetImageCb(mgtCbFn fn, void *data)
SortableMythGenericTreeList * m_subnodes
void SetText(const QString &text, const QString &name="", const QString &state="")
void SetVisible(bool visible)
int siblingCount(void) const
void ensureSortFields(void)
MythGenericTree * addNode(const QString &a_string, int an_int=0, bool selectable_flag=false, bool visible=true)
MythGenericTree * getChildById(int an_int) const
void removeNode(MythGenericTree *child)
void SetImage(const QString &filename, const QString &name="")
MythGenericTree(QString a_string="", int an_int=0, bool selectable_flag=false)
QMap< QString, TextProperties > m_strings
MythGenericTree * prevSibling(int number_up)
void DisplayStateFromMap(const InfoMap &infoMap)
QStringList getRouteByString(void)
virtual MythUIButtonListItem * CreateListButton(MythUIButtonList *list)
QList< int > getRouteById(void)
MythGenericTree * getChildAt(uint reference) const
void SetTextFromMap(const InfoMap &infoMap, const QString &state="")
MythGenericTree * findNode(QList< int > route_of_branches)
QString GetState(const QString &name="") const
void deleteNode(MythGenericTree *child)
bool IsVisible() const
int childCount(void) const
InfoMap m_imageFilenames
MythGenericTree * getSelectedChild(bool onlyVisible=false) const
void SetStateCb(mgtCbFn fn, void *data)
virtual ~MythGenericTree()
uint visibleChildCount() const
void becomeSelectedChild(void)
void DisplayState(const QString &state, const QString &name="")
MythGenericTree * getChildByName(const QString &a_name) const
void MoveItemUpDown(MythGenericTree *item, bool flag)
QList< MythGenericTree * > getRoute(void)
void SetTextCb(mgtCbFn fn, void *data)
QList< MythGenericTree * > * getAllChildren() const
void DetachParent(void)
Detach this node/branch from it's parent without deleting it, it can then be reattached elsewhere or ...
MythGenericTree * m_selectedSubnode
QString GetImage(const QString &name="") const
int getChildPosition(MythGenericTree *child) const
MythGenericTree * nextSibling(int number_down)
QString GetSortText() const
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
static bool sortByString(const MythGenericTree *one, const MythGenericTree *two)
void SetSortType(SortType stype)
void Sort(SortType stype, int attributeIndex=0)
static int sortBySelectable(const MythGenericTree *one, const MythGenericTree *two)
unsigned int uint
Definition: compat.h:60
QString(*)(const QString &name, void *data) mgtCbFn
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
std::shared_ptr< MythSortHelper > getMythSortHelper(void)
Get a pointer to the MythSortHelper singleton.
QHash< QString, QString > InfoMap
Definition: mythtypes.h:15
mgtCbFn fn