From ffca21f2743a7b367fa212799c6e2fea6190dd5d Mon Sep 17 00:00:00 2001 From: manuel Date: Tue, 3 Mar 2015 16:53:59 +0100 Subject: initial commit for kodi master --- xbmc/addons/Skin.cpp | 491 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 491 insertions(+) create mode 100644 xbmc/addons/Skin.cpp (limited to 'xbmc/addons/Skin.cpp') diff --git a/xbmc/addons/Skin.cpp b/xbmc/addons/Skin.cpp new file mode 100644 index 0000000..acb5799 --- /dev/null +++ b/xbmc/addons/Skin.cpp @@ -0,0 +1,491 @@ +/* + * Copyright (C) 2005-2013 Team XBMC + * http://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, see + * . + * + */ + +#include "Skin.h" +#include "AddonManager.h" +#include "LangInfo.h" +#include "Util.h" +#include "dialogs/GUIDialogKaiToast.h" +#include "dialogs/GUIDialogYesNo.h" +#include "filesystem/File.h" +#include "filesystem/SpecialProtocol.h" +#include "guilib/GUIWindowManager.h" +#include "guilib/WindowIDs.h" +#include "settings/Settings.h" +#include "settings/lib/Setting.h" +#include "utils/URIUtils.h" +#include "utils/log.h" +#include "utils/StringUtils.h" +#include "ApplicationMessenger.h" + +// fallback for new skin resolution code +#include "filesystem/Directory.h" + +using namespace std; +using namespace XFILE; + +std::shared_ptr g_SkinInfo; + +namespace ADDON +{ + +CSkinInfo::CSkinInfo(const AddonProps &props, const RESOLUTION_INFO &resolution) + : CAddon(props), m_defaultRes(resolution), m_version(""), m_effectsSlowDown(1.f), m_debugging(false) +{ +} + +CSkinInfo::CSkinInfo(const cp_extension_t *ext) + : CAddon(ext), m_version(""), m_effectsSlowDown(1.f) +{ + ELEMENTS elements; + if (CAddonMgr::Get().GetExtElements(ext->configuration, "res", elements)) + { + for (ELEMENTS::iterator i = elements.begin(); i != elements.end(); ++i) + { + int width = atoi(CAddonMgr::Get().GetExtValue(*i, "@width").c_str()); + int height = atoi(CAddonMgr::Get().GetExtValue(*i, "@height").c_str()); + bool defRes = CAddonMgr::Get().GetExtValue(*i, "@default") == "true"; + std::string folder = CAddonMgr::Get().GetExtValue(*i, "@folder"); + float aspect = 0; + std::string strAspect = CAddonMgr::Get().GetExtValue(*i, "@aspect"); + vector fracs = StringUtils::Split(strAspect, ':'); + if (fracs.size() == 2) + aspect = (float)(atof(fracs[0].c_str())/atof(fracs[1].c_str())); + if (width > 0 && height > 0) + { + RESOLUTION_INFO res(width, height, aspect, folder); + res.strId = strAspect; // for skin usage, store aspect string in strId + if (defRes) + m_defaultRes = res; + m_resolutions.push_back(res); + } + } + } + else + { // no resolutions specified -> backward compatibility + std::string defaultWide = CAddonMgr::Get().GetExtValue(ext->configuration, "@defaultwideresolution"); + if (defaultWide.empty()) + defaultWide = CAddonMgr::Get().GetExtValue(ext->configuration, "@defaultresolution"); + TranslateResolution(defaultWide, m_defaultRes); + } + + std::string str = CAddonMgr::Get().GetExtValue(ext->configuration, "@effectslowdown"); + if (!str.empty()) + m_effectsSlowDown = (float)atof(str.c_str()); + + m_debugging = CAddonMgr::Get().GetExtValue(ext->configuration, "@debugging") == "true"; + + LoadStartupWindows(ext); + + // figure out the version + m_version = GetDependencyVersion("xbmc.gui"); +} + +CSkinInfo::~CSkinInfo() +{ +} + +AddonPtr CSkinInfo::Clone() const +{ + return AddonPtr(new CSkinInfo(*this)); +} + +struct closestRes +{ + closestRes(const RESOLUTION_INFO &target) : m_target(target) { }; + bool operator()(const RESOLUTION_INFO &i, const RESOLUTION_INFO &j) + { + float diff = fabs(i.DisplayRatio() - m_target.DisplayRatio()) - fabs(j.DisplayRatio() - m_target.DisplayRatio()); + if (diff < 0) return true; + if (diff > 0) return false; + diff = fabs((float)i.iHeight - m_target.iHeight) - fabs((float)j.iHeight - m_target.iHeight); + if (diff < 0) return true; + if (diff > 0) return false; + return fabs((float)i.iWidth - m_target.iWidth) < fabs((float)j.iWidth - m_target.iWidth); + } + RESOLUTION_INFO m_target; +}; + +void CSkinInfo::Start() +{ + if (!m_resolutions.size()) + { // try falling back to whatever resolutions exist in the directory + CFileItemList items; + CDirectory::GetDirectory(Path(), items, "", DIR_FLAG_NO_FILE_DIRS); + for (int i = 0; i < items.Size(); i++) + { + RESOLUTION_INFO res; + if (items[i]->m_bIsFolder && TranslateResolution(items[i]->GetLabel(), res)) + m_resolutions.push_back(res); + } + } + + if (!m_resolutions.empty()) + { + // find the closest resolution + const RESOLUTION_INFO &target = g_graphicsContext.GetResInfo(); + RESOLUTION_INFO& res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target)); + m_currentAspect = res.strId; + } +} + +std::string CSkinInfo::GetSkinPath(const std::string& strFile, RESOLUTION_INFO *res, const std::string& strBaseDir /* = "" */) const +{ + if (m_resolutions.empty()) + return ""; // invalid skin + + std::string strPathToUse = Path(); + if (!strBaseDir.empty()) + strPathToUse = strBaseDir; + + // if the caller doesn't care about the resolution just use a temporary + RESOLUTION_INFO tempRes; + if (!res) + res = &tempRes; + + // find the closest resolution + const RESOLUTION_INFO &target = g_graphicsContext.GetResInfo(); + *res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target)); + + std::string strPath = URIUtils::AddFileToFolder(strPathToUse, res->strMode); + strPath = URIUtils::AddFileToFolder(strPath, strFile); + if (CFile::Exists(strPath)) + return strPath; + + // use the default resolution + *res = m_defaultRes; + + strPath = URIUtils::AddFileToFolder(strPathToUse, res->strMode); + strPath = URIUtils::AddFileToFolder(strPath, strFile); + return strPath; +} + +bool CSkinInfo::HasSkinFile(const std::string &strFile) const +{ + return CFile::Exists(GetSkinPath(strFile)); +} + +void CSkinInfo::LoadIncludes() +{ + std::string includesPath = CSpecialProtocol::TranslatePathConvertCase(GetSkinPath("includes.xml")); + CLog::Log(LOGINFO, "Loading skin includes from %s", includesPath.c_str()); + m_includes.ClearIncludes(); + m_includes.LoadIncludes(includesPath); +} + +void CSkinInfo::ResolveIncludes(TiXmlElement *node, std::map* xmlIncludeConditions /* = NULL */) +{ + if(xmlIncludeConditions) + xmlIncludeConditions->clear(); + + m_includes.ResolveIncludes(node, xmlIncludeConditions); +} + +int CSkinInfo::GetStartWindow() const +{ + int windowID = CSettings::Get().GetInt("lookandfeel.startupwindow"); + assert(m_startupWindows.size()); + for (vector::const_iterator it = m_startupWindows.begin(); it != m_startupWindows.end(); ++it) + { + if (windowID == (*it).m_id) + return windowID; + } + // return our first one + return m_startupWindows[0].m_id; +} + +bool CSkinInfo::LoadStartupWindows(const cp_extension_t *ext) +{ + m_startupWindows.clear(); + m_startupWindows.push_back(CStartupWindow(WINDOW_HOME, "513")); + m_startupWindows.push_back(CStartupWindow(WINDOW_TV_CHANNELS, "19180")); + m_startupWindows.push_back(CStartupWindow(WINDOW_RADIO_CHANNELS, "19183")); + m_startupWindows.push_back(CStartupWindow(WINDOW_PROGRAMS, "0")); + m_startupWindows.push_back(CStartupWindow(WINDOW_PICTURES, "1")); + m_startupWindows.push_back(CStartupWindow(WINDOW_MUSIC, "2")); + m_startupWindows.push_back(CStartupWindow(WINDOW_VIDEOS, "3")); + m_startupWindows.push_back(CStartupWindow(WINDOW_FILES, "7")); + m_startupWindows.push_back(CStartupWindow(WINDOW_SETTINGS_MENU, "5")); + m_startupWindows.push_back(CStartupWindow(WINDOW_WEATHER, "8")); + return true; +} + +void CSkinInfo::GetSkinPaths(std::vector &paths) const +{ + RESOLUTION_INFO res; + GetSkinPath("Home.xml", &res); + if (!res.strMode.empty()) + paths.push_back(URIUtils::AddFileToFolder(Path(), res.strMode)); + if (res.strMode != m_defaultRes.strMode) + paths.push_back(URIUtils::AddFileToFolder(Path(), m_defaultRes.strMode)); +} + +bool CSkinInfo::TranslateResolution(const std::string &name, RESOLUTION_INFO &res) +{ + std::string lower(name); StringUtils::ToLower(lower); + if (lower == "pal") + res = RESOLUTION_INFO(720, 576, 4.0f/3, "pal"); + else if (lower == "pal16x9") + res = RESOLUTION_INFO(720, 576, 16.0f/9, "pal16x9"); + else if (lower == "ntsc") + res = RESOLUTION_INFO(720, 480, 4.0f/3, "ntsc"); + else if (lower == "ntsc16x9") + res = RESOLUTION_INFO(720, 480, 16.0f/9, "ntsc16x9"); + else if (lower == "720p") + res = RESOLUTION_INFO(1280, 720, 0, "720p"); + else if (lower == "1080i") + res = RESOLUTION_INFO(1920, 1080, 0, "1080i"); + else + return false; + return true; +} + +int CSkinInfo::GetFirstWindow() const +{ + int startWindow = GetStartWindow(); + if (HasSkinFile("Startup.xml")) + startWindow = WINDOW_STARTUP_ANIM; + return startWindow; +} + +bool CSkinInfo::IsInUse() const +{ + // Could extend this to prompt for reverting to the standard skin perhaps + return CSettings::Get().GetString("lookandfeel.skin") == ID(); +} + +const INFO::CSkinVariableString* CSkinInfo::CreateSkinVariable(const std::string& name, int context) +{ + return m_includes.CreateSkinVariable(name, context); +} + +bool CSkinInfo::OnPreInstall() +{ + // check whether this is an active skin - we need to unload it if so + if (IsInUse()) + { + CApplicationMessenger::Get().ExecBuiltIn("UnloadSkin", true); + return true; + } + return false; +} + +void CSkinInfo::OnPostInstall(bool restart, bool update, bool modal) +{ + if (restart || (!update && !modal && CGUIDialogYesNo::ShowAndGetInput(Name(), g_localizeStrings.Get(24099),"",""))) + { + CGUIDialogKaiToast *toast = (CGUIDialogKaiToast *)g_windowManager.GetWindow(WINDOW_DIALOG_KAI_TOAST); + if (toast) + { + toast->ResetTimer(); + toast->Close(true); + } + if (CSettings::Get().GetString("lookandfeel.skin") == ID()) + CApplicationMessenger::Get().ExecBuiltIn("ReloadSkin", true); + else + CSettings::Get().SetString("lookandfeel.skin", ID()); + } +} + +void CSkinInfo::SettingOptionsSkinColorsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + // Remove the .xml extension from the Themes + if (URIUtils::HasExtension(settingValue, ".xml")) + URIUtils::RemoveExtension(settingValue); + current = "SKINDEFAULT"; + + // There is a default theme (just defaults.xml) + // any other *.xml files are additional color themes on top of this one. + + // add the default label + list.push_back(make_pair(g_localizeStrings.Get(15109), "SKINDEFAULT")); // the standard defaults.xml will be used! + + // Search for colors in the Current skin! + vector vecColors; + string strPath = URIUtils::AddFileToFolder(g_SkinInfo->Path(), "colors"); + + CFileItemList items; + CDirectory::GetDirectory(CSpecialProtocol::TranslatePathConvertCase(strPath), items, ".xml"); + // Search for Themes in the Current skin! + for (int i = 0; i < items.Size(); ++i) + { + CFileItemPtr pItem = items[i]; + if (!pItem->m_bIsFolder && !StringUtils::EqualsNoCase(pItem->GetLabel(), "defaults.xml")) + { // not the default one + vecColors.push_back(pItem->GetLabel().substr(0, pItem->GetLabel().size() - 4)); + } + } + sort(vecColors.begin(), vecColors.end(), sortstringbyname()); + for (int i = 0; i < (int) vecColors.size(); ++i) + list.push_back(make_pair(vecColors[i], vecColors[i])); + + // try to find the best matching value + for (vector< pair >::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (StringUtils::EqualsNoCase(it->second, settingValue)) + current = settingValue; + } +} + +void CSkinInfo::SettingOptionsSkinFontsFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + bool currentValueSet = false; + std::string strPath = g_SkinInfo->GetSkinPath("Font.xml"); + + CXBMCTinyXML xmlDoc; + if (!xmlDoc.LoadFile(strPath)) + { + CLog::Log(LOGERROR, "FillInSkinFonts: Couldn't load %s", strPath.c_str()); + return; + } + + const TiXmlElement* pRootElement = xmlDoc.RootElement(); + if (!pRootElement || pRootElement->ValueStr() != "fonts") + { + CLog::Log(LOGERROR, "FillInSkinFonts: file %s doesn't start with ", strPath.c_str()); + return; + } + + const TiXmlElement *pChild = pRootElement->FirstChildElement("fontset"); + while (pChild) + { + const char* idAttr = pChild->Attribute("id"); + const char* idLocAttr = pChild->Attribute("idloc"); + if (idAttr != NULL) + { + if (idLocAttr) + list.push_back(make_pair(g_localizeStrings.Get(atoi(idLocAttr)), idAttr)); + else + list.push_back(make_pair(idAttr, idAttr)); + + if (StringUtils::EqualsNoCase(idAttr, settingValue)) + currentValueSet = true; + } + pChild = pChild->NextSiblingElement("fontset"); + } + + if (list.empty()) + { // Since no fontset is defined, there is no selection of a fontset, so disable the component + list.push_back(make_pair(g_localizeStrings.Get(13278), "")); + current = ""; + currentValueSet = true; + } + + if (!currentValueSet) + current = list[0].second; +} + +void CSkinInfo::SettingOptionsSkinSoundFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + current = "SKINDEFAULT"; + + //find skins... + CFileItemList items; + CDirectory::GetDirectory("special://xbmc/sounds/", items); + CDirectory::GetDirectory("special://home/sounds/", items); + + vector vecSoundSkins; + for (int i = 0; i < items.Size(); i++) + { + CFileItemPtr pItem = items[i]; + if (pItem->m_bIsFolder) + { + if (StringUtils::EqualsNoCase(pItem->GetLabel(), ".svn") || + StringUtils::EqualsNoCase(pItem->GetLabel(), "fonts") || + StringUtils::EqualsNoCase(pItem->GetLabel(), "media")) + continue; + + vecSoundSkins.push_back(pItem->GetLabel()); + } + } + + list.push_back(make_pair(g_localizeStrings.Get(474), "OFF")); + list.push_back(make_pair(g_localizeStrings.Get(15109), "SKINDEFAULT")); + + sort(vecSoundSkins.begin(), vecSoundSkins.end(), sortstringbyname()); + for (unsigned int i = 0; i < vecSoundSkins.size(); i++) + list.push_back(make_pair(vecSoundSkins[i], vecSoundSkins[i])); + + // try to find the best matching value + for (vector< pair >::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (StringUtils::EqualsNoCase(it->second, settingValue)) + current = settingValue; + } +} + +void CSkinInfo::SettingOptionsSkinThemesFiller(const CSetting *setting, std::vector< std::pair > &list, std::string ¤t, void *data) +{ + // get the choosen theme and remove the extension from the current theme (backward compat) + std::string settingValue = ((const CSettingString*)setting)->GetValue(); + URIUtils::RemoveExtension(settingValue); + current = "SKINDEFAULT"; + + // there is a default theme (just Textures.xpr/xbt) + // any other *.xpr|*.xbt files are additional themes on top of this one. + + // add the default Label + list.push_back(make_pair(g_localizeStrings.Get(15109), "SKINDEFAULT")); // the standard Textures.xpr/xbt will be used + + // search for themes in the current skin! + vector vecTheme; + CUtil::GetSkinThemes(vecTheme); + + // sort the themes for GUI and list them + for (int i = 0; i < (int) vecTheme.size(); ++i) + list.push_back(make_pair(vecTheme[i], vecTheme[i])); + + // try to find the best matching value + for (vector< pair >::const_iterator it = list.begin(); it != list.end(); ++it) + { + if (StringUtils::EqualsNoCase(it->second, settingValue)) + current = settingValue; + } +} + +void CSkinInfo::SettingOptionsStartupWindowsFiller(const CSetting *setting, std::vector< std::pair > &list, int ¤t, void *data) +{ + int settingValue = ((const CSettingInt *)setting)->GetValue(); + current = -1; + + const vector &startupWindows = g_SkinInfo->GetStartupWindows(); + + for (vector::const_iterator it = startupWindows.begin(); it != startupWindows.end(); ++it) + { + string windowName = it->m_name; + if (StringUtils::IsNaturalNumber(windowName)) + windowName = g_localizeStrings.Get(atoi(windowName.c_str())); + int windowID = it->m_id; + + list.push_back(make_pair(windowName, windowID)); + + if (settingValue == windowID) + current = settingValue; + } + + // if the current value hasn't been properly set, set it to the first window in the list + if (current < 0) + current = list[0].second; +} + +} /*namespace ADDON*/ -- cgit v1.2.3