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 if (
type ==
"imagetype")
484 else if (
type ==
"procedural")
486 else if (
type ==
"textarea")
488 else if (
type ==
"group")
490 else if (
type ==
"textedit")
492 else if (
type ==
"button")
494 else if (
type ==
"buttonlist2" ||
type ==
"buttonlist")
496 else if (
type ==
"buttontree")
498 else if (
type ==
"spinbox")
500 else if (
type ==
"checkbox")
502 else if (
type ==
"statetype")
504 else if (
type ==
"clock")
506 else if (
type ==
"progressbar")
508 else if (
type ==
"scrollbar") {
511 }
else if (
type ==
"webbrowser") {
514 }
else if (
type ==
"guidegrid") {
516 }
else if (
type ==
"shape") {
518 }
else if (
type ==
"editbar") {
520 }
else if (
type ==
"video") {
526 "Unknown widget type.");
533 "Failed to instantiate widget type.");
537 if (olduitype && parent)
539 if (
typeid(*olduitype) !=
typeid(*uitype))
542 QString(
"Duplicate name: '%1' in parent '%2'")
543 .arg(name, parent->objectName()));
555 if (
typeid(*base) !=
typeid(*uitype))
558 QString(
"Type of new widget '%1' doesn't match old '%2'")
559 .arg(name, inherits));
568 QString dependee = element.attribute(
"depends",
"");
569 if (!dependee.isEmpty())
570 parentDependsMap.insert(name, dependee);
579 for (QDomNode child = element.firstChild(); !child.isNull();
580 child = child.nextSibling())
582 QDomElement
info = child.toElement();
585 if (
info.tagName() ==
"fontdef")
593 QString name2 =
info.attribute(
"name");
599 else if (
info.tagName() ==
"imagetype" ||
600 info.tagName() ==
"procedural" ||
601 info.tagName() ==
"textarea" ||
602 info.tagName() ==
"group" ||
603 info.tagName() ==
"textedit" ||
604 info.tagName() ==
"button" ||
605 info.tagName() ==
"buttonlist" ||
606 info.tagName() ==
"buttonlist2" ||
607 info.tagName() ==
"buttontree" ||
608 info.tagName() ==
"spinbox" ||
609 info.tagName() ==
"checkbox" ||
610 info.tagName() ==
"statetype" ||
611 info.tagName() ==
"clock" ||
612 info.tagName() ==
"progressbar" ||
613 info.tagName() ==
"scrollbar" ||
614 info.tagName() ==
"webbrowser" ||
615 info.tagName() ==
"guidegrid" ||
616 info.tagName() ==
"shape" ||
617 info.tagName() ==
"editbar" ||
618 info.tagName() ==
"video")
621 uitype, screen, showWarnings, dependsMap);
637 const QString &windowname)
640 for (
const auto & dir : std::as_const(searchpath))
642 QString themefile = dir + xmlfile;
645 if (!f.open(QIODevice::ReadOnly))
649 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
654 if (!doc.setContent(&f,
false, &errorMsg, &errorLine, &errorColumn))
656 LOG(VB_GENERAL, LOG_ERR,
LOC +
657 QString(
"Location: '%1' @ %2 column: %3"
659 .arg(qPrintable(themefile)).arg(errorLine).arg(errorColumn)
660 .arg(qPrintable(errorMsg)));
665 auto parseResult = doc.setContent(&f);
668 LOG(VB_GENERAL, LOG_ERR,
LOC +
669 QString(
"Location: '%1' @ %2 column: %3"
671 .arg(qPrintable(themefile)).arg(parseResult.errorLine)
672 .arg(parseResult.errorColumn)
673 .arg(qPrintable(parseResult.errorMessage)));
680 QDomElement docElem = doc.documentElement();
681 QDomNode n = docElem.firstChild();
684 QDomElement e = n.toElement();
687 if (e.tagName() ==
"window")
689 QString name = e.attribute(
"name",
"");
690 if (name == windowname)
702 const QString &windowname,
705 bool onlyLoadWindows =
true;
706 bool showWarnings =
true;
709 for (
const auto & dir : std::as_const(searchpath))
711 QString themefile = dir + xmlfile;
712 LOG(VB_GUI, LOG_INFO,
LOC + QString(
"Loading window %1 from %2").arg(windowname, themefile));
713 if (
doLoad(windowname, parent, themefile,
714 onlyLoadWindows, showWarnings))
718 LOG(VB_FILE, LOG_ERR,
LOC +
"No theme file " + themefile);
721 LOG(VB_GENERAL, LOG_ERR,
LOC +
722 QString(
"Unable to load window '%1' from '%2'")
723 .arg(windowname, xmlfile));
731 bool onlyLoadWindows,
737 if (!f.open(QIODevice::ReadOnly))
740 #if QT_VERSION < QT_VERSION_CHECK(6,5,0)
745 if (!doc.setContent(&f,
false, &errorMsg, &errorLine, &errorColumn))
747 LOG(VB_GENERAL, LOG_ERR,
LOC +
748 QString(
"Location: '%1' @ %2 column: %3"
750 .arg(qPrintable(
filename)).arg(errorLine).arg(errorColumn)
751 .arg(qPrintable(errorMsg)));
756 auto parseResult = doc.setContent(&f);
759 LOG(VB_GENERAL, LOG_ERR,
LOC +
760 QString(
"Location: '%1' @ %2 column: %3"
762 .arg(qPrintable(
filename)).arg(parseResult.errorLine)
763 .arg(parseResult.errorColumn)
764 .arg(qPrintable(parseResult.errorMessage)));
772 QDomElement docElem = doc.documentElement();
773 QDomNode n = docElem.firstChild();
776 QDomElement e = n.toElement();
779 if (e.tagName() ==
"include")
783 if (!include.isEmpty())
787 if (onlyLoadWindows && e.tagName() ==
"window")
789 QString name = e.attribute(
"name",
"");
790 QString include = e.attribute(
"include",
"");
794 "Window needs a name");
798 if (!include.isEmpty())
801 if (name == windowname)
808 if (!onlyLoadWindows)
810 QString
type = e.tagName();
811 if (
type ==
"font" ||
type ==
"fontdef")
815 filename, e, parent, global, showWarnings);
819 QString name = e.attribute(
"name");
824 else if (
type ==
"imagetype" ||
825 type ==
"procedural" ||
826 type ==
"textarea" ||
828 type ==
"textedit" ||
830 type ==
"buttonlist" ||
831 type ==
"buttonlist2" ||
832 type ==
"buttontree" ||
834 type ==
"checkbox" ||
835 type ==
"statetype" ||
838 type ==
"progressbar" ||
839 type ==
"scrollbar" ||
840 type ==
"webbrowser" ||
841 type ==
"guidegrid" ||
849 QMap<QString, QString> dependsMap;
852 nullptr, showWarnings, dependsMap);
859 "Unknown widget type");
865 return !onlyLoadWindows;
871 bool loadOnlyWindows =
false;
872 bool showWarnings =
true;
875 for (
const auto & dir : std::as_const(searchpath))
877 QString themefile = dir +
"base.xml";
879 loadOnlyWindows, showWarnings))
881 LOG(VB_GUI, LOG_INFO,
LOC +
882 QString(
"Loaded base theme from '%1'").arg(themefile));
885 showWarnings =
false;
890 LOG(VB_GUI | VB_FILE, LOG_WARNING,
LOC +
891 QString(
"No theme file '%1'").arg(themefile));
900 LOG(VB_GUI, LOG_INFO,
LOC +
901 QString(
"Asked to load base file from '%1'").arg(baseTheme));
905 LOG(VB_GUI, LOG_INFO,
LOC +
906 QString(
"Base file already loaded '%1'").arg(baseTheme));
911 bool loadOnlyWindows =
false;
912 bool showWarnings =
true;
915 for (
const auto & dir : std::as_const(searchpath))
917 QString themefile = dir + baseTheme;
919 loadOnlyWindows, showWarnings))
921 LOG(VB_GUI, LOG_INFO,
LOC +
922 QString(
"Loaded base theme from '%1'").arg(themefile));
925 showWarnings =
false;
930 LOG(VB_GUI | VB_FILE, LOG_WARNING,
LOC +
931 QString(
"No theme file '%1'").arg(themefile));
947 LOG(VB_GENERAL, LOG_ERR,
LOC +
948 QString(
"Unable to load window '%1' from base") .arg(windowname));
955 LOG(VB_GENERAL, LOG_ERR,
LOC +
956 QString(
"UI Object '%1' is not a ScreenType") .arg(windowname));