Merge pull request #3125 from janbar/noepg_timeline_crash
[vuplus_xbmc] / xbmc / guilib / GUIIncludes.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://xbmc.org
4  *
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)
8  *  any later version.
9  *
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.
14  *
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/>.
18  *
19  */
20
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"
28
29 using namespace std;
30
31 CGUIIncludes::CGUIIncludes()
32 {
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   
50   m_constantNodes.insert("posx");
51   m_constantNodes.insert("posy");
52   m_constantNodes.insert("width");
53   m_constantNodes.insert("height");
54   m_constantNodes.insert("offsetx");
55   m_constantNodes.insert("offsety");
56   m_constantNodes.insert("textoffsetx");
57   m_constantNodes.insert("textoffsety");  
58   m_constantNodes.insert("textwidth");
59   m_constantNodes.insert("spinposx");
60   m_constantNodes.insert("spinposy");
61   m_constantNodes.insert("spinwidth");
62   m_constantNodes.insert("spinheight");
63   m_constantNodes.insert("radioposx");
64   m_constantNodes.insert("radioposy");
65   m_constantNodes.insert("radiowidth");
66   m_constantNodes.insert("radioheight");
67   m_constantNodes.insert("markwidth");
68   m_constantNodes.insert("markheight");
69   m_constantNodes.insert("sliderwidth");
70   m_constantNodes.insert("sliderheight");
71   m_constantNodes.insert("itemgap");
72   m_constantNodes.insert("bordersize");
73   m_constantNodes.insert("timeperimage");
74   m_constantNodes.insert("fadetime");
75   m_constantNodes.insert("pauseatend");
76 }
77
78 CGUIIncludes::~CGUIIncludes()
79 {
80 }
81
82 void CGUIIncludes::ClearIncludes()
83 {
84   m_includes.clear();
85   m_defaults.clear();
86   m_constants.clear();
87   m_skinvariables.clear();
88   m_files.clear();
89 }
90
91 bool CGUIIncludes::LoadIncludes(const CStdString &includeFile)
92 {
93   // check to see if we already have this loaded
94   if (HasIncludeFile(includeFile))
95     return true;
96
97   CXBMCTinyXML doc;
98   if (!doc.LoadFile(includeFile))
99   {
100     CLog::Log(LOGINFO, "Error loading includes.xml file (%s): %s (row=%i, col=%i)", includeFile.c_str(), doc.ErrorDesc(), doc.ErrorRow(), doc.ErrorCol());
101     return false;
102   }
103   // success, load the tags
104   if (LoadIncludesFromXML(doc.RootElement()))
105   {
106     m_files.push_back(includeFile);
107     return true;
108   }
109   return false;
110 }
111
112 bool CGUIIncludes::LoadIncludesFromXML(const TiXmlElement *root)
113 {
114   if (!root || strcmpi(root->Value(), "includes"))
115   {
116     CLog::Log(LOGERROR, "Skin includes must start with the <includes> tag");
117     return false;
118   }
119   const TiXmlElement* node = root->FirstChildElement("include");
120   while (node)
121   {
122     if (node->Attribute("name") && node->FirstChild())
123     {
124       CStdString tagName = node->Attribute("name");
125       m_includes.insert(pair<CStdString, TiXmlElement>(tagName, *node));
126     }
127     else if (node->Attribute("file"))
128     { // load this file in as well
129       LoadIncludes(g_SkinInfo->GetSkinPath(node->Attribute("file")));
130     }
131     node = node->NextSiblingElement("include");
132   }
133   // now defaults
134   node = root->FirstChildElement("default");
135   while (node)
136   {
137     if (node->Attribute("type") && node->FirstChild())
138     {
139       CStdString tagName = node->Attribute("type");
140       m_defaults.insert(pair<CStdString, TiXmlElement>(tagName, *node));
141     }
142     node = node->NextSiblingElement("default");
143   }
144   // and finally constants
145   node = root->FirstChildElement("constant");
146   while (node)
147   {
148     if (node->Attribute("name") && node->FirstChild())
149     {
150       CStdString tagName = node->Attribute("name");
151       m_constants.insert(make_pair(tagName, node->FirstChild()->ValueStr()));
152     }
153     node = node->NextSiblingElement("constant");
154   }
155
156   node = root->FirstChildElement("variable");
157   while (node)
158   {
159     if (node->Attribute("name") && node->FirstChild())
160     {
161       CStdString tagName = node->Attribute("name");
162       m_skinvariables.insert(make_pair(tagName, *node));
163     }
164     node = node->NextSiblingElement("variable");
165   }
166
167   return true;
168 }
169
170 bool CGUIIncludes::HasIncludeFile(const CStdString &file) const
171 {
172   for (iFiles it = m_files.begin(); it != m_files.end(); ++it)
173     if (*it == file) return true;
174   return false;
175 }
176
177 void CGUIIncludes::ResolveIncludes(TiXmlElement *node, std::map<int, bool>* xmlIncludeConditions /* = NULL */)
178 {
179   if (!node)
180     return;
181   ResolveIncludesForNode(node, xmlIncludeConditions);
182
183   TiXmlElement *child = node->FirstChildElement();
184   while (child)
185   {
186     ResolveIncludes(child, xmlIncludeConditions);
187     child = child->NextSiblingElement();
188   }
189 }
190
191 void CGUIIncludes::ResolveIncludesForNode(TiXmlElement *node, std::map<int, bool>* xmlIncludeConditions /* = NULL */)
192 {
193   // we have a node, find any <include file="fileName">tagName</include> tags and replace
194   // recursively with their real includes
195   if (!node) return;
196
197   // First add the defaults if this is for a control
198   CStdString type;
199   if (node->ValueStr() == "control")
200   {
201     type = node->Attribute("type");
202     map<CStdString, TiXmlElement>::const_iterator it = m_defaults.find(type);
203     if (it != m_defaults.end())
204     {
205       const TiXmlElement &element = (*it).second;
206       const TiXmlElement *tag = element.FirstChildElement();
207       while (tag)
208       {
209         // we insert at the end of block
210         node->InsertEndChild(*tag);
211         tag = tag->NextSiblingElement();
212       }
213     }
214   }
215
216   TiXmlElement *include = node->FirstChildElement("include");
217   while (include && include->FirstChild())
218   {
219     // have an include tag - grab it's tag name and replace it with the real tag contents
220     const char *file = include->Attribute("file");
221     if (file)
222     { // we need to load this include from the alternative file
223       LoadIncludes(g_SkinInfo->GetSkinPath(file));
224     }
225     const char *condition = include->Attribute("condition");
226     if (condition)
227     { // check this condition
228       int conditionID = g_infoManager.Register(condition);
229       bool value = g_infoManager.GetBoolValue(conditionID);
230
231       if (xmlIncludeConditions)
232         (*xmlIncludeConditions)[conditionID] = value;
233
234       if (!value)
235       {
236         include = include->NextSiblingElement("include");
237         continue;
238       }
239     }
240     CStdString tagName = include->FirstChild()->Value();
241     map<CStdString, TiXmlElement>::const_iterator it = m_includes.find(tagName);
242     if (it != m_includes.end())
243     { // found the tag(s) to include - let's replace it
244       const TiXmlElement &element = (*it).second;
245       const TiXmlElement *tag = element.FirstChildElement();
246       while (tag)
247       {
248         // we insert before the <include> element to keep the correct
249         // order (we render in the order given in the xml file)
250         node->InsertBeforeChild(include, *tag);
251         tag = tag->NextSiblingElement();
252       }
253       // remove the <include>tagName</include> element
254       node->RemoveChild(include);
255       include = node->FirstChildElement("include");
256     }
257     else
258     { // invalid include
259       CLog::Log(LOGWARNING, "Skin has invalid include: %s", tagName.c_str());
260       include = include->NextSiblingElement("include");
261     }
262   }
263
264   // run through this element's attributes, resolving any constants
265   TiXmlAttribute *attribute = node->FirstAttribute();
266   while (attribute)
267   { // check the attribute against our set
268     if (m_constantAttributes.count(attribute->Name()))
269       attribute->SetValue(ResolveConstant(attribute->ValueStr()));
270     attribute = attribute->Next();
271   }
272   // also do the value
273   if (node->FirstChild() && node->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT && m_constantNodes.count(node->ValueStr()))
274     node->FirstChild()->SetValue(ResolveConstant(node->FirstChild()->ValueStr()));
275 }
276
277 CStdString CGUIIncludes::ResolveConstant(const CStdString &constant) const
278 {
279   CStdStringArray values;
280   StringUtils::SplitString(constant, ",", values);
281   for (unsigned int i = 0; i < values.size(); ++i)
282   {
283     map<CStdString, CStdString>::const_iterator it = m_constants.find(values[i]);
284     if (it != m_constants.end())
285       values[i] = it->second;
286   }
287   CStdString value;
288   StringUtils::JoinString(values, ",", value);
289   return value;
290 }
291
292 const INFO::CSkinVariableString* CGUIIncludes::CreateSkinVariable(const CStdString& name, int context)
293 {
294   map<CStdString, TiXmlElement>::const_iterator it = m_skinvariables.find(name);
295   if (it != m_skinvariables.end())
296     return INFO::CSkinVariable::CreateFromXML(it->second, context);
297   return NULL;
298 }