10#include <QDomDocument>
13#include <QLinearGradient>
14#include <QRadialGradient>
17#include "libmythbase/mythconfig.h"
50#define LOC QString("XMLParseBase: ")
54 for (QDomNode dname = element.firstChild(); !dname.isNull();
55 dname = dname.nextSibling())
57 QDomText
t = dname.toText();
66 QString s = text.toLower();
67 return (s ==
"yes" || s ==
"true" || (s.toInt() != 0));
78 QStringList values = text.split(
',', Qt::SkipEmptyParts);
79 if (values.size() == 2)
99 QStringList
tmp = text.split(
",");
104 x =
tmp[0].toInt(&x_ok);
105 y =
tmp[1].toInt(&y_ok);
110 if (x == -1 || y == -1)
118 retval = QSize(x, y);
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],
155 int alignment = Qt::AlignLeft | Qt::AlignTop;
157 QStringList values = text.split(
',');
159 QStringList::Iterator it;
160 for ( it = values.begin(); it != values.end(); ++it )
164 align = align.trimmed();
165 align = align.toLower();
167 if (align ==
"center" || align ==
"allcenter")
169 alignment &= ~(Qt::AlignHorizontal_Mask | Qt::AlignVertical_Mask);
170 alignment |= Qt::AlignCenter;
173 if (align ==
"justify")
175 alignment &= ~Qt::AlignHorizontal_Mask;
176 alignment |= Qt::AlignJustify;
178 else if (align ==
"left")
180 alignment &= ~Qt::AlignHorizontal_Mask;
181 alignment |= Qt::AlignLeft;
183 else if (align ==
"hcenter")
185 alignment &= ~Qt::AlignHorizontal_Mask;
186 alignment |= Qt::AlignHCenter;
188 else if (align ==
"right")
190 alignment &= ~Qt::AlignHorizontal_Mask;
191 alignment |= Qt::AlignRight;
193 else if (align ==
"top")
195 alignment &= ~Qt::AlignVertical_Mask;
196 alignment |= Qt::AlignTop;
198 else if (align ==
"vcenter")
200 alignment &= ~Qt::AlignVertical_Mask;
201 alignment |= Qt::AlignVCenter;
203 else if (align ==
"bottom")
205 alignment &= ~Qt::AlignVertical_Mask;
206 alignment |= Qt::AlignBottom;
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");
226 QGradientStops stops;
228 if (!gradientStart.isEmpty())
230 auto startColor = QColor(gradientStart);
231 startColor.setAlpha(gradientAlpha);
232 QGradientStop stop(0.0, startColor);
236 for (QDomNode child = element.firstChild(); !child.isNull();
237 child = child.nextSibling())
239 QDomElement childElem = child.toElement();
240 if (childElem.tagName() ==
"stop")
242 double position = childElem.attribute(
"position",
"0").toDouble();
243 QString color = childElem.attribute(
"color",
"");
244 int alpha = childElem.attribute(
"alpha",
"-1").toInt();
246 alpha = gradientAlpha;
247 auto stopColor = QColor(color);
248 stopColor.setAlpha(alpha);
249 QGradientStop stop((position / 100), stopColor);
254 if (!gradientEnd.isEmpty())
256 auto endColor = QColor(gradientEnd);
257 endColor.setAlpha(gradientAlpha);
258 QGradientStop stop(1.0, endColor);
262 if (direction ==
"radial")
264 QRadialGradient gradient;
265 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
269 gradient.setCenter(
x1,y1);
270 gradient.setFocalPoint(
x1,y1);
271 gradient.setRadius(radius);
272 gradient.setStops(stops);
273 brush = QBrush(gradient);
277 QLinearGradient gradient;
278 gradient.setCoordinateMode(QGradient::ObjectBoundingMode);
283 if (direction ==
"vertical")
290 else if (direction ==
"diagonal")
305 gradient.setStart(
x1, y1);
306 gradient.setFinalStop(
x2, y2);
307 gradient.setStops(stops);
308 brush = QBrush(gradient);
320 text.replace(
"\\n", QString(
"<newline>"));
324 QStringList lines = text.split(
'\n');
325 QStringList::iterator lineIt;
327 for (lineIt = lines.begin(); lineIt != lines.end(); ++lineIt)
329 (*lineIt) = (*lineIt).trimmed();
332 text = lines.join(
" ");
334 text.replace(QString(
"<newline>"), QString(
"\n"));
360 QDomElement &element,
366 LOG(VB_GENERAL, LOG_ERR,
LOC +
"Parent is NULL");
370 QMap<QString, QString> dependsMap;
371 for (QDomNode child = element.firstChild(); !child.isNull();
372 child = child.nextSibling())
374 QDomElement
info = child.toElement();
378 if (
type ==
"fontdef")
386 QString name =
info.attribute(
"name");
392 else if (
type ==
"imagetype" ||
393 type ==
"procedural" ||
394 type ==
"textarea" ||
396 type ==
"textedit" ||
398 type ==
"buttonlist" ||
399 type ==
"buttonlist2" ||
400 type ==
"buttontree" ||
402 type ==
"checkbox" ||
403 type ==
"statetype" ||
405 type ==
"progressbar" ||
406 type ==
"scrollbar" ||
407 type ==
"webbrowser" ||
408 type ==
"guidegrid" ||
429 QDomElement &element,
const QString &
type,
433 QMap<QString, QString> &parentDependsMap)
435 QString name = element.attribute(
"name",
"");
439 "This element requires a name");
446 if (parent && parent->
GetChild(name))
460 QString inherits = element.attribute(
"from",
"");
461 if (!inherits.isEmpty())
476 QString(
"Couldn't find object '%1' to inherit '%2' from")
477 .arg(inherits, name));
482 QString shadow = element.attribute(
"shadow",
"");
484 if (
type ==
"imagetype")
486 else if (
type ==
"procedural")
488 else if (
type ==
"textarea")
490 else if (
type ==
"group")
492 else if (
type ==
"textedit")
494 else if (
type ==
"button")
496 else if (
type ==
"buttonlist2" ||
type ==
"buttonlist")
498 else if (
type ==
"buttontree")
500 else if (
type ==
"spinbox")
502 else if (
type ==
"checkbox")
504 else if (
type ==
"statetype")
506 else if (
type ==
"clock")
508 else if (
type ==
"progressbar")
510 else if (
type ==
"scrollbar") {
512#if CONFIG_QTWEBENGINE
513 }
else if (
type ==
"webbrowser") {
516 }
else if (
type ==
"guidegrid") {
518 }
else if (
type ==
"shape") {
520 }
else if (
type ==
"editbar") {
522 }
else if (
type ==
"video") {
528 "Unknown widget type.");
535 "Failed to instantiate widget type.");
539 if (olduitype && parent)
541 if (
typeid(*olduitype) !=
typeid(*uitype))
544 QString(
"Duplicate name: '%1' in parent '%2'")
545 .arg(name, parent->objectName()));
557 if (
typeid(*base) !=
typeid(*uitype))
560 QString(
"Type of new widget '%1' doesn't match old '%2'")
561 .arg(name, inherits));
570 QString dependee = element.attribute(
"depends",
"");
571 if (!dependee.isEmpty())
572 parentDependsMap.insert(name, dependee);
581 for (QDomNode child = element.firstChild(); !child.isNull();
582 child = child.nextSibling())
584 QDomElement
info = child.toElement();
587 if (
info.tagName() ==
"fontdef")
595 QString name2 =
info.attribute(
"name");
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")
623 uitype, screen, showWarnings, dependsMap);
639 const QString &windowname)
642 for (
const auto & dir : std::as_const(searchpath))
644 QString themefile = dir + xmlfile;
647 if (!f.open(QIODevice::ReadOnly))
651#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
656 if (!doc.setContent(&f,
false, &errorMsg, &errorLine, &errorColumn))
658 LOG(VB_GENERAL, LOG_ERR,
LOC +
659 QString(
"Location: '%1' @ %2 column: %3"
661 .arg(qPrintable(themefile)).arg(errorLine).arg(errorColumn)
662 .arg(qPrintable(errorMsg)));
667 auto parseResult = doc.setContent(&f);
670 LOG(VB_GENERAL, LOG_ERR,
LOC +
671 QString(
"Location: '%1' @ %2 column: %3"
673 .arg(qPrintable(themefile)).arg(parseResult.errorLine)
674 .arg(parseResult.errorColumn)
675 .arg(qPrintable(parseResult.errorMessage)));
682 QDomElement docElem = doc.documentElement();
683 QDomNode n = docElem.firstChild();
686 QDomElement e = n.toElement();
689 if (e.tagName() ==
"window")
691 QString name = e.attribute(
"name",
"");
692 if (name == windowname)
704 const QString &windowname,
707 bool onlyLoadWindows =
true;
708 bool showWarnings =
true;
711 for (
const auto & dir : std::as_const(searchpath))
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))
720 LOG(VB_FILE, LOG_ERR,
LOC +
"No theme file " + themefile);
723 LOG(VB_GENERAL, LOG_ERR,
LOC +
724 QString(
"Unable to load window '%1' from '%2'")
725 .arg(windowname, xmlfile));
733 bool onlyLoadWindows,
739 if (!f.open(QIODevice::ReadOnly))
742#if QT_VERSION < QT_VERSION_CHECK(6,5,0)
747 if (!doc.setContent(&f,
false, &errorMsg, &errorLine, &errorColumn))
749 LOG(VB_GENERAL, LOG_ERR,
LOC +
750 QString(
"Location: '%1' @ %2 column: %3"
752 .arg(qPrintable(
filename)).arg(errorLine).arg(errorColumn)
753 .arg(qPrintable(errorMsg)));
758 auto parseResult = doc.setContent(&f);
761 LOG(VB_GENERAL, LOG_ERR,
LOC +
762 QString(
"Location: '%1' @ %2 column: %3"
764 .arg(qPrintable(
filename)).arg(parseResult.errorLine)
765 .arg(parseResult.errorColumn)
766 .arg(qPrintable(parseResult.errorMessage)));
774 QDomElement docElem = doc.documentElement();
775 QDomNode n = docElem.firstChild();
778 QDomElement e = n.toElement();
781 if (e.tagName() ==
"include")
785 if (!include.isEmpty())
789 if (onlyLoadWindows && e.tagName() ==
"window")
791 QString name = e.attribute(
"name",
"");
792 QString include = e.attribute(
"include",
"");
796 "Window needs a name");
800 if (!include.isEmpty())
803 if (name == windowname)
810 if (!onlyLoadWindows)
812 QString
type = e.tagName();
813 if (
type ==
"font" ||
type ==
"fontdef")
817 filename, e, parent, global, showWarnings);
821 QString name = e.attribute(
"name");
826 else if (
type ==
"imagetype" ||
827 type ==
"procedural" ||
828 type ==
"textarea" ||
830 type ==
"textedit" ||
832 type ==
"buttonlist" ||
833 type ==
"buttonlist2" ||
834 type ==
"buttontree" ||
836 type ==
"checkbox" ||
837 type ==
"statetype" ||
840 type ==
"progressbar" ||
841 type ==
"scrollbar" ||
842 type ==
"webbrowser" ||
843 type ==
"guidegrid" ||
851 QMap<QString, QString> dependsMap;
854 nullptr, showWarnings, dependsMap);
861 "Unknown widget type");
867 return !onlyLoadWindows;
873 bool loadOnlyWindows =
false;
874 bool showWarnings =
true;
877 for (
const auto & dir : std::as_const(searchpath))
879 QString themefile = dir +
"base.xml";
881 loadOnlyWindows, showWarnings))
883 LOG(VB_GUI, LOG_INFO,
LOC +
884 QString(
"Loaded base theme from '%1'").arg(themefile));
887 showWarnings =
false;
892 LOG(VB_GUI | VB_FILE, LOG_WARNING,
LOC +
893 QString(
"No theme file '%1'").arg(themefile));
902 LOG(VB_GUI, LOG_INFO,
LOC +
903 QString(
"Asked to load base file from '%1'").arg(baseTheme));
907 LOG(VB_GUI, LOG_INFO,
LOC +
908 QString(
"Base file already loaded '%1'").arg(baseTheme));
913 bool loadOnlyWindows =
false;
914 bool showWarnings =
true;
917 for (
const auto & dir : std::as_const(searchpath))
919 QString themefile = dir + baseTheme;
921 loadOnlyWindows, showWarnings))
923 LOG(VB_GUI, LOG_INFO,
LOC +
924 QString(
"Loaded base theme from '%1'").arg(themefile));
927 showWarnings =
false;
932 LOG(VB_GUI | VB_FILE, LOG_WARNING,
LOC +
933 QString(
"No theme file '%1'").arg(themefile));
949 LOG(VB_GENERAL, LOG_ERR,
LOC +
950 QString(
"Unable to load window '%1' from base") .arg(windowname));
957 LOG(VB_GENERAL, LOG_ERR,
LOC +
958 QString(
"UI Object '%1' is not a ScreenType") .arg(windowname));
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...
Wrapper around QRect allowing us to handle percentage and other relative values for areas in mythui.
Screen in which all other widgets are contained and rendered.
void CopyFrom(MythUIType *base) override
Copy this widgets state from another.
A checkbox widget supporting three check states - on,off,half and two conditions - selected and unsel...
A simple text clock widget.
A narrow purpose widget used to represent cut positions and regions when editing a video.
Create a group of widgets.
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.
QSize NormSize(QSize Size) const
A widget for rendering primitive shapes and lines.
A widget for offering a range of numerical values where only the the bounding values and interval are...
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.
QStringList GetThemeSearchPath()
The base class on which all widgets and screens are based.
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)
QMap< QString, QString > GetDependsMap() const
MythUIType * GetChild(const QString &name) const
Get a named child of this UIType.
void DeleteChild(const QString &name)
Delete a named child of this UIType.
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)
Video widget, displays raw image data.
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)
#define LOG(_MASK_, _LEVEL_, _QSTRING_)
MythMainWindow * GetMythMainWindow(void)
MythUIHelper * GetMythUI()
static QStringList loadedBaseFiles
static MythUIType * globalObjectStore
#define VERBOSE_XML(type, level, filename, element, msg)