/* * Copyright (C) 2005-2008 Team XBMC * http://www.xbmc.org * * This Program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2, or (at your option) * any later version. * * This Program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with XBMC; see the file COPYING. If not, write to * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. * http://www.gnu.org/copyleft/gpl.html * */ #include "GUIControlFactory.h" #include "LocalizeStrings.h" #include "GUIButtonControl.h" #include "GUIRadioButtonControl.h" #include "GUISpinControl.h" #include "GUIRSSControl.h" #include "GUIImage.h" #include "GUIBorderedImage.h" #include "GUILabelControl.h" #include "GUIEditControl.h" #include "GUIFadeLabelControl.h" #include "GUICheckMarkControl.h" #include "GUIToggleButtonControl.h" #include "GUITextBox.h" #include "GUIVideoControl.h" #include "GUIProgressControl.h" #include "GUISliderControl.h" #include "GUISelectButtonControl.h" #include "GUIMoverControl.h" #include "GUIResizeControl.h" #include "GUIButtonScroller.h" #include "GUISpinControlEx.h" #include "GUIVisualisationControl.h" #include "GUISettingsSliderControl.h" #include "GUIMultiImage.h" #include "GUIControlGroup.h" #include "GUIControlGroupList.h" #include "GUIScrollBarControl.h" #include "GUIListContainer.h" #include "GUIFixedListContainer.h" #include "GUIWrappingListContainer.h" #include "GUIPanelContainer.h" #include "GUIMultiSelectText.h" #include "GUIListLabel.h" #include "GUIListGroup.h" #include "utils/GUIInfoManager.h" #include "utils/CharsetConverter.h" #include "utils/log.h" #include "ButtonTranslator.h" #include "XMLUtils.h" #include "GUIFontManager.h" #include "GUIColorManager.h" #include "addons/Skin.h" #include "Settings.h" #include "StringUtils.h" using namespace std; typedef struct { const char* name; CGUIControl::GUICONTROLTYPES type; } ControlMapping; static const ControlMapping controls[] = {{"button", CGUIControl::GUICONTROL_BUTTON}, {"checkmark", CGUIControl::GUICONTROL_CHECKMARK}, {"fadelabel", CGUIControl::GUICONTROL_FADELABEL}, {"image", CGUIControl::GUICONTROL_IMAGE}, {"largeimage", CGUIControl::GUICONTROL_IMAGE}, {"image", CGUIControl::GUICONTROL_BORDEREDIMAGE}, {"label", CGUIControl::GUICONTROL_LABEL}, {"label", CGUIControl::GUICONTROL_LISTLABEL}, {"group", CGUIControl::GUICONTROL_GROUP}, {"group", CGUIControl::GUICONTROL_LISTGROUP}, {"progress", CGUIControl::GUICONTROL_PROGRESS}, {"radiobutton", CGUIControl::GUICONTROL_RADIO}, {"rss", CGUIControl::GUICONTROL_RSS}, {"selectbutton", CGUIControl::GUICONTROL_SELECTBUTTON}, {"slider", CGUIControl::GUICONTROL_SLIDER}, {"sliderex", CGUIControl::GUICONTROL_SETTINGS_SLIDER}, {"spincontrol", CGUIControl::GUICONTROL_SPIN}, {"spincontrolex", CGUIControl::GUICONTROL_SPINEX}, {"textbox", CGUIControl::GUICONTROL_TEXTBOX}, {"togglebutton", CGUIControl::GUICONTROL_TOGGLEBUTTON}, {"videowindow", CGUIControl::GUICONTROL_VIDEO}, {"mover", CGUIControl::GUICONTROL_MOVER}, {"resize", CGUIControl::GUICONTROL_RESIZE}, {"buttonscroller", CGUIControl::GUICONTROL_BUTTONBAR}, {"edit", CGUIControl::GUICONTROL_EDIT}, {"visualisation", CGUIControl::GUICONTROL_VISUALISATION}, {"karvisualisation", CGUIControl::GUICONTROL_VISUALISATION}, {"renderaddon", CGUIControl::GUICONTROL_RENDERADDON}, {"multiimage", CGUIControl::GUICONTROL_MULTI_IMAGE}, {"grouplist", CGUIControl::GUICONTROL_GROUPLIST}, {"scrollbar", CGUIControl::GUICONTROL_SCROLLBAR}, {"multiselect", CGUIControl::GUICONTROL_MULTISELECT}, {"list", CGUIControl::GUICONTAINER_LIST}, {"wraplist", CGUIControl::GUICONTAINER_WRAPLIST}, {"fixedlist", CGUIControl::GUICONTAINER_FIXEDLIST}, {"panel", CGUIControl::GUICONTAINER_PANEL}}; CGUIControl::GUICONTROLTYPES CGUIControlFactory::TranslateControlType(const CStdString &type) { for (unsigned int i = 0; i < sizeof(controls) / sizeof(controls[0]); ++i) if (0 == type.CompareNoCase(controls[i].name)) return controls[i].type; return CGUIControl::GUICONTROL_UNKNOWN; } CStdString CGUIControlFactory::TranslateControlType(CGUIControl::GUICONTROLTYPES type) { for (unsigned int i = 0; i < sizeof(controls) / sizeof(controls[0]); ++i) if (type == controls[i].type) return controls[i].name; return ""; } CGUIControlFactory::CGUIControlFactory(void) {} CGUIControlFactory::~CGUIControlFactory(void) {} bool CGUIControlFactory::GetIntRange(const TiXmlNode* pRootNode, const char* strTag, int& iMinValue, int& iMaxValue, int& iIntervalValue) { const TiXmlNode* pNode = pRootNode->FirstChild(strTag); if (!pNode || !pNode->FirstChild()) return false; iMinValue = atoi(pNode->FirstChild()->Value()); const char* maxValue = strchr(pNode->FirstChild()->Value(), ','); if (maxValue) { maxValue++; iMaxValue = atoi(maxValue); const char* intervalValue = strchr(maxValue, ','); if (intervalValue) { intervalValue++; iIntervalValue = atoi(intervalValue); } } return true; } bool CGUIControlFactory::GetFloatRange(const TiXmlNode* pRootNode, const char* strTag, float& fMinValue, float& fMaxValue, float& fIntervalValue) { const TiXmlNode* pNode = pRootNode->FirstChild(strTag); if (!pNode || !pNode->FirstChild()) return false; fMinValue = (float)atof(pNode->FirstChild()->Value()); const char* maxValue = strchr(pNode->FirstChild()->Value(), ','); if (maxValue) { maxValue++; fMaxValue = (float)atof(maxValue); const char* intervalValue = strchr(maxValue, ','); if (intervalValue) { intervalValue++; fIntervalValue = (float)atof(intervalValue); } } return true; } bool CGUIControlFactory::GetFloat(const TiXmlNode* pRootNode, const char* strTag, float& value) { const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); if (!pNode || !pNode->FirstChild()) return false; return g_SkinInfo->ResolveConstant(pNode->FirstChild()->Value(), value); } bool CGUIControlFactory::GetUnsigned(const TiXmlNode* pRootNode, const char* strTag, unsigned int &value) { const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); if (!pNode || !pNode->FirstChild()) return false; return g_SkinInfo->ResolveConstant(pNode->FirstChild()->Value(), value); } bool CGUIControlFactory::GetDimension(const TiXmlNode *pRootNode, const char* strTag, float &value, float &min) { const TiXmlElement* pNode = pRootNode->FirstChildElement(strTag); if (!pNode || !pNode->FirstChild()) return false; if (0 == strnicmp("auto", pNode->FirstChild()->Value(), 4)) { // auto-width - at least min must be set g_SkinInfo->ResolveConstant(pNode->Attribute("max"), value); g_SkinInfo->ResolveConstant(pNode->Attribute("min"), min); if (!min) min = 1; return true; } return g_SkinInfo->ResolveConstant(pNode->FirstChild()->Value(), value); } bool CGUIControlFactory::GetMultipleString(const TiXmlNode* pRootNode, const char* strTag, std::vector& vecStringValue) { const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); if (!pNode) return false; vecStringValue.clear(); bool bFound = false; while (pNode) { CGUIActionDescriptor action; if (CGUIControlFactory::GetAction((const TiXmlElement*) pNode, action)) { vecStringValue.push_back(action); bFound = true; } pNode = pNode->NextSibling(strTag); } return bFound; } bool CGUIControlFactory::GetAction(const TiXmlElement* pElement, CGUIActionDescriptor &action) { CStdString langStr = pElement->Attribute("lang"); if (langStr.CompareNoCase("python") == 0 ) action.m_lang = CGUIActionDescriptor::LANG_PYTHON; else action.m_lang = CGUIActionDescriptor::LANG_XBMC; if (pElement->FirstChild()) { action.m_action = pElement->FirstChild()->Value(); return true; } else { action.m_action = ""; return false; } } bool CGUIControlFactory::GetAspectRatio(const TiXmlNode* pRootNode, const char* strTag, CAspectRatio &aspect) { CStdString ratio; const TiXmlElement *node = pRootNode->FirstChildElement(strTag); if (!node || !node->FirstChild()) return false; ratio = node->FirstChild()->Value(); if (ratio.CompareNoCase("keep") == 0) aspect.ratio = CAspectRatio::AR_KEEP; else if (ratio.CompareNoCase("scale") == 0) aspect.ratio = CAspectRatio::AR_SCALE; else if (ratio.CompareNoCase("center") == 0) aspect.ratio = CAspectRatio::AR_CENTER; else if (ratio.CompareNoCase("stretch") == 0) aspect.ratio = CAspectRatio::AR_STRETCH; const char *attribute = node->Attribute("align"); if (attribute) { CStdString align(attribute); if (align.CompareNoCase("center") == 0) aspect.align = ASPECT_ALIGN_CENTER | (aspect.align & ASPECT_ALIGNY_MASK); else if (align.CompareNoCase("right") == 0) aspect.align = ASPECT_ALIGN_RIGHT | (aspect.align & ASPECT_ALIGNY_MASK); else if (align.CompareNoCase("left") == 0) aspect.align = ASPECT_ALIGN_LEFT | (aspect.align & ASPECT_ALIGNY_MASK); } attribute = node->Attribute("aligny"); if (attribute) { CStdString align(attribute); if (align.CompareNoCase("center") == 0) aspect.align = ASPECT_ALIGNY_CENTER | (aspect.align & ASPECT_ALIGN_MASK); else if (align.CompareNoCase("bottom") == 0) aspect.align = ASPECT_ALIGNY_BOTTOM | (aspect.align & ASPECT_ALIGN_MASK); else if (align.CompareNoCase("top") == 0) aspect.align = ASPECT_ALIGNY_TOP | (aspect.align & ASPECT_ALIGN_MASK); } attribute = node->Attribute("scalediffuse"); if (attribute) { CStdString scale(attribute); if (scale.CompareNoCase("true") == 0 || scale.CompareNoCase("yes") == 0) aspect.scaleDiffuse = true; else aspect.scaleDiffuse = false; } return true; } bool CGUIControlFactory::GetInfoTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image, CGUIInfoLabel &info) { GetTexture(pRootNode, strTag, image); image.filename = ""; GetInfoLabel(pRootNode, strTag, info); return true; } bool CGUIControlFactory::GetTexture(const TiXmlNode* pRootNode, const char* strTag, CTextureInfo &image) { const TiXmlElement* pNode = pRootNode->FirstChildElement(strTag); if (!pNode) return false; const char *border = pNode->Attribute("border"); if (border) GetRectFromString(border, image.border); image.orientation = 0; const char *flipX = pNode->Attribute("flipx"); if (flipX && strcmpi(flipX, "true") == 0) image.orientation = 1; const char *flipY = pNode->Attribute("flipy"); if (flipY && strcmpi(flipY, "true") == 0) image.orientation = 3 - image.orientation; // either 3 or 2 image.diffuse = pNode->Attribute("diffuse"); const char *background = pNode->Attribute("background"); if (background && strnicmp(background, "true", 4) == 0) image.useLarge = true; image.filename = (pNode->FirstChild() && pNode->FirstChild()->ValueStr() != "-") ? pNode->FirstChild()->Value() : ""; return true; } void CGUIControlFactory::GetRectFromString(const CStdString &string, CRect &rect) { // format is rect="left[,top,right,bottom]" CStdStringArray strRect; StringUtils::SplitString(string, ",", strRect); if (strRect.size() == 1) { g_SkinInfo->ResolveConstant(strRect[0], rect.x1); rect.y1 = rect.x1; rect.x2 = rect.x1; rect.y2 = rect.x1; } else if (strRect.size() == 4) { g_SkinInfo->ResolveConstant(strRect[0], rect.x1); g_SkinInfo->ResolveConstant(strRect[1], rect.y1); g_SkinInfo->ResolveConstant(strRect[2], rect.x2); g_SkinInfo->ResolveConstant(strRect[3], rect.y2); } } bool CGUIControlFactory::GetAlignment(const TiXmlNode* pRootNode, const char* strTag, uint32_t& alignment) { const TiXmlNode* pNode = pRootNode->FirstChild(strTag); if (!pNode || !pNode->FirstChild()) return false; CStdString strAlign = pNode->FirstChild()->Value(); if (strAlign == "right" || strAlign == "bottom") alignment = XBFONT_RIGHT; else if (strAlign == "center") alignment = XBFONT_CENTER_X; else if (strAlign == "justify") alignment = XBFONT_JUSTIFIED; else alignment = XBFONT_LEFT; return true; } bool CGUIControlFactory::GetAlignmentY(const TiXmlNode* pRootNode, const char* strTag, uint32_t& alignment) { const TiXmlNode* pNode = pRootNode->FirstChild(strTag ); if (!pNode || !pNode->FirstChild()) { return false; } CStdString strAlign = pNode->FirstChild()->Value(); alignment = 0; if (strAlign == "center") { alignment = XBFONT_CENTER_Y; } return true; } bool CGUIControlFactory::GetConditionalVisibility(const TiXmlNode* control, int &condition, CGUIInfoBool &allowHiddenFocus) { const TiXmlElement* node = control->FirstChildElement("visible"); if (!node) return false; vector conditions; while (node) { const char *hidden = node->Attribute("allowhiddenfocus"); if (hidden) allowHiddenFocus.Parse(hidden); // add to our condition string if (!node->NoChildren()) conditions.push_back(node->FirstChild()->Value()); node = node->NextSiblingElement("visible"); } if (!conditions.size()) return false; if (conditions.size() == 1) condition = g_infoManager.TranslateString(conditions[0]); else { // multiple conditions should be anded together CStdString conditionString = "["; for (unsigned int i = 0; i < conditions.size() - 1; i++) conditionString += conditions[i] + "] + ["; conditionString += conditions[conditions.size() - 1] + "]"; condition = g_infoManager.TranslateString(conditionString); } return (condition != 0); } bool CGUIControlFactory::GetCondition(const TiXmlNode *control, const char *tag, int &condition) { CStdString condString; if (XMLUtils::GetString(control, tag, condString)) { condition = g_infoManager.TranslateString(condString); return true; } return false; } bool CGUIControlFactory::GetConditionalVisibility(const TiXmlNode *control, int &condition) { CGUIInfoBool allowHiddenFocus; return GetConditionalVisibility(control, condition, allowHiddenFocus); } bool CGUIControlFactory::GetAnimations(TiXmlNode *control, const CRect &rect, vector &animations) { TiXmlElement* node = control->FirstChildElement("animation"); bool ret = false; if (node) animations.clear(); while (node) { g_SkinInfo->ResolveIncludes(node); ret = true; if (node->FirstChild()) { CAnimation anim; anim.Create(node, rect); animations.push_back(anim); if (strcmpi(node->FirstChild()->Value(), "VisibleChange") == 0) { // add the hidden one as well TiXmlElement hidden(*node); hidden.FirstChild()->SetValue("hidden"); const char *start = hidden.Attribute("start"); const char *end = hidden.Attribute("end"); if (start && end) { CStdString temp = end; hidden.SetAttribute("end", start); hidden.SetAttribute("start", temp.c_str()); } else if (start) hidden.SetAttribute("end", start); else if (end) hidden.SetAttribute("start", end); CAnimation anim2; anim2.Create(&hidden, rect); animations.push_back(anim2); } } node = node->NextSiblingElement("animation"); } return ret; } bool CGUIControlFactory::GetHitRect(const TiXmlNode *control, CRect &rect) { const TiXmlElement* node = control->FirstChildElement("hitrect"); if (node) { if (node->Attribute("x")) g_SkinInfo->ResolveConstant(node->Attribute("x"), rect.x1); if (node->Attribute("y")) g_SkinInfo->ResolveConstant(node->Attribute("y"), rect.y1); if (node->Attribute("w")) { g_SkinInfo->ResolveConstant(node->Attribute("w"), rect.x2); rect.x2 += rect.x1; } if (node->Attribute("h")) { g_SkinInfo->ResolveConstant(node->Attribute("h"), rect.y2); rect.y2 += rect.y1; } return true; } return false; } bool CGUIControlFactory::GetColor(const TiXmlNode *control, const char *strTag, color_t &value) { const TiXmlElement* node = control->FirstChildElement(strTag); if (node && node->FirstChild()) { value = g_colorManager.GetColor(node->FirstChild()->Value()); return true; } return false; } bool CGUIControlFactory::GetInfoColor(const TiXmlNode *control, const char *strTag, CGUIInfoColor &value) { const TiXmlElement* node = control->FirstChildElement(strTag); if (node && node->FirstChild()) { value.Parse(node->FirstChild()->Value()); return true; } return false; } bool CGUIControlFactory::GetNavigation(const TiXmlElement *node, const char *tag, int &direction, vector &actions) { if (!GetMultipleString(node, tag, actions)) return false; // no tag specified if (actions.size() == 1 && StringUtils::IsNaturalNumber(actions[0].m_action)) { // single numeric tag specified direction = atol(actions[0].m_action.c_str()); actions.clear(); } else direction = 0; return true; } void CGUIControlFactory::GetInfoLabel(const TiXmlNode *pControlNode, const CStdString &labelTag, CGUIInfoLabel &infoLabel) { vector labels; GetInfoLabels(pControlNode, labelTag, labels); if (labels.size()) infoLabel = labels[0]; } bool CGUIControlFactory::GetInfoLabelFromElement(const TiXmlElement *element, CGUIInfoLabel &infoLabel) { if (!element || !element->FirstChild()) return false; CStdString label = element->FirstChild()->Value(); if (label.IsEmpty() || label == "-") return false; CStdString fallback = element->Attribute("fallback"); if (StringUtils::IsNaturalNumber(label)) label = g_localizeStrings.Get(atoi(label)); else // we assume the skin xml's aren't encoded as UTF-8 g_charsetConverter.unknownToUTF8(label); if (StringUtils::IsNaturalNumber(fallback)) fallback = g_localizeStrings.Get(atoi(fallback)); else g_charsetConverter.unknownToUTF8(fallback); infoLabel.SetLabel(label, fallback); return true; } void CGUIControlFactory::GetInfoLabels(const TiXmlNode *pControlNode, const CStdString &labelTag, vector &infoLabels) { // we can have the following infolabels: // 1. 1234 -> direct number // 2. -> lookup in localizestrings // 3. -> infolabel with given fallback // 4. ListItem.Album (uses