1 #pragma once 2 3 #include <memory> 4 5 #include <QHash> 6 #include <QString> 7 #include <QDomNode> 8 #include <QDomElement> 9 #include <QScriptEngine> 10 #include <QDir> 11 #include <QScriptEngineDebugger> 12 #include <QtDebug> 13 #include <QRegExp> 14 15 #include "preferences/usersettings.h" 16 #include "skin/legacy/pixmapsource.h" 17 #include "util/color/color.h" 18 #include "widget/wsingletoncontainer.h" 19 #include "widget/wpixmapstore.h" 20 21 #define SKIN_WARNING(node, context) (context).logWarning(__FILE__, __LINE__, (node)) 22 23 // A class for managing the current context/environment when processing a 24 // skin. Used hierarchically by LegacySkinParser to create new contexts and 25 // evaluate skin XML nodes while loading the skin. 26 class SkinContext { 27 public: 28 SkinContext( 29 UserSettingsPointer pConfig, 30 const QString& xmlPath); 31 SkinContext( 32 const SkinContext* parent); 33 virtual ~SkinContext(); 34 35 // Not copiable 36 SkinContext(const SkinContext&) = delete; 37 SkinContext& operator=(const SkinContext&) = delete; 38 39 // Moveable 40 SkinContext(SkinContext&&) = default; 41 SkinContext& operator=(SkinContext&&) = default; 42 43 // Gets a path relative to the skin path. makeSkinPath(const QString & relativePath)44 QString makeSkinPath(const QString& relativePath) const { 45 if (relativePath.isEmpty() || relativePath.startsWith("/") 46 || relativePath.contains(":")) { 47 // This is already an absolute path start with the root folder "/" 48 // a windows drive letter e.g. "C:" or a qt search path prefix 49 return relativePath; 50 } 51 return QString("skin:").append(relativePath); 52 } 53 54 // Sets the base path used by getSkinPath. setSkinBasePath(const QString & skinBasePath)55 void setSkinBasePath(const QString& skinBasePath) { 56 QStringList skinPaths(skinBasePath); 57 QDir::setSearchPaths("skin", skinPaths); 58 m_skinBasePath = skinBasePath; 59 } 60 61 // Sets the base path used by getSkinPath. setSkinTemplatePath(const QString & skinTemplatePath)62 void setSkinTemplatePath(const QString& skinTemplatePath) { 63 QStringList skinPaths(m_skinBasePath); 64 skinPaths.append(skinTemplatePath); 65 QDir::setSearchPaths("skin", skinPaths); 66 } 67 68 // Variable lookup and modification methods. 69 QString variable(const QString& name) const; variables()70 const QHash<QString, QString>& variables() const { 71 return m_variables; 72 } 73 void setVariable(const QString& name, const QString& value); 74 void setXmlPath(const QString& xmlPath); 75 76 // Returns whether the node has a <SetVariable> node. 77 bool hasVariableUpdates(const QDomNode& node) const; 78 // Updates the SkinContext with all the <SetVariable> children of node. 79 void updateVariables(const QDomNode& node); 80 // Updates the SkinContext with 'element', a <SetVariable> node. 81 void updateVariable(const QDomElement& element); 82 selectNode(const QDomNode & node,const QString & nodeName)83 static inline QDomNode selectNode(const QDomNode& node, const QString& nodeName) { 84 QDomNode child = node.firstChild(); 85 while (!child.isNull()) { 86 if (child.nodeName() == nodeName) { 87 return child; 88 } 89 child = child.nextSibling(); 90 } 91 return QDomNode(); 92 } 93 selectElement(const QDomNode & node,const QString & nodeName)94 static inline QDomElement selectElement(const QDomNode& node, const QString& nodeName) { 95 QDomNode child = selectNode(node, nodeName); 96 return child.toElement(); 97 } 98 selectString(const QDomNode & node,const QString & nodeName)99 inline QString selectString(const QDomNode& node, const QString& nodeName) const { 100 QDomElement child = selectElement(node, nodeName); 101 return nodeToString(child); 102 } 103 104 inline float selectFloat(const QDomNode& node, const QString& nodeName, float defaultValue = 0.0) const { 105 bool ok = false; 106 float conv = nodeToString(selectElement(node, nodeName)).toFloat(&ok); 107 return ok ? conv : defaultValue; 108 } 109 110 inline double selectDouble(const QDomNode& node, const QString& nodeName, double defaultValue = 0.0) const { 111 bool ok = false; 112 double conv = nodeToString(selectElement(node, nodeName)).toDouble(&ok); 113 return ok ? conv : defaultValue; 114 } 115 116 inline int selectInt(const QDomNode& node, const QString& nodeName, 117 bool* pOk = nullptr) const { 118 bool ok = false; 119 int conv = nodeToString(selectElement(node, nodeName)).toInt(&ok); 120 if (pOk != nullptr) { 121 *pOk = ok; 122 } 123 return ok ? conv : 0; 124 } 125 selectColor(const QDomNode & node,const QString & nodeName)126 inline QColor selectColor(const QDomNode& node, const QString& nodeName) const { 127 QString sColorString = nodeToString(selectElement(node, nodeName)); 128 return QColor(sColorString); 129 } 130 selectBool(const QDomNode & node,const QString & nodeName,bool defaultValue)131 inline bool selectBool(const QDomNode& node, const QString& nodeName, 132 bool defaultValue) const { 133 QDomNode child = selectNode(node, nodeName); 134 if (!child.isNull()) { 135 QString stringValue = nodeToString(child); 136 return stringValue.contains("true", Qt::CaseInsensitive); 137 } 138 return defaultValue; 139 } 140 hasNodeSelectElement(const QDomNode & node,const QString & nodeName,QDomElement * value)141 inline bool hasNodeSelectElement(const QDomNode& node, const QString& nodeName, 142 QDomElement* value) const { 143 QDomElement child = selectElement(node, nodeName); 144 if (!child.isNull()) { 145 *value = child; 146 return true; 147 } 148 return false; 149 } 150 hasNodeSelectString(const QDomNode & node,const QString & nodeName,QString * value)151 inline bool hasNodeSelectString(const QDomNode& node, const QString& nodeName, 152 QString *value) const { 153 QDomNode child = selectNode(node, nodeName); 154 if (!child.isNull()) { 155 *value = nodeToString(child); 156 return true; 157 } 158 return false; 159 } 160 hasNodeSelectBool(const QDomNode & node,const QString & nodeName,bool * value)161 inline bool hasNodeSelectBool(const QDomNode& node, const QString& nodeName, 162 bool* value) const { 163 QDomNode child = selectNode(node, nodeName); 164 if (!child.isNull()) { 165 QString stringValue = nodeToString(child); 166 *value = stringValue.contains("true", Qt::CaseInsensitive); 167 return true; 168 } 169 return false; 170 } 171 hasNodeSelectInt(const QDomNode & node,const QString & nodeName,int * value)172 inline bool hasNodeSelectInt(const QDomNode& node, const QString& nodeName, 173 int* value) const { 174 QDomNode child = selectNode(node, nodeName); 175 if (!child.isNull()) { 176 bool ok = false; 177 int result = nodeToString(child).toInt(&ok); 178 if (ok) { 179 *value = result; 180 return true; 181 } 182 } 183 return false; 184 } 185 hasNodeSelectDouble(const QDomNode & node,const QString & nodeName,double * value)186 inline bool hasNodeSelectDouble(const QDomNode& node, const QString& nodeName, 187 double* value) const { 188 QDomNode child = selectNode(node, nodeName); 189 if (!child.isNull()) { 190 bool ok = false; 191 double result = nodeToString(child).toDouble(&ok); 192 if (ok) { 193 *value = result; 194 return true; 195 } 196 } 197 return false; 198 } 199 selectAttributeBool(const QDomElement & element,const QString & attributeName,bool defaultValue)200 inline bool selectAttributeBool(const QDomElement& element, 201 const QString& attributeName, 202 bool defaultValue) const { 203 QString stringValue; 204 if (hasAttributeSelectString(element, attributeName, &stringValue)) { 205 return stringValue.contains("true", Qt::CaseInsensitive); 206 } 207 return defaultValue; 208 } 209 hasAttributeSelectString(const QDomElement & element,const QString & attributeName,QString * result)210 inline bool hasAttributeSelectString(const QDomElement& element, 211 const QString& attributeName, 212 QString* result) const { 213 *result = element.attribute(attributeName); 214 return !result->isNull(); 215 } 216 217 QString nodeToString(const QDomNode& node) const; 218 PixmapSource getPixmapSource(const QDomNode& pixmapNode) const; 219 PixmapSource getPixmapSource(const QString& filename) const; 220 selectScaleMode(const QDomElement & element,Paintable::DrawMode defaultDrawMode)221 inline Paintable::DrawMode selectScaleMode( 222 const QDomElement& element, 223 Paintable::DrawMode defaultDrawMode) const { 224 QString drawModeStr; 225 if (hasAttributeSelectString(element, "scalemode", &drawModeStr)) { 226 return Paintable::DrawModeFromString(drawModeStr); 227 } 228 return defaultDrawMode; 229 } 230 231 QScriptValue evaluateScript(const QString& expression, 232 const QString& filename=QString(), 233 int lineNumber=1) const; 234 QScriptValue importScriptExtension(const QString& extensionName); hasUncaughtScriptException()235 bool hasUncaughtScriptException() const { 236 return m_pSharedState->scriptEngine.hasUncaughtException(); 237 } 238 void enableDebugger(bool state) const; 239 240 QDebug logWarning(const char* file, const int line, const QDomNode& node) const; 241 defineSingleton(const QString & objectName,QWidget * widget)242 void defineSingleton(const QString& objectName, QWidget* widget) { 243 m_pSharedState->singletons.insertSingleton(objectName, widget); 244 } 245 getSingletonWidget(const QString & objectName)246 QWidget* getSingletonWidget(const QString& objectName) const { 247 return m_pSharedState->singletons.getSingletonWidget(objectName); 248 } 249 getHookRegex()250 const QRegExp& getHookRegex() const { 251 return m_hookRx; 252 } 253 254 int scaleToWidgetSize(QString& size) const; 255 getScaleFactor()256 double getScaleFactor() const { 257 return m_scaleFactor; 258 } 259 getConfig()260 UserSettingsPointer getConfig() const { 261 return m_pConfig; 262 } 263 getSkinBasePath()264 QString getSkinBasePath() const { 265 return m_skinBasePath; 266 } 267 268 private: 269 PixmapSource getPixmapSourceInner(const QString& filename) const; 270 271 QDomElement loadSvg(const QString& filename) const; 272 273 // If our parent global isValid() then we were constructed with a 274 // parent. Otherwise we are a root SkinContext. isRoot()275 bool isRoot() const { return !m_parentGlobal.isValid(); } 276 277 QString variableNodeToText(const QDomElement& element) const; 278 279 UserSettingsPointer m_pConfig; 280 281 QString m_xmlPath; 282 QString m_skinBasePath; 283 284 struct SharedState final { 285 SharedState() = default; 286 SharedState(const SharedState&) = delete; 287 SharedState(SharedState&&) = delete; 288 289 QScriptEngine scriptEngine; 290 QScriptEngineDebugger scriptDebugger; 291 QHash<QString, QDomElement> svgCache; 292 // The SingletonContainer map is passed to child SkinContexts, so that all 293 // templates in the tree can share a single map. 294 SingletonMap singletons; 295 }; 296 // Use std::shared_ptr instead of QSharedPointer to guarantee 297 // correct move semantics! 298 std::shared_ptr<SharedState> m_pSharedState; 299 300 QHash<QString, QString> m_variables; 301 QScriptValue m_parentGlobal; 302 QRegExp m_hookRx; 303 304 double m_scaleFactor; 305 }; 306