bool CGUIIncludes::LoadIncludesFromXML(const TiXmlElement *root) { if (!root || strcmpi(root->Value(), "includes")) { CLog::Log(LOGERROR, "Skin includes must start with the <includes> tag"); return false; } const TiXmlElement* node = root->FirstChildElement("include"); while (node) { if (node->Attribute("name") && node->FirstChild()) { std::string tagName = node->Attribute("name"); // we'll parse and store parameter list with defaults when include definition is first encountered // if there's a <definition> tag only use its body as the actually included part const TiXmlElement *definitionTag = node->FirstChildElement("definition"); const TiXmlElement *includeBody = definitionTag ? definitionTag : node; // if there's a <param> tag there also must be a <definition> tag Params defaultParams; bool haveParamTags = GetParameters(node, "default", defaultParams); if (haveParamTags && !definitionTag) CLog::Log(LOGWARNING, "Skin has invalid include definition: %s", tagName.c_str()); else m_includes.insert({ tagName, { *includeBody, std::move(defaultParams) } }); } else if (node->Attribute("file")) { const char *condition = node->Attribute("condition"); if (condition) { // check this condition INFO::InfoPtr conditionID = g_infoManager.Register(condition); bool value = conditionID->Get(); if (value) { // load this file in as well LoadIncludes(g_SkinInfo->GetSkinPath(node->Attribute("file"))); } } else LoadIncludes(g_SkinInfo->GetSkinPath(node->Attribute("file"))); } node = node->NextSiblingElement("include"); } // now defaults node = root->FirstChildElement("default"); while (node) { if (node->Attribute("type") && node->FirstChild()) { std::string tagName = node->Attribute("type"); m_defaults.insert(std::pair<std::string, TiXmlElement>(tagName, *node)); } node = node->NextSiblingElement("default"); } // and finally constants node = root->FirstChildElement("constant"); while (node) { if (node->Attribute("name") && node->FirstChild()) { std::string tagName = node->Attribute("name"); m_constants.insert(make_pair(tagName, node->FirstChild()->ValueStr())); } node = node->NextSiblingElement("constant"); } node = root->FirstChildElement("variable"); while (node) { if (node->Attribute("name") && node->FirstChild()) { std::string tagName = node->Attribute("name"); m_skinvariables.insert(make_pair(tagName, *node)); } node = node->NextSiblingElement("variable"); } node = root->FirstChildElement("expression"); while (node) { if (node->Attribute("name") && node->FirstChild()) { std::string tagName = node->Attribute("name"); m_expressions.insert(make_pair(tagName, node->FirstChild()->ValueStr())); } node = node->NextSiblingElement("expression"); } return true; }
void CGUIIncludes::ResolveIncludesForNode(TiXmlElement *node, std::map<INFO::InfoPtr, bool>* xmlIncludeConditions /* = NULL */) { // we have a node, find any <include file="fileName">tagName</include> tags and replace // recursively with their real includes if (!node) return; // First add the defaults if this is for a control std::string type; if (node->ValueStr() == "control") { type = XMLUtils::GetAttribute(node, "type"); std::map<std::string, TiXmlElement>::const_iterator it = m_defaults.find(type); if (it != m_defaults.end()) { // we don't insert <left> et. al. if <posx> or <posy> is specified bool hasPosX(node->FirstChild("posx") != NULL); bool hasPosY(node->FirstChild("posy") != NULL); const TiXmlElement &element = (*it).second; const TiXmlElement *tag = element.FirstChildElement(); while (tag) { std::string value = tag->ValueStr(); bool skip(false); if (hasPosX && (value == "left" || value == "right" || value == "centerleft" || value == "centerright")) skip = true; if (hasPosY && (value == "top" || value == "bottom" || value == "centertop" || value == "centerbottom")) skip = true; // we insert at the end of block if (!skip) node->InsertEndChild(*tag); tag = tag->NextSiblingElement(); } } } TiXmlElement *include = node->FirstChildElement("include"); while (include) { // have an include tag - grab it's tag name and replace it with the real tag contents const char *file = include->Attribute("file"); if (file) { // we need to load this include from the alternative file LoadIncludes(g_SkinInfo->GetSkinPath(file)); } const char *condition = include->Attribute("condition"); if (condition) { // check this condition INFO::InfoPtr conditionID = g_infoManager.Register(condition); bool value = conditionID->Get(); if (xmlIncludeConditions) (*xmlIncludeConditions)[conditionID] = value; if (!value) { include = include->NextSiblingElement("include"); continue; } } Params params; std::string tagName; // determine which form of include call we have const char *name = include->Attribute("content"); if (name) { // 1. <include name="MyControl" /> // 2. <include name="MyControl"> // <param name="posx" value="225" /> // <param name="posy">150</param> // ... // </include> tagName = name; GetParameters(include, "value", params); } else { const TiXmlNode *child = include->FirstChild(); if (child && child->Type() == TiXmlNode::TINYXML_TEXT) { // 3. <include>MyControl</include> // old-style includes for backward compatibility tagName = child->ValueStr(); } } std::map<std::string, std::pair<TiXmlElement, Params>>::const_iterator it = m_includes.find(tagName); if (it != m_includes.end()) { // found the tag(s) to include - let's replace it const TiXmlElement *includeBody = &it->second.first; const Params& defaultParams = it->second.second; const TiXmlElement *tag = includeBody->FirstChildElement(); // combine passed include parameters with their default values into a single list (no overwrites) params.insert(defaultParams.begin(), defaultParams.end()); while (tag) { // we insert before the <include> element to keep the correct // order (we render in the order given in the xml file) TiXmlElement *insertedTag = static_cast<TiXmlElement*>(node->InsertBeforeChild(include, *tag)); // after insertion we resolve parameters even if parameter list is empty (to remove param references) ResolveParametersForNode(insertedTag, params); tag = tag->NextSiblingElement(); } // remove the include element itself node->RemoveChild(include); include = node->FirstChildElement("include"); } else { // invalid include CLog::Log(LOGWARNING, "Skin has invalid include: %s", tagName.c_str()); include = include->NextSiblingElement("include"); } } // run through this element's attributes, resolving any constants TiXmlAttribute *attribute = node->FirstAttribute(); while (attribute) { // check the attribute against our set if (m_constantAttributes.count(attribute->Name())) attribute->SetValue(ResolveConstant(attribute->ValueStr())); if (m_expressionAttributes.count(attribute->Name())) attribute->SetValue(ResolveExpressions(attribute->ValueStr())); attribute = attribute->Next(); } // also do the value if (node->FirstChild() && node->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT && m_constantNodes.count(node->ValueStr())) node->FirstChild()->SetValue(ResolveConstant(node->FirstChild()->ValueStr())); if (node->FirstChild() && node->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT && m_expressionNodes.count(node->ValueStr())) node->FirstChild()->SetValue(ResolveExpressions(node->FirstChild()->ValueStr())); }
void CGUIIncludes::ResolveIncludes(TiXmlElement *node, std::map<INFO::InfoPtr, bool>* xmlIncludeConditions /* = NULL */) { if (!node) return; TiXmlElement *include = node->FirstChildElement("include"); while (include) { // file: load includes from specified XML file const char *file = include->Attribute("file"); if (file) Load(g_SkinInfo->GetSkinPath(file)); // condition: process include if condition evals to true const char *condition = include->Attribute("condition"); if (condition) { INFO::InfoPtr conditionID = g_infoManager.Register(ResolveExpressions(condition)); bool value = conditionID->Get(); if (xmlIncludeConditions) xmlIncludeConditions->insert(std::make_pair(conditionID, value)); if (!value) { include = include->NextSiblingElement("include"); continue; } } Params params; std::string tagName; // normal or old-style include const char *name = include->Attribute("content"); if (name) { // <include content="MyControl" /> // or // <include content="MyControl"> // <param name="posx" value="225" /> // <param name="posy">150</param> // ... // </include> tagName = name; GetParameters(include, "value", params); } else { const TiXmlNode *child = include->FirstChild(); if (child && child->Type() == TiXmlNode::TINYXML_TEXT) { // <include>MyControl</include> // old-style includes for backward compatibility tagName = child->ValueStr(); } } // check, whether the include exists and therefore should be replaced by its definition auto it = m_includes.find(tagName); if (it != m_includes.end()) { const TiXmlElement *includeBody = &it->second.first; const Params& defaultParams = it->second.second; const TiXmlElement *tag = includeBody->FirstChildElement(); // combine passed include parameters with their default values into a single list (no overwrites) params.insert(defaultParams.begin(), defaultParams.end()); while (tag) { // we insert before the <include> element to keep the correct // order (we render in the order given in the xml file) TiXmlElement *insertedTag = static_cast<TiXmlElement*>(node->InsertBeforeChild(include, *tag)); // after insertion we resolve parameters even if parameter list is empty (to remove param references) ResolveParametersForNode(insertedTag, params); tag = tag->NextSiblingElement(); } // remove the include element itself node->RemoveChild(include); include = node->FirstChildElement("include"); } else { // invalid include CLog::Log(LOGWARNING, "Skin has invalid include: %s", tagName.c_str()); include = include->NextSiblingElement("include"); } } }