MythTV master
xmlparsebase.cpp
Go to the documentation of this file.
1
2// Own header
3#include "xmlparsebase.h"
4
5// C++/C headers
6#include <typeinfo>
7
8// QT headers
9#include <QFile>
10#include <QDomDocument>
11#include <QString>
12#include <QBrush>
13#include <QLinearGradient>
14#include <QRadialGradient>
15
16// libmythbase headers
17#include "libmythbase/mythconfig.h"
19
20// Mythui headers
21#include "mythmainwindow.h"
22#include "mythuihelper.h"
23
24/* ui type includes */
25#include "mythscreentype.h"
26#include "mythuiimage.h"
27#include "mythuiprocedural.h"
28#include "mythuitext.h"
29#include "mythuitextedit.h"
30#include "mythuiclock.h"
31#include "mythuibuttonlist.h"
32#include "mythuibutton.h"
33#include "mythuispinbox.h"
34#include "mythuicheckbox.h"
35#include "mythuiprogressbar.h"
36#include "mythuiscrollbar.h"
37#include "mythuigroup.h"
38
39#if CONFIG_QTWEBENGINE
40#include "mythuiwebbrowser.h"
41#endif
42
43#include "mythuiguidegrid.h"
44#include "mythuishape.h"
45#include "mythuibuttontree.h"
46#include "mythuivideo.h"
47#include "mythuieditbar.h"
48#include "mythfontproperties.h"
49
50#define LOC QString("XMLParseBase: ")
51
52QString XMLParseBase::getFirstText(QDomElement &element)
53{
54 for (QDomNode dname = element.firstChild(); !dname.isNull();
55 dname = dname.nextSibling())
56 {
57 QDomText t = dname.toText();
58 if (!t.isNull())
59 return t.data();
60 }
61 return {};
62}
63
64bool XMLParseBase::parseBool(const QString &text)
65{
66 QString s = text.toLower();
67 return (s == "yes" || s == "true" || (s.toInt() != 0));
68}
69
70bool XMLParseBase::parseBool(QDomElement &element)
71{
72 return parseBool(getFirstText(element));
73}
74
75MythPoint XMLParseBase::parsePoint(const QString &text, bool normalize)
76{
77 MythPoint retval;
78 QStringList values = text.split(',', Qt::SkipEmptyParts);
79 if (values.size() == 2)
80 retval = MythPoint(values[0], values[1]);
81
82 if (normalize)
83 retval.NormPoint();
84
85 return retval;
86}
87
88MythPoint XMLParseBase::parsePoint(QDomElement &element, bool normalize)
89{
90 return parsePoint(getFirstText(element), normalize);
91}
92
93QSize XMLParseBase::parseSize(const QString &text, bool normalize)
94{
95 int x = 0;
96 int y = 0;
97 QSize retval;
98
99 QStringList tmp = text.split(",");
100 bool x_ok = false;
101 bool y_ok = false;
102 if (tmp.size() >= 2)
103 {
104 x = tmp[0].toInt(&x_ok);
105 y = tmp[1].toInt(&y_ok);
106 }
107
108 if (x_ok && y_ok)
109 {
110 if (x == -1 || y == -1)
111 {
112 QRect uiSize = GetMythMainWindow()->GetUIScreenRect();
113 x = uiSize.width();
114 y = uiSize.height();
115 normalize = false;
116 }
117
118 retval = QSize(x, y);
119 }
120
121 if (normalize)
122 retval = GetMythMainWindow()->NormSize(retval);
123
124 return retval;
125}
126
127QSize XMLParseBase::parseSize(QDomElement &element, bool normalize)
128{
129 return parseSize(getFirstText(element), normalize);
130}
131
132MythRect XMLParseBase::parseRect(const QString &text, bool normalize)
133{
134 MythRect retval;
135 QStringList values = text.split(',', Qt::SkipEmptyParts);
136 if (values.size() == 4)
137 retval = MythRect(values[0], values[1], values[2], values[3]);
138 if (values.size() == 5)
139 retval = MythRect(values[0], values[1], values[2], values[3],
140 values[4]);
141
142 if (normalize)
143 retval.NormRect();
144
145 return retval;
146}
147
148MythRect XMLParseBase::parseRect(QDomElement &element, bool normalize)
149{
150 return parseRect(getFirstText(element), normalize);
151}
152
153int XMLParseBase::parseAlignment(const QString &text)
154{
155 int alignment = Qt::AlignLeft | Qt::AlignTop;
156
157 QStringList values = text.split(',');
158
159 QStringList::Iterator it;
160 for ( it = values.begin(); it != values.end(); ++it )
161 {
162
163 QString align = *it;
164 align = align.trimmed();
165 align = align.toLower();
166
167 if (align == "center" || align == "allcenter")
168 {
169 alignment &= ~(Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask);
170 alignment |= Qt::AlignCenter;
171 break;
172 }
173 if (align == "justify")
174 {
175 alignment &= ~Qt::AlignHorizontal_Mask;
176 alignment |= Qt::AlignJustify;
177 }
178 else if (align == "left")
179 {
180 alignment &= ~Qt::AlignHorizontal_Mask;
181 alignment |= Qt::AlignLeft;
182 }
183 else if (align == "hcenter")
184 {
185 alignment &= ~Qt::AlignHorizontal_Mask;
186 alignment |= Qt::AlignHCenter;
187 }
188 else if (align == "right")
189 {
190 alignment &= ~Qt::AlignHorizontal_Mask;
191 alignment |= Qt::AlignRight;
192 }
193 else if (align == "top")
194 {
195 alignment &= ~Qt::AlignVertical_Mask;
196 alignment |= Qt::AlignTop;
197 }
198 else if (align == "vcenter")
199 {
200 alignment &= ~Qt::AlignVertical_Mask;
201 alignment |= Qt::AlignVCenter;
202 }
203 else if (align == "bottom")
204 {
205 alignment &= ~Qt::AlignVertical_Mask;
206 alignment |= Qt::AlignBottom;
207 }
208 }
209
210 return alignment;
211}
212
213int XMLParseBase::parseAlignment(QDomElement &element)
214{
215 return parseAlignment(getFirstText(element));
216}
217
218QBrush XMLParseBase::parseGradient(const QDomElement &element)
219{
220 QBrush brush;
221 QString gradientStart = element.attribute("start", "");
222 QString gradientEnd = element.attribute("end", "");
223 int gradientAlpha = element.attribute("alpha", "255").toInt();
224 QString direction = element.attribute("direction", "vertical");
225
226 QGradientStops stops;
227
228 if (!gradientStart.isEmpty())
229 {
230 auto startColor = QColor(gradientStart);
231 startColor.setAlpha(gradientAlpha);
232 QGradientStop stop(0.0, startColor);
233 stops.append(stop);
234 }
235
236 for (QDomNode child = element.firstChild(); !child.isNull();
237 child = child.nextSibling())
238 {
239 QDomElement childElem = child.toElement();
240 if (childElem.tagName() == "stop")
241 {
242 double position = childElem.attribute("position", "0").toDouble();
243 QString color = childElem.attribute("color", "");
244 int alpha = childElem.attribute("alpha", "-1").toInt();
245 if (alpha < 0)
246 alpha = gradientAlpha;
247 auto stopColor = QColor(color);
248 stopColor.setAlpha(alpha);
249 QGradientStop stop((position / 100), stopColor);
250 stops.append(stop);
251 }
252 }
253
254 if (!gradientEnd.isEmpty())
255 {
256 auto endColor = QColor(gradientEnd);
257 endColor.setAlpha(gradientAlpha);
258 QGradientStop stop(1.0, endColor);
259 stops.append(stop);
260 }
261
262 if (direction == "radial")
263 {
264 QRadialGradient gradient;
265 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
266 double x1 = 0.5;
267 double y1 = 0.5;
268 double radius = 0.5;
269 gradient.setCenter(x1,y1);
270 gradient.setFocalPoint(x1,y1);
271 gradient.setRadius(radius);
272 gradient.setStops(stops);
273 brush = QBrush(gradient);
274 }
275 else // Linear
276 {
277 QLinearGradient gradient;
278 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
279 double x1 = 0.0;
280 double y1 = 0.0;
281 double x2 = 0.0;
282 double y2 = 0.0;
283 if (direction == "vertical")
284 {
285 x1 = 0.5;
286 x2 = 0.5;
287 y1 = 0.0;
288 y2 = 1.0;
289 }
290 else if (direction == "diagonal")
291 {
292 x1 = 0.0;
293 x2 = 1.0;
294 y1 = 0.0;
295 y2 = 1.0;
296 }
297 else // Horizontal
298 {
299 x1 = 0.0;
300 x2 = 1.0;
301 y1 = 0.5;
302 y2 = 0.5;
303 }
304
305 gradient.setStart(x1, y1);
306 gradient.setFinalStop(x2, y2);
307 gradient.setStops(stops);
308 brush = QBrush(gradient);
309 }
310
311
312 return brush;
313}
314
315QString XMLParseBase::parseText(QDomElement &element)
316{
317 QString text = getFirstText(element);
318
319 // Escape xml-escaped newline
320 text.replace("\\n", QString("<newline>"));
321
322 // Remove newline whitespace added by
323 // xml formatting
324 QStringList lines = text.split('\n');
325 QStringList::iterator lineIt;
326
327 for (lineIt = lines.begin(); lineIt != lines.end(); ++lineIt)
328 {
329 (*lineIt) = (*lineIt).trimmed();
330 }
331
332 text = lines.join(" ");
333
334 text.replace(QString("<newline>"), QString("\n"));
335
336 return text;
337}
338
340static QStringList loadedBaseFiles;
341
343{
345 globalObjectStore = new MythUIType(nullptr, "global store");
346 return globalObjectStore;
347}
348
350{
351 delete globalObjectStore;
352 globalObjectStore = nullptr;
354
355 // clear any loaded base xml files which will force a reload the next time they are used
356 loadedBaseFiles.clear();
357}
358
360 QDomElement &element,
361 MythUIType *parent,
362 bool showWarnings)
363{
364 if (!parent)
365 {
366 LOG(VB_GENERAL, LOG_ERR, LOC + "Parent is NULL");
367 return;
368 }
369
370 QMap<QString, QString> dependsMap;
371 for (QDomNode child = element.firstChild(); !child.isNull();
372 child = child.nextSibling())
373 {
374 QDomElement info = child.toElement();
375 if (!info.isNull())
376 {
377 QString type = info.tagName();
378 if (type == "fontdef")
379 {
380 bool global = (GetGlobalObjectStore() == parent);
382 filename, info, parent, global, showWarnings);
383
384 if (!global && font)
385 {
386 QString name = info.attribute("name");
387 parent->AddFont(name, font);
388 }
389
390 delete font;
391 }
392 else if (type == "imagetype" ||
393 type == "procedural" ||
394 type == "textarea" ||
395 type == "group" ||
396 type == "textedit" ||
397 type == "button" ||
398 type == "buttonlist" ||
399 type == "buttonlist2" ||
400 type == "buttontree" ||
401 type == "spinbox" ||
402 type == "checkbox" ||
403 type == "statetype" ||
404 type == "clock" ||
405 type == "progressbar" ||
406 type == "scrollbar" ||
407 type == "webbrowser" ||
408 type == "guidegrid" ||
409 type == "shape" ||
410 type == "editbar" ||
411 type == "video")
412 {
413 ParseUIType(filename, info, type, parent, nullptr, showWarnings, dependsMap);
414 }
415 else
416 {
417 // This will print an error if there is no match.
418 parent->ParseElement(filename, info, showWarnings);
419 }
420 }
421 }
422 parent->SetDependsMap(dependsMap);
423 parent->ConnectDependants(true);
424 parent->Finalize();
425}
426
428 const QString &filename,
429 QDomElement &element, const QString &type,
430 MythUIType *parent,
431 MythScreenType *screen,
432 bool showWarnings,
433 QMap<QString, QString> &parentDependsMap)
434{
435 QString name = element.attribute("name", "");
436 if (name.isEmpty())
437 {
438 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
439 "This element requires a name");
440 return nullptr;
441 }
442
443 MythUIType *olduitype = nullptr;
444
445 // check for name in immediate parent as siblings cannot share names
446 if (parent && parent->GetChild(name))
447 {
448 // if we're the global object store, assume it's just a theme overriding
449 // the defaults..
450 if (parent == GetGlobalObjectStore())
451 return nullptr;
452
453 // Reuse the existing child and reparse
454 olduitype = parent->GetChild(name);
455 }
456
457 MythUIType *uitype = nullptr;
458 MythUIType *base = nullptr;
459
460 QString inherits = element.attribute("from", "");
461 if (!inherits.isEmpty())
462 {
463 if (parent)
464 base = parent->GetChild(inherits);
465
466 // might remove this
467 if (screen && !base)
468 base = screen->GetChild(inherits);
469
470 if (!base)
471 base = GetGlobalObjectStore()->GetChild(inherits);
472
473 if (!base)
474 {
475 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
476 QString("Couldn't find object '%1' to inherit '%2' from")
477 .arg(inherits, name));
478 return nullptr;
479 }
480 }
481
482 QString shadow = element.attribute("shadow", "");
483
484 if (type == "imagetype")
485 uitype = new MythUIImage(parent, name);
486 else if (type == "procedural")
487 uitype = new MythUIProcedural(parent, name);
488 else if (type == "textarea")
489 uitype = new MythUIText(parent, name);
490 else if (type == "group")
491 uitype = new MythUIGroup(parent, name);
492 else if (type == "textedit")
493 uitype = new MythUITextEdit(parent, name);
494 else if (type == "button")
495 uitype = new MythUIButton(parent, name);
496 else if (type == "buttonlist2" || type == "buttonlist")
497 uitype = new MythUIButtonList(parent, name, shadow);
498 else if (type == "buttontree")
499 uitype = new MythUIButtonTree(parent, name);
500 else if (type == "spinbox")
501 uitype = new MythUISpinBox(parent, name);
502 else if (type == "checkbox")
503 uitype = new MythUICheckBox(parent, name);
504 else if (type == "statetype")
505 uitype = new MythUIStateType(parent, name);
506 else if (type == "clock")
507 uitype = new MythUIClock(parent, name);
508 else if (type == "progressbar")
509 uitype = new MythUIProgressBar(parent, name);
510 else if (type == "scrollbar") {
511 uitype = new MythUIScrollBar(parent, name);
512#if CONFIG_QTWEBENGINE
513 } else if (type == "webbrowser") {
514 uitype = new MythUIWebBrowser(parent, name);
515#endif
516 } else if (type == "guidegrid") {
517 uitype = new MythUIGuideGrid(parent, name);
518 } else if (type == "shape") {
519 uitype = new MythUIShape(parent, name);
520 } else if (type == "editbar") {
521 uitype = new MythUIEditBar(parent, name);
522 } else if (type == "video") {
523 uitype = new MythUIVideo(parent, name);
524 } else if (type == "window" && parent == GetGlobalObjectStore()) {
525 uitype = new MythScreenType(parent, name);
526 } else {
527 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
528 "Unknown widget type.");
529 return nullptr;
530 }
531
532 if (!uitype)
533 {
534 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
535 "Failed to instantiate widget type.");
536 return nullptr;
537 }
538
539 if (olduitype && parent)
540 {
541 if (typeid(*olduitype) != typeid(*uitype))
542 {
543 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
544 QString("Duplicate name: '%1' in parent '%2'")
545 .arg(name, parent->objectName()));
546 parent->DeleteChild(olduitype);
547 }
548 else
549 {
550 parent->DeleteChild(uitype);
551 uitype = olduitype;
552 }
553 }
554
555 if (base)
556 {
557 if (typeid(*base) != typeid(*uitype))
558 {
559 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, element,
560 QString("Type of new widget '%1' doesn't match old '%2'")
561 .arg(name, inherits));
562 if (parent)
563 parent->DeleteChild(uitype);
564 return nullptr;
565 }
566 uitype->CopyFrom(base);
567
568 }
569
570 QString dependee = element.attribute("depends", "");
571 if (!dependee.isEmpty())
572 parentDependsMap.insert(name, dependee);
573
574 QFileInfo fi(filename);
575 uitype->SetXMLName(name);
576 uitype->SetXMLLocation(fi.fileName(), element.lineNumber());
577
578 // If this was copied from another uitype then it already has a depends
579 // map so we want to append to that one
580 QMap<QString, QString> dependsMap = uitype->GetDependsMap();
581 for (QDomNode child = element.firstChild(); !child.isNull();
582 child = child.nextSibling())
583 {
584 QDomElement info = child.toElement();
585 if (!info.isNull())
586 {
587 if (info.tagName() == "fontdef")
588 {
589 bool global = (GetGlobalObjectStore() == parent);
591 filename, info, parent, global, showWarnings);
592
593 if (!global && font)
594 {
595 QString name2 = info.attribute("name");
596 uitype->AddFont(name2, font);
597 }
598
599 delete font;
600 }
601 else if (info.tagName() == "imagetype" ||
602 info.tagName() == "procedural" ||
603 info.tagName() == "textarea" ||
604 info.tagName() == "group" ||
605 info.tagName() == "textedit" ||
606 info.tagName() == "button" ||
607 info.tagName() == "buttonlist" ||
608 info.tagName() == "buttonlist2" ||
609 info.tagName() == "buttontree" ||
610 info.tagName() == "spinbox" ||
611 info.tagName() == "checkbox" ||
612 info.tagName() == "statetype" ||
613 info.tagName() == "clock" ||
614 info.tagName() == "progressbar" ||
615 info.tagName() == "scrollbar" ||
616 info.tagName() == "webbrowser" ||
617 info.tagName() == "guidegrid" ||
618 info.tagName() == "shape" ||
619 info.tagName() == "editbar" ||
620 info.tagName() == "video")
621 {
622 ParseUIType(filename, info, info.tagName(),
623 uitype, screen, showWarnings, dependsMap);
624 }
625 else
626 {
627 // This will print an error if there is no match.
628 uitype->ParseElement(filename, info, showWarnings);
629 }
630 }
631 }
632 uitype->SetDependsMap(dependsMap);
633
634 uitype->Finalize();
635 return uitype;
636}
637
638bool XMLParseBase::WindowExists(const QString &xmlfile,
639 const QString &windowname)
640{
641 const QStringList searchpath = GetMythUI()->GetThemeSearchPath();
642 for (const auto & dir : std::as_const(searchpath))
643 {
644 QString themefile = dir + xmlfile;
645 QFile f(themefile);
646
647 if (!f.open(QIODevice::ReadOnly))
648 continue;
649
650 QDomDocument doc;
651#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
652 QString errorMsg;
653 int errorLine = 0;
654 int errorColumn = 0;
655
656 if (!doc.setContent(&f, false, &errorMsg, &errorLine, &errorColumn))
657 {
658 LOG(VB_GENERAL, LOG_ERR, LOC +
659 QString("Location: '%1' @ %2 column: %3"
660 "\n\t\t\tError: %4")
661 .arg(qPrintable(themefile)).arg(errorLine).arg(errorColumn)
662 .arg(qPrintable(errorMsg)));
663 f.close();
664 continue;
665 }
666#else
667 auto parseResult = doc.setContent(&f);
668 if (!parseResult)
669 {
670 LOG(VB_GENERAL, LOG_ERR, LOC +
671 QString("Location: '%1' @ %2 column: %3"
672 "\n\t\t\tError: %4")
673 .arg(qPrintable(themefile)).arg(parseResult.errorLine)
674 .arg(parseResult.errorColumn)
675 .arg(qPrintable(parseResult.errorMessage)));
676 f.close();
677 continue;
678 }
679#endif
680 f.close();
681
682 QDomElement docElem = doc.documentElement();
683 QDomNode n = docElem.firstChild();
684 while (!n.isNull())
685 {
686 QDomElement e = n.toElement();
687 if (!e.isNull())
688 {
689 if (e.tagName() == "window")
690 {
691 QString name = e.attribute("name", "");
692 if (name == windowname)
693 return true;
694 }
695 }
696 n = n.nextSibling();
697 }
698 }
699
700 return false;
701}
702
703bool XMLParseBase::LoadWindowFromXML(const QString &xmlfile,
704 const QString &windowname,
705 MythUIType *parent)
706{
707 bool onlyLoadWindows = true;
708 bool showWarnings = true;
709
710 const QStringList searchpath = GetMythUI()->GetThemeSearchPath();
711 for (const auto & dir : std::as_const(searchpath))
712 {
713 QString themefile = dir + xmlfile;
714 LOG(VB_GUI, LOG_INFO, LOC + QString("Loading window %1 from %2").arg(windowname, themefile));
715 if (doLoad(windowname, parent, themefile,
716 onlyLoadWindows, showWarnings))
717 {
718 return true;
719 }
720 LOG(VB_FILE, LOG_ERR, LOC + "No theme file " + themefile);
721 }
722
723 LOG(VB_GENERAL, LOG_ERR, LOC +
724 QString("Unable to load window '%1' from '%2'")
725 .arg(windowname, xmlfile));
726
727 return false;
728}
729
730bool XMLParseBase::doLoad(const QString &windowname,
731 MythUIType *parent,
732 const QString &filename,
733 bool onlyLoadWindows,
734 bool showWarnings)
735{
736 QDomDocument doc;
737 QFile f(filename);
738
739 if (!f.open(QIODevice::ReadOnly))
740 return false;
741
742#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
743 QString errorMsg;
744 int errorLine = 0;
745 int errorColumn = 0;
746
747 if (!doc.setContent(&f, false, &errorMsg, &errorLine, &errorColumn))
748 {
749 LOG(VB_GENERAL, LOG_ERR, LOC +
750 QString("Location: '%1' @ %2 column: %3"
751 "\n\t\t\tError: %4")
752 .arg(qPrintable(filename)).arg(errorLine).arg(errorColumn)
753 .arg(qPrintable(errorMsg)));
754 f.close();
755 return false;
756 }
757#else
758 auto parseResult = doc.setContent(&f);
759 if (!parseResult)
760 {
761 LOG(VB_GENERAL, LOG_ERR, LOC +
762 QString("Location: '%1' @ %2 column: %3"
763 "\n\t\t\tError: %4")
764 .arg(qPrintable(filename)).arg(parseResult.errorLine)
765 .arg(parseResult.errorColumn)
766 .arg(qPrintable(parseResult.errorMessage)));
767 f.close();
768 return false;
769 }
770#endif
771
772 f.close();
773
774 QDomElement docElem = doc.documentElement();
775 QDomNode n = docElem.firstChild();
776 while (!n.isNull())
777 {
778 QDomElement e = n.toElement();
779 if (!e.isNull())
780 {
781 if (e.tagName() == "include")
782 {
783 QString include = getFirstText(e);
784
785 if (!include.isEmpty())
786 LoadBaseTheme(include);
787 }
788
789 if (onlyLoadWindows && e.tagName() == "window")
790 {
791 QString name = e.attribute("name", "");
792 QString include = e.attribute("include", "");
793 if (name.isEmpty())
794 {
795 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, e,
796 "Window needs a name");
797 return false;
798 }
799
800 if (!include.isEmpty())
801 LoadBaseTheme(include);
802
803 if (name == windowname)
804 {
805 ParseChildren(filename, e, parent, showWarnings);
806 return true;
807 }
808 }
809
810 if (!onlyLoadWindows)
811 {
812 QString type = e.tagName();
813 if (type == "font" || type == "fontdef")
814 {
815 bool global = (GetGlobalObjectStore() == parent);
817 filename, e, parent, global, showWarnings);
818
819 if (!global && font)
820 {
821 QString name = e.attribute("name");
822 parent->AddFont(name, font);
823 }
824 delete font;
825 }
826 else if (type == "imagetype" ||
827 type == "procedural" ||
828 type == "textarea" ||
829 type == "group" ||
830 type == "textedit" ||
831 type == "button" ||
832 type == "buttonlist" ||
833 type == "buttonlist2" ||
834 type == "buttontree" ||
835 type == "spinbox" ||
836 type == "checkbox" ||
837 type == "statetype" ||
838 type == "window" ||
839 type == "clock" ||
840 type == "progressbar" ||
841 type == "scrollbar" ||
842 type == "webbrowser" ||
843 type == "guidegrid" ||
844 type == "shape" ||
845 type == "editbar" ||
846 type == "video")
847 {
848
849 // We don't want widgets in base.xml
850 // depending on each other so ignore dependsMap
851 QMap<QString, QString> dependsMap;
852 MythUIType *uitype = nullptr;
853 uitype = ParseUIType(filename, e, type, parent,
854 nullptr, showWarnings, dependsMap);
855 if (uitype)
856 uitype->ConnectDependants(true);
857 }
858 else
859 {
860 VERBOSE_XML(VB_GENERAL, LOG_ERR, filename, e,
861 "Unknown widget type");
862 }
863 }
864 }
865 n = n.nextSibling();
866 }
867 return !onlyLoadWindows;
868}
869
871{
872 bool ok = false;
873 bool loadOnlyWindows = false;
874 bool showWarnings = true;
875
876 const QStringList searchpath = GetMythUI()->GetThemeSearchPath();
877 for (const auto & dir : std::as_const(searchpath))
878 {
879 QString themefile = dir + "base.xml";
880 if (doLoad(QString(), GetGlobalObjectStore(), themefile,
881 loadOnlyWindows, showWarnings))
882 {
883 LOG(VB_GUI, LOG_INFO, LOC +
884 QString("Loaded base theme from '%1'").arg(themefile));
885 // Don't complain about duplicate definitions after first
886 // successful load (set showWarnings to false).
887 showWarnings = false;
888 ok = true;
889 }
890 else
891 {
892 LOG(VB_GUI | VB_FILE, LOG_WARNING, LOC +
893 QString("No theme file '%1'").arg(themefile));
894 }
895 }
896
897 return ok;
898}
899
900bool XMLParseBase::LoadBaseTheme(const QString &baseTheme)
901{
902 LOG(VB_GUI, LOG_INFO, LOC +
903 QString("Asked to load base file from '%1'").arg(baseTheme));
904
905 if (loadedBaseFiles.contains(baseTheme))
906 {
907 LOG(VB_GUI, LOG_INFO, LOC +
908 QString("Base file already loaded '%1'").arg(baseTheme));
909 return true;
910 }
911
912 bool ok = false;
913 bool loadOnlyWindows = false;
914 bool showWarnings = true;
915
916 const QStringList searchpath = GetMythUI()->GetThemeSearchPath();
917 for (const auto & dir : std::as_const(searchpath))
918 {
919 QString themefile = dir + baseTheme;
920 if (doLoad(QString(), GetGlobalObjectStore(), themefile,
921 loadOnlyWindows, showWarnings))
922 {
923 LOG(VB_GUI, LOG_INFO, LOC +
924 QString("Loaded base theme from '%1'").arg(themefile));
925 // Don't complain about duplicate definitions after first
926 // successful load (set showWarnings to false).
927 showWarnings = false;
928 ok = true;
929 }
930 else
931 {
932 LOG(VB_GUI | VB_FILE, LOG_WARNING, LOC +
933 QString("No theme file '%1'").arg(themefile));
934 }
935 }
936
937 if (ok)
938 loadedBaseFiles.append(baseTheme);
939
940 return ok;
941}
942
943bool XMLParseBase::CopyWindowFromBase(const QString &windowname,
944 MythScreenType *win)
945{
946 MythUIType *ui = GetGlobalObjectStore()->GetChild(windowname);
947 if (!ui)
948 {
949 LOG(VB_GENERAL, LOG_ERR, LOC +
950 QString("Unable to load window '%1' from base") .arg(windowname));
951 return false;
952 }
953
954 auto *st = dynamic_cast<MythScreenType *>(ui);
955 if (!st)
956 {
957 LOG(VB_GENERAL, LOG_ERR, LOC +
958 QString("UI Object '%1' is not a ScreenType") .arg(windowname));
959 return false;
960 }
961
962 win->CopyFrom(st);
963 return true;
964}
static MythFontProperties * ParseFromXml(const QString &filename, const QDomElement &element, MythUIType *parent=nullptr, bool addToGlobal=false, bool showWarnings=true)
Wrapper around QPoint allowing us to handle percentage and other relative values for positioning in m...
Definition: mythrect.h:89
void NormPoint(void)
Definition: mythrect.cpp:471
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Definition: mythrect.h:18
void NormRect(void)
Definition: mythrect.cpp:109
Screen in which all other widgets are contained and rendered.
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
List widget, displays list items in a variety of themeable arrangements and can trigger signals when ...
A tree widget for displaying and navigating a MythGenericTree()
A single button widget.
Definition: mythuibutton.h:22
A checkbox widget supporting three check states - on,off,half and two conditions - selected and unsel...
A simple text clock widget.
Definition: mythuiclock.h:26
A narrow purpose widget used to represent cut positions and regions when editing a video.
Definition: mythuieditbar.h:17
Create a group of widgets.
Definition: mythuigroup.h:12
A narrow purpose widget used to show television programs and the timeslots they occupy on channels.
Image widget, displays a single image or multiple images in sequence.
Definition: mythuiimage.h:98
Progress bar widget.
QSize NormSize(QSize Size) const
Scroll bar widget.
A widget for rendering primitive shapes and lines.
Definition: mythuishape.h:22
A widget for offering a range of numerical values where only the the bounding values and interval are...
Definition: mythuispinbox.h:23
This widget is used for grouping other widgets for display when a particular named state is called.
A text entry and edit widget.
All purpose text widget, displays a text string.
Definition: mythuitext.h:29
QStringList GetThemeSearchPath()
The base class on which all widgets and screens are based.
Definition: mythuitype.h:86
bool AddFont(const QString &text, MythFontProperties *fontProp)
void SetDependsMap(QMap< QString, QString > dependsMap)
virtual void CopyFrom(MythUIType *base)
Copy this widgets state from another.
virtual void Finalize(void)
Perform any post-xml parsing initialisation tasks.
void ConnectDependants(bool recurse=false)
void SetXMLName(const QString &name)
Definition: mythuitype.h:184
QMap< QString, QString > GetDependsMap() const
Definition: mythuitype.h:204
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
Definition: mythuitype.cpp:138
void DeleteChild(const QString &name)
Delete a named child of this UIType.
Definition: mythuitype.cpp:153
virtual bool ParseElement(const QString &filename, QDomElement &element, bool showWarnings)
Parse the xml definition of this widget setting the state of the object accordingly.
void SetXMLLocation(const QString &filename, int where)
Definition: mythuitype.h:180
Video widget, displays raw image data.
Definition: mythuivideo.h:15
Web browsing widget.
static MythUIType * GetGlobalObjectStore(void)
static void ParseChildren(const QString &filename, QDomElement &element, MythUIType *parent, bool showWarnings)
static bool LoadWindowFromXML(const QString &xmlfile, const QString &windowname, MythUIType *parent)
static bool doLoad(const QString &windowname, MythUIType *parent, const QString &filename, bool onlyLoadWindows, bool showWarnings)
static MythRect parseRect(const QString &text, bool normalize=true)
static MythPoint parsePoint(const QString &text, bool normalize=true)
static void ClearGlobalObjectStore(void)
static int parseAlignment(const QString &text)
static bool CopyWindowFromBase(const QString &windowname, MythScreenType *win)
static MythUIType * ParseUIType(const QString &filename, QDomElement &element, const QString &type, MythUIType *parent, MythScreenType *screen, bool showWarnings, QMap< QString, QString > &parentDependsMap)
static QSize parseSize(const QString &text, bool normalize=true)
static QBrush parseGradient(const QDomElement &element)
static bool LoadBaseTheme(void)
static QString getFirstText(QDomElement &element)
static bool WindowExists(const QString &xmlfile, const QString &windowname)
static bool parseBool(const QString &text)
static QString parseText(QDomElement &element)
static uint32_t * tmp
Definition: goom_core.cpp:28
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
Definition: mythlogging.h:39
MythMainWindow * GetMythMainWindow(void)
static int x1
Definition: mythsocket.cpp:50
static int x2
Definition: mythsocket.cpp:51
MythUIHelper * GetMythUI()
dictionary info
Definition: azlyrics.py:7
#define LOC
static QStringList loadedBaseFiles
static MythUIType * globalObjectStore
#define VERBOSE_XML(type, level, filename, element, msg)
Definition: xmlparsebase.h:15