Merge pull request #2022 from ronie/skin-startup
[vuplus_xbmc] / xbmc / addons / Skin.cpp
1 /*
2  *      Copyright (C) 2005-2013 Team XBMC
3  *      http://www.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 "Skin.h"
22 #include "AddonManager.h"
23 #include "filesystem/File.h"
24 #include "filesystem/SpecialProtocol.h"
25 #include "guilib/WindowIDs.h"
26 #include "utils/URIUtils.h"
27 #include "utils/log.h"
28 #include "utils/StringUtils.h"
29 #include "settings/GUISettings.h"
30
31 // fallback for new skin resolution code
32 #include "filesystem/Directory.h"
33
34 using namespace std;
35 using namespace XFILE;
36
37 #define SKIN_MIN_VERSION 2.1f
38
39 boost::shared_ptr<ADDON::CSkinInfo> g_SkinInfo;
40
41 namespace ADDON
42 {
43
44 CSkinInfo::CSkinInfo(const AddonProps &props, const RESOLUTION_INFO &resolution)
45   : CAddon(props), m_defaultRes(resolution)
46 {
47 }
48
49 CSkinInfo::CSkinInfo(const cp_extension_t *ext)
50   : CAddon(ext)
51 {
52   ELEMENTS elements;
53   if (CAddonMgr::Get().GetExtElements(ext->configuration, "res", elements))
54   {
55     for (ELEMENTS::iterator i = elements.begin(); i != elements.end(); ++i)
56     {
57       int width = atoi(CAddonMgr::Get().GetExtValue(*i, "@width"));
58       int height = atoi(CAddonMgr::Get().GetExtValue(*i, "@height"));
59       bool defRes = CAddonMgr::Get().GetExtValue(*i, "@default").Equals("true");
60       CStdString folder = CAddonMgr::Get().GetExtValue(*i, "@folder");
61       float aspect = 0;
62       CStdStringArray fracs;
63       CStdString strAspect = CAddonMgr::Get().GetExtValue(*i, "@aspect");
64       StringUtils::SplitString(strAspect, ":", fracs);
65       if (fracs.size() == 2)
66         aspect = (float)(atof(fracs[0].c_str())/atof(fracs[1].c_str()));
67       if (width > 0 && height > 0)
68       {
69         RESOLUTION_INFO res(width, height, aspect, folder);
70         res.strId = strAspect; // for skin usage, store aspect string in strId
71         if (defRes)
72           m_defaultRes = res;
73         m_resolutions.push_back(res);
74       }
75     }
76   }
77   else
78   { // no resolutions specified -> backward compatibility
79     CStdString defaultWide = CAddonMgr::Get().GetExtValue(ext->configuration, "@defaultwideresolution");
80     if (defaultWide.IsEmpty())
81       defaultWide = CAddonMgr::Get().GetExtValue(ext->configuration, "@defaultresolution");
82     TranslateResolution(defaultWide, m_defaultRes);
83   }
84
85   CStdString str = CAddonMgr::Get().GetExtValue(ext->configuration, "@effectslowdown");
86   if (!str.IsEmpty())
87     m_effectsSlowDown = (float)atof(str.c_str());
88   else
89     m_effectsSlowDown = 1.f;
90
91   str = CAddonMgr::Get().GetExtValue(ext->configuration, "@debugging");
92   m_debugging = !strcmp(str.c_str(), "true");
93
94   LoadStartupWindows(ext);
95   m_Version = 2.11;
96 }
97
98 CSkinInfo::~CSkinInfo()
99 {
100 }
101
102 struct closestRes
103 {
104   closestRes(const RESOLUTION_INFO &target) : m_target(target) { };
105   bool operator()(const RESOLUTION_INFO &i, const RESOLUTION_INFO &j)
106   {
107     float diff = fabs(i.DisplayRatio() - m_target.DisplayRatio()) - fabs(j.DisplayRatio() - m_target.DisplayRatio());
108     if (diff < 0) return true;
109     if (diff > 0) return false;
110     diff = fabs((float)i.iHeight - m_target.iHeight) - fabs((float)j.iHeight - m_target.iHeight);
111     if (diff < 0) return true;
112     if (diff > 0) return false;
113     return fabs((float)i.iWidth - m_target.iWidth) < fabs((float)j.iWidth - m_target.iWidth);
114   }
115   RESOLUTION_INFO m_target;
116 };
117
118 void CSkinInfo::Start()
119 {
120   if (!m_resolutions.size())
121   { // try falling back to whatever resolutions exist in the directory
122     CFileItemList items;
123     CDirectory::GetDirectory(Path(), items, "", DIR_FLAG_NO_FILE_DIRS);
124     for (int i = 0; i < items.Size(); i++)
125     {
126       RESOLUTION_INFO res;
127       if (items[i]->m_bIsFolder && TranslateResolution(items[i]->GetLabel(), res))
128         m_resolutions.push_back(res);
129     }
130   }
131
132   if (!m_resolutions.empty())
133   {
134     // find the closest resolution
135     const RESOLUTION_INFO &target = g_graphicsContext.GetResInfo();
136     RESOLUTION_INFO& res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target));
137     m_currentAspect = res.strId;
138   }
139 }
140
141 CStdString CSkinInfo::GetSkinPath(const CStdString& strFile, RESOLUTION_INFO *res, const CStdString& strBaseDir /* = "" */) const
142 {
143   if (m_resolutions.empty())
144     return ""; // invalid skin
145
146   CStdString strPathToUse = Path();
147   if (!strBaseDir.IsEmpty())
148     strPathToUse = strBaseDir;
149
150   // if the caller doesn't care about the resolution just use a temporary
151   RESOLUTION_INFO tempRes;
152   if (!res)
153     res = &tempRes;
154
155   // find the closest resolution
156   const RESOLUTION_INFO &target = g_graphicsContext.GetResInfo();
157   *res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target));
158
159   CStdString strPath = URIUtils::AddFileToFolder(strPathToUse, res->strMode);
160   strPath = URIUtils::AddFileToFolder(strPath, strFile);
161   if (CFile::Exists(strPath))
162     return strPath;
163
164   // use the default resolution
165   *res = m_defaultRes;
166
167   strPath = URIUtils::AddFileToFolder(strPathToUse, res->strMode);
168   strPath = URIUtils::AddFileToFolder(strPath, strFile);
169   return strPath;
170 }
171
172 bool CSkinInfo::HasSkinFile(const CStdString &strFile) const
173 {
174   return CFile::Exists(GetSkinPath(strFile));
175 }
176
177 double CSkinInfo::GetMinVersion()
178 {
179   return SKIN_MIN_VERSION;
180 }
181
182 void CSkinInfo::LoadIncludes()
183 {
184   CStdString includesPath = CSpecialProtocol::TranslatePathConvertCase(GetSkinPath("includes.xml"));
185   CLog::Log(LOGINFO, "Loading skin includes from %s", includesPath.c_str());
186   m_includes.ClearIncludes();
187   m_includes.LoadIncludes(includesPath);
188 }
189
190 void CSkinInfo::ResolveIncludes(TiXmlElement *node, std::map<int, bool>* xmlIncludeConditions /* = NULL */)
191 {
192   if(xmlIncludeConditions)
193     xmlIncludeConditions->clear();
194
195   m_includes.ResolveIncludes(node, xmlIncludeConditions);
196 }
197
198 int CSkinInfo::GetStartWindow() const
199 {
200   int windowID = g_guiSettings.GetInt("lookandfeel.startupwindow");
201   assert(m_startupWindows.size());
202   for (vector<CStartupWindow>::const_iterator it = m_startupWindows.begin(); it != m_startupWindows.end(); it++)
203   {
204     if (windowID == (*it).m_id)
205       return windowID;
206   }
207   // return our first one
208   return m_startupWindows[0].m_id;
209 }
210
211 bool CSkinInfo::LoadStartupWindows(const cp_extension_t *ext)
212 {
213   m_startupWindows.clear();
214   m_startupWindows.push_back(CStartupWindow(WINDOW_HOME, "513"));
215   m_startupWindows.push_back(CStartupWindow(WINDOW_PVR, "19180"));
216   m_startupWindows.push_back(CStartupWindow(WINDOW_PROGRAMS, "0"));
217   m_startupWindows.push_back(CStartupWindow(WINDOW_PICTURES, "1"));
218   m_startupWindows.push_back(CStartupWindow(WINDOW_MUSIC, "2"));
219   m_startupWindows.push_back(CStartupWindow(WINDOW_VIDEOS, "3"));
220   m_startupWindows.push_back(CStartupWindow(WINDOW_FILES, "7"));
221   m_startupWindows.push_back(CStartupWindow(WINDOW_SETTINGS_MENU, "5"));
222   m_startupWindows.push_back(CStartupWindow(WINDOW_WEATHER, "8"));
223   return true;
224 }
225
226 void CSkinInfo::GetSkinPaths(std::vector<CStdString> &paths) const
227 {
228   RESOLUTION_INFO res;
229   GetSkinPath("Home.xml", &res);
230   if (!res.strMode.empty())
231     paths.push_back(URIUtils::AddFileToFolder(Path(), res.strMode));
232   if (res.strMode != m_defaultRes.strMode)
233     paths.push_back(URIUtils::AddFileToFolder(Path(), m_defaultRes.strMode));
234 }
235
236 bool CSkinInfo::TranslateResolution(const CStdString &name, RESOLUTION_INFO &res)
237 {
238   if (name.Equals("pal"))
239     res = RESOLUTION_INFO(720, 576, 4.0f/3, "pal");
240   else if (name.Equals("pal16x9"))
241     res = RESOLUTION_INFO(720, 576, 16.0f/9, "pal16x9");
242   else if (name.Equals("ntsc"))
243     res = RESOLUTION_INFO(720, 480, 4.0f/3, "ntsc");
244   else if (name.Equals("ntsc16x9"))
245     res = RESOLUTION_INFO(720, 480, 16.0f/9, "ntsc16x9");
246   else if (name.Equals("720p"))
247     res = RESOLUTION_INFO(1280, 720, 0, "720p");
248   else if (name.Equals("1080i"))
249     res = RESOLUTION_INFO(1920, 1080, 0, "1080i");
250   else
251     return false;
252   return true;
253 }
254
255 int CSkinInfo::GetFirstWindow() const
256 {
257   int startWindow = GetStartWindow();
258   if (HasSkinFile("Startup.xml"))
259     startWindow = WINDOW_STARTUP_ANIM;
260   return startWindow;
261 }
262
263 bool CSkinInfo::IsInUse() const
264 {
265   // Could extend this to prompt for reverting to the standard skin perhaps
266   return g_guiSettings.GetString("lookandfeel.skin") == ID();
267 }
268
269 const INFO::CSkinVariableString* CSkinInfo::CreateSkinVariable(const CStdString& name, int context)
270 {
271   return m_includes.CreateSkinVariable(name, context);
272 }
273
274 } /*namespace ADDON*/