2 * Copyright (C) 2005-2013 Team XBMC
5 * This Program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2, or (at your option)
10 * This Program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with XBMC; see the file COPYING. If not, see
17 * <http://www.gnu.org/licenses/>.
21 #include "GUIIncludes.h"
22 #include "addons/Skin.h"
23 #include "GUIInfoManager.h"
24 #include "utils/log.h"
25 #include "utils/XBMCTinyXML.h"
26 #include "utils/StringUtils.h"
27 #include "interfaces/info/SkinVariable.h"
31 CGUIIncludes::CGUIIncludes()
33 m_constantAttributes.insert("x");
34 m_constantAttributes.insert("y");
35 m_constantAttributes.insert("width");
36 m_constantAttributes.insert("height");
37 m_constantAttributes.insert("center");
38 m_constantAttributes.insert("max");
39 m_constantAttributes.insert("min");
40 m_constantAttributes.insert("w");
41 m_constantAttributes.insert("h");
42 m_constantAttributes.insert("time");
43 m_constantAttributes.insert("acceleration");
44 m_constantAttributes.insert("delay");
45 m_constantAttributes.insert("start");
46 m_constantAttributes.insert("end");
47 m_constantAttributes.insert("center");
48 m_constantAttributes.insert("border");
49 m_constantAttributes.insert("repeat");
51 m_constantNodes.insert("posx");
52 m_constantNodes.insert("posy");
53 m_constantNodes.insert("left");
54 m_constantNodes.insert("centerleft");
55 m_constantNodes.insert("right");
56 m_constantNodes.insert("centerright");
57 m_constantNodes.insert("top");
58 m_constantNodes.insert("centertop");
59 m_constantNodes.insert("bottom");
60 m_constantNodes.insert("centerbottom");
61 m_constantNodes.insert("width");
62 m_constantNodes.insert("height");
63 m_constantNodes.insert("offsetx");
64 m_constantNodes.insert("offsety");
65 m_constantNodes.insert("textoffsetx");
66 m_constantNodes.insert("textoffsety");
67 m_constantNodes.insert("textwidth");
68 m_constantNodes.insert("spinposx");
69 m_constantNodes.insert("spinposy");
70 m_constantNodes.insert("spinwidth");
71 m_constantNodes.insert("spinheight");
72 m_constantNodes.insert("radioposx");
73 m_constantNodes.insert("radioposy");
74 m_constantNodes.insert("radiowidth");
75 m_constantNodes.insert("radioheight");
76 m_constantNodes.insert("markwidth");
77 m_constantNodes.insert("markheight");
78 m_constantNodes.insert("sliderwidth");
79 m_constantNodes.insert("sliderheight");
80 m_constantNodes.insert("itemgap");
81 m_constantNodes.insert("bordersize");
82 m_constantNodes.insert("timeperimage");
83 m_constantNodes.insert("fadetime");
84 m_constantNodes.insert("pauseatend");
87 CGUIIncludes::~CGUIIncludes()
91 void CGUIIncludes::ClearIncludes()
96 m_skinvariables.clear();
100 bool CGUIIncludes::LoadIncludes(const CStdString &includeFile)
102 // check to see if we already have this loaded
103 if (HasIncludeFile(includeFile))
107 if (!doc.LoadFile(includeFile))
109 CLog::Log(LOGINFO, "Error loading includes.xml file (%s): %s (row=%i, col=%i)", includeFile.c_str(), doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol());
112 // success, load the tags
113 if (LoadIncludesFromXML(doc.RootElement()))
115 m_files.push_back(includeFile);
121 bool CGUIIncludes::LoadIncludesFromXML(const TiXmlElement *root)
123 if (!root || strcmpi(root->Value(), "includes"))
125 CLog::Log(LOGERROR, "Skin includes must start with the <includes> tag");
128 const TiXmlElement* node = root->FirstChildElement("include");
131 if (node->Attribute("name") && node->FirstChild())
133 CStdString tagName = node->Attribute("name");
134 m_includes.insert(pair<CStdString, TiXmlElement>(tagName, *node));
136 else if (node->Attribute("file"))
137 { // load this file in as well
138 LoadIncludes(g_SkinInfo->GetSkinPath(node->Attribute("file")));
140 node = node->NextSiblingElement("include");
143 node = root->FirstChildElement("default");
146 if (node->Attribute("type") && node->FirstChild())
148 CStdString tagName = node->Attribute("type");
149 m_defaults.insert(pair<CStdString, TiXmlElement>(tagName, *node));
151 node = node->NextSiblingElement("default");
153 // and finally constants
154 node = root->FirstChildElement("constant");
157 if (node->Attribute("name") && node->FirstChild())
159 CStdString tagName = node->Attribute("name");
160 m_constants.insert(make_pair(tagName, node->FirstChild()->ValueStr()));
162 node = node->NextSiblingElement("constant");
165 node = root->FirstChildElement("variable");
168 if (node->Attribute("name") && node->FirstChild())
170 CStdString tagName = node->Attribute("name");
171 m_skinvariables.insert(make_pair(tagName, *node));
173 node = node->NextSiblingElement("variable");
179 bool CGUIIncludes::HasIncludeFile(const CStdString &file) const
181 for (iFiles it = m_files.begin(); it != m_files.end(); ++it)
182 if (*it == file) return true;
186 void CGUIIncludes::ResolveIncludes(TiXmlElement *node, std::map<INFO::InfoPtr, bool>* xmlIncludeConditions /* = NULL */)
190 ResolveIncludesForNode(node, xmlIncludeConditions);
192 TiXmlElement *child = node->FirstChildElement();
195 ResolveIncludes(child, xmlIncludeConditions);
196 child = child->NextSiblingElement();
200 void CGUIIncludes::ResolveIncludesForNode(TiXmlElement *node, std::map<INFO::InfoPtr, bool>* xmlIncludeConditions /* = NULL */)
202 // we have a node, find any <include file="fileName">tagName</include> tags and replace
203 // recursively with their real includes
206 // First add the defaults if this is for a control
208 if (node->ValueStr() == "control")
210 type = node->Attribute("type");
211 map<CStdString, TiXmlElement>::const_iterator it = m_defaults.find(type);
212 if (it != m_defaults.end())
214 // we don't insert <left> et. al. if <posx> or <posy> is specified
215 bool hasPosX(node->FirstChild("posx") != NULL);
216 bool hasPosY(node->FirstChild("posy") != NULL);
218 const TiXmlElement &element = (*it).second;
219 const TiXmlElement *tag = element.FirstChildElement();
222 std::string value = tag->ValueStr();
224 if (hasPosX && (value == "left" || value == "right" || value == "centerleft" || value == "centerright"))
226 if (hasPosY && (value == "top" || value == "bottom" || value == "centertop" || value == "centerbottom"))
228 // we insert at the end of block
230 node->InsertEndChild(*tag);
231 tag = tag->NextSiblingElement();
236 TiXmlElement *include = node->FirstChildElement("include");
237 while (include && include->FirstChild())
239 // have an include tag - grab it's tag name and replace it with the real tag contents
240 const char *file = include->Attribute("file");
242 { // we need to load this include from the alternative file
243 LoadIncludes(g_SkinInfo->GetSkinPath(file));
245 const char *condition = include->Attribute("condition");
247 { // check this condition
248 INFO::InfoPtr conditionID = g_infoManager.Register(condition);
249 bool value = conditionID->Get();
251 if (xmlIncludeConditions)
252 (*xmlIncludeConditions)[conditionID] = value;
256 include = include->NextSiblingElement("include");
260 CStdString tagName = include->FirstChild()->Value();
261 map<CStdString, TiXmlElement>::const_iterator it = m_includes.find(tagName);
262 if (it != m_includes.end())
263 { // found the tag(s) to include - let's replace it
264 const TiXmlElement &element = (*it).second;
265 const TiXmlElement *tag = element.FirstChildElement();
268 // we insert before the <include> element to keep the correct
269 // order (we render in the order given in the xml file)
270 node->InsertBeforeChild(include, *tag);
271 tag = tag->NextSiblingElement();
273 // remove the <include>tagName</include> element
274 node->RemoveChild(include);
275 include = node->FirstChildElement("include");
279 CLog::Log(LOGWARNING, "Skin has invalid include: %s", tagName.c_str());
280 include = include->NextSiblingElement("include");
284 // run through this element's attributes, resolving any constants
285 TiXmlAttribute *attribute = node->FirstAttribute();
287 { // check the attribute against our set
288 if (m_constantAttributes.count(attribute->Name()))
289 attribute->SetValue(ResolveConstant(attribute->ValueStr()));
290 attribute = attribute->Next();
293 if (node->FirstChild() && node->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT && m_constantNodes.count(node->ValueStr()))
294 node->FirstChild()->SetValue(ResolveConstant(node->FirstChild()->ValueStr()));
297 CStdString CGUIIncludes::ResolveConstant(const CStdString &constant) const
299 CStdStringArray values;
300 StringUtils::SplitString(constant, ",", values);
301 for (unsigned int i = 0; i < values.size(); ++i)
303 map<CStdString, CStdString>::const_iterator it = m_constants.find(values[i]);
304 if (it != m_constants.end())
305 values[i] = it->second;
308 StringUtils::JoinString(values, ",", value);
312 const INFO::CSkinVariableString* CGUIIncludes::CreateSkinVariable(const CStdString& name, int context)
314 map<CStdString, TiXmlElement>::const_iterator it = m_skinvariables.find(name);
315 if (it != m_skinvariables.end())
316 return INFO::CSkinVariable::CreateFromXML(it->second, context);