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
24 {
25 return one->GetSortText() < two->GetSortText();
26 }
27
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 std::sort(begin(), end(), sortByString);
48 break;
49 case SORT_SELECTABLE:
50 std::sort(begin(), end(), sortBySelectable);
51 break;
52 }
53 }
54
55 private:
57 int m_attributeIndex {0}; // for getAttribute
58};
59
61
62MythGenericTree::MythGenericTree(QString a_string, int an_int,
63 bool selectable_flag)
64 : m_text(std::move(a_string)),
65 m_int(an_int),
66 m_subnodes(new SortableMythGenericTreeList),
67 m_selectable(selectable_flag)
68{
70}
71
73{
75 delete m_subnodes;
76}
77
79{
80 std::shared_ptr<MythSortHelper>sh = getMythSortHelper();
81 if (m_sortText.isEmpty() and not m_text.isEmpty())
82 m_sortText = sh->doTitle(m_text);
83}
84
85MythGenericTree* MythGenericTree::addNode(const QString &a_string, int an_int,
86 bool selectable_flag, bool visible)
87{
88 auto *new_node = new MythGenericTree(a_string.simplified(),
89 an_int, selectable_flag);
90 new_node->SetVisible(visible);
91 return addNode(new_node);
92}
93
95 const QString &sortText, int an_int, bool
96 selectable_flag, bool visible)
97{
98 auto *new_node = new MythGenericTree(a_string.simplified(),
99 an_int, selectable_flag);
100 new_node->SetVisible(visible);
101 new_node->SetSortText(sortText);
102
103 return addNode(new_node);
104}
105
107{
108 child->setParent(this);
109 m_subnodes->append(child);
110 if (child->IsVisible())
112
113 return child;
114}
115
117{
118 if (!m_parent)
119 return;
120
121 m_parent->removeNode(this);
122}
123
125{
126 if (!child)
127 return;
128
129 if (m_selectedSubnode == child)
130 m_selectedSubnode = nullptr;
131
132 m_subnodes->removeAll(child);
133 child->setParent(nullptr);
134
135 if (child && child->IsVisible())
137}
138
140{
141 if (!child)
142 return;
143
144 removeNode(child);
145 delete child;
146}
147
149{
150 if (m_subnodes->count() > 0)
151 return m_subnodes->first()->findLeaf();
152
153 return this;
154}
155
156MythGenericTree* MythGenericTree::findNode(QList<int> route_of_branches)
157{
158 // Starting from *this* node (which will often be root) find a set of
159 // branches that have id's that match the collection passed in
160 // route_of_branches. Return the end point of those branches.
161 //
162 // In practical terms, mythmusic will use this to force the playback
163 // screen's ManagedTreeList to move to a given track in a given playlist
164
165 MythGenericTree *node = nullptr;
166 for (int i = 0; i < route_of_branches.count(); i++)
167 {
168 if (!node)
169 node = this;
170
171 bool foundit = false;
172 QList<MythGenericTree*>::iterator it;
173 QList<MythGenericTree*> *children = node->getAllChildren();
174
175 if (!children)
176 break;
177
178 MythGenericTree *child = nullptr;
179
180 for (it = children->begin(); it != children->end(); ++it)
181 {
182 child = *it;
183 if (!child)
184 continue;
185 if (child->getInt() == route_of_branches[i])
186 {
187 node = child;
188 foundit = true;
189 break;
190 }
191 }
192
193 if (!foundit)
194 break;
195 }
196
197 return nullptr;
198}
199
201{
202 return m_subnodes->indexOf(child);
203}
204
206{
207 if (m_parent)
208 return m_parent->getChildPosition(this);
209 return 0;
210}
211
213{
214 QList<int> routeByID;
215
216 routeByID.push_front(getInt());
217
218 MythGenericTree *parent = this;
219 while( (parent = parent->getParent()) )
220 {
221 routeByID.push_front(parent->getInt());
222 }
223 return routeByID;
224}
225
227{
228 QStringList routeByString;
229
230 routeByString.push_front(GetText());
231
232 MythGenericTree *parent = this;
233 while( (parent = parent->getParent()) )
234 {
235 routeByString.push_front(parent->GetText());
236 }
237 return routeByString;
238}
239
240QList<MythGenericTree*> MythGenericTree::getRoute(void)
241{
242 QList<MythGenericTree*> route;
243
244 route.push_front(this);
245
246 MythGenericTree *parent = this;
247 while( (parent = parent->getParent()) )
248 {
249 route.push_front(parent);
250 }
251 return route;
252}
253
255{
256 return m_subnodes->count();
257}
258
260{
261 if (m_parent)
262 return m_parent->childCount();
263 return 1;
264}
265
270{
271 QList<MythGenericTree *> route = getRoute();
272
273 return (route.size() - 1);
274}
275
276QList<MythGenericTree*> *MythGenericTree::getAllChildren() const
277{
278 return m_subnodes;
279}
280
282{
283 if (reference >= (uint)m_subnodes->count())
284 return nullptr;
285
286 return m_subnodes->at(reference);
287}
288
290{
291 if (reference >= (uint)m_subnodes->count())
292 return nullptr;
293
294 QList<MythGenericTree*> *list = m_subnodes;
295
296 uint n = 0;
297 for (auto *child : std::as_const(*list))
298 {
299 if (child->IsVisible())
300 {
301 if (n == reference)
302 return child;
303 n++;
304 }
305 }
306
307 return nullptr;
308}
309
311{
312 MythGenericTree *selectedChild = nullptr;
313
315 selectedChild = m_selectedSubnode;
316 else if (onlyVisible)
317 selectedChild = getVisibleChildAt(0);
318 else
319 selectedChild = getChildAt(0);
320
321 return selectedChild;
322}
323
325{
326 if (m_parent)
328 else
329 LOG(VB_GENERAL, LOG_ERR, "Top level can't become selected child");
330}
331
333{
334 if (!m_parent)
335 {
336 // I'm root = no siblings
337 return nullptr;
338 }
339
340 int position = m_parent->getChildPosition(this);
341
342 if (position < number_up)
343 {
344 // not enough siblings "above" me
345 return nullptr;
346 }
347
348 return m_parent->getChildAt(position - number_up);
349}
350
352{
353 if (!m_parent)
354 {
355 // I'm root = no siblings
356 return nullptr;
357 }
358
359 int position = m_parent->getChildPosition(this);
360
361 if (position + number_down >= m_parent->childCount())
362 {
363 // not enough siblings "below" me
364 return nullptr;
365 }
366
367 return m_parent->getChildAt(position + number_down);
368}
369
371{
372 if (m_parent)
373 return m_parent;
374 return nullptr;
375}
376
378{
379 QList<MythGenericTree*> *children = getAllChildren();
380 if (children && children->count() > 0)
381 {
382 SortableMythGenericTreeList::Iterator it;
383 MythGenericTree *child = nullptr;
384
385 for (it = children->begin(); it != children->end(); ++it)
386 {
387 child = *it;
388 if (!child)
389 continue;
390 if (child->GetText() == a_name)
391 return child;
392 }
393 }
394
395 return nullptr;
396}
397
399{
400 QList<MythGenericTree*> *children = getAllChildren();
401 if (children && children->count() > 0)
402 {
403 SortableMythGenericTreeList::Iterator it;
404 MythGenericTree *child = nullptr;
405
406 for (it = children->begin(); it != children->end(); ++it)
407 {
408 child = *it;
409 if (!child)
410 continue;
411 if (child->getInt() == an_int)
412 return child;
413 }
414 }
415
416 return nullptr;
417}
418
420{
422
423 QList<MythGenericTree*> *children = getAllChildren();
424 if (children && children->count() > 0)
425 {
426 SortableMythGenericTreeList::Iterator it;
427 MythGenericTree *child = nullptr;
428
429 for (it = children->begin(); it != children->end(); ++it)
430 {
431 child = *it;
432 if (!child)
433 continue;
434 child->sortByString();
435 }
436 }
437}
438
440{
442
443 QList<MythGenericTree*>::iterator it;
444 it = m_subnodes->begin();
445 MythGenericTree *child = nullptr;
446 while ((child = *it) != nullptr)
447 {
448 child->sortBySelectable();
449 ++it;
450 }
451}
452
454{
455 m_selectedSubnode = nullptr;
456 while (!m_subnodes->isEmpty())
457 {
458 MythGenericTree *child = m_subnodes->takeFirst();
459 delete child;
460 child = nullptr;
461 }
462 m_subnodes->clear();
463}
464
466{
467 if (item == m_subnodes->first() && flag)
468 return;
469 if (item == m_subnodes->last() && !flag)
470 return;
471
472 int num = m_subnodes->indexOf(item);
473
474 int insertat = 0;
475 if (flag)
476 insertat = num - 1;
477 else
478 insertat = num + 1;
479
480 m_subnodes->removeAt(num);
481 m_subnodes->insert(insertat, item);
482}
483
485{
486 if (m_visible == visible)
487 return;
488
489 m_visible = visible;
490
491 if (!m_parent)
492 return;
493
494 if (visible)
496 else
498}
499
501{
502 auto *item = new MythUIButtonListItem(list, GetText());
503 item->SetData(QVariant::fromValue(this));
504 item->SetTextFromMap(m_strings);
505 item->SetImageFromMap(m_imageFilenames);
506 item->SetStatesFromMap(m_states);
507 item->SetTextCb(m_textCb.fn, m_textCb.data);
508 item->SetImageCb(m_imageCb.fn, m_imageCb.data);
509 item->SetStateCb(m_stateCb.fn, m_stateCb.data);
510
511 if (visibleChildCount() > 0)
512 item->setDrawArrow(true);
513
514 return item;
515}
516
517void MythGenericTree::SetText(const QString &text, const QString &name,
518 const QString &state)
519{
520 if (!name.isEmpty())
521 {
522 TextProperties textprop;
523 textprop.text = text;
524 textprop.state = state;
525 m_strings.insert(name, textprop);
526 }
527 else
528 {
529 m_text = text;
530 m_sortText = nullptr;
532 }
533}
534
536 const QString &state)
537{
538 InfoMap::const_iterator map_it = infoMap.begin();
539 while (map_it != infoMap.end())
540 {
541 TextProperties textprop;
542 textprop.text = (*map_it);
543 textprop.state = state;
544 m_strings[map_it.key()] = textprop;
545 ++map_it;
546 }
547}
548
550{
551 m_textCb.fn = fn;
552 m_textCb.data = data;
553}
554
555QString MythGenericTree::GetText(const QString &name) const
556{
557 if (name.isEmpty())
558 return m_text;
559
560 if (m_textCb.fn != nullptr)
561 {
562 QString result = m_textCb.fn(name, m_textCb.data);
563 if (!result.isEmpty())
564 return result;
565 }
566
567 if (m_strings.contains(name))
568 return m_strings[name].text;
569 return {};
570}
571
572void MythGenericTree::SetImage(const QString &filename, const QString &name)
573{
574 if (!name.isEmpty())
575 m_imageFilenames.insert(name, filename);
576}
577
579{
580 m_imageFilenames.clear();
581 m_imageFilenames = infoMap;
582}
583
585{
586 m_imageCb.fn = fn;
587 m_imageCb.data = data;
588}
589
590QString MythGenericTree::GetImage(const QString &name) const
591{
592 if (name.isEmpty())
593 return {};
594
595 if (m_imageCb.fn != nullptr)
596 {
597 QString result = m_imageCb.fn(name, m_imageCb.data);
598 if (!result.isEmpty())
599 return result;
600 }
601
602 InfoMap::const_iterator it = m_imageFilenames.find(name);
603 if (it != m_imageFilenames.end())
604 return *it;
605
606 return {};
607}
608
610{
611 m_states.clear();
612 m_states = infoMap;
613}
614
616{
617 m_stateCb.fn = fn;
618 m_stateCb.data = data;
619}
620
621void MythGenericTree::DisplayState(const QString &state, const QString &name)
622{
623 if (!name.isEmpty())
624 m_states.insert(name, state);
625}
626
627QString MythGenericTree::GetState(const QString &name) const
628{
629 if (name.isEmpty())
630 return {};
631
632 if (m_stateCb.fn != nullptr)
633 {
634 QString result = m_stateCb.fn(name, m_stateCb.data);
635 if (!result.isEmpty())
636 return result;
637 }
638
639 InfoMap::const_iterator it = m_states.find(name);
640 if (it != m_states.end())
641 return *it;
642
643 return {};
644}
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(MythGenericTree *one, MythGenericTree *two)
void SetSortType(SortType stype)
void Sort(SortType stype, int attributeIndex=0)
static int sortBySelectable(MythGenericTree *one, MythGenericTree *two)
unsigned int uint
Definition: freesurround.h:24
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
STL namespace.
mgtCbFn fn