[Windows] Fix driver version detection of AMD RDNA+ GPU on Windows 10
[xbmc.git] / xbmc / addons / Skin.cpp
blob746800776ce8f57a9e25469342acab40d342b3c0
1 /*
2 * Copyright (C) 2005-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
9 #include "Skin.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "ServiceBroker.h"
14 #include "Util.h"
15 #include "addons/addoninfo/AddonType.h"
16 #include "dialogs/GUIDialogKaiToast.h"
17 #include "filesystem/Directory.h"
18 #include "filesystem/SpecialProtocol.h"
19 #include "guilib/GUIComponent.h"
20 #include "guilib/GUIWindowManager.h"
21 #include "guilib/LocalizeStrings.h"
22 #include "guilib/WindowIDs.h"
23 #include "messaging/ApplicationMessenger.h"
24 #include "messaging/helpers/DialogHelper.h"
25 #include "settings/Settings.h"
26 #include "settings/SettingsComponent.h"
27 #include "settings/lib/Setting.h"
28 #include "settings/lib/SettingDefinitions.h"
29 #include "threads/Timer.h"
30 #include "utils/FileUtils.h"
31 #include "utils/StringUtils.h"
32 #include "utils/URIUtils.h"
33 #include "utils/Variant.h"
34 #include "utils/XMLUtils.h"
35 #include "utils/log.h"
37 #include <charconv>
38 #include <memory>
40 #define XML_SETTINGS "settings"
41 #define XML_SETTING "setting"
42 #define XML_ATTR_TYPE "type"
43 #define XML_ATTR_NAME "name"
44 #define XML_ATTR_ID "id"
46 using namespace XFILE;
47 using namespace KODI::MESSAGING;
48 using namespace std::chrono_literals;
50 using KODI::MESSAGING::HELPERS::DialogResponse;
52 std::shared_ptr<ADDON::CSkinInfo> g_SkinInfo;
54 namespace
56 constexpr auto DELAY = 500ms;
59 namespace ADDON
62 class CSkinSettingUpdateHandler : private ITimerCallback
64 public:
65 CSkinSettingUpdateHandler(CAddon& addon)
66 : m_addon(addon), m_timer(this) {}
67 ~CSkinSettingUpdateHandler() override = default;
69 void OnTimeout() override;
70 void TriggerSave();
71 private:
72 CAddon &m_addon;
73 CTimer m_timer;
76 bool CSkinSetting::Serialize(TiXmlElement* parent) const
78 if (parent == nullptr)
79 return false;
81 TiXmlElement setting(XML_SETTING);
82 setting.SetAttribute(XML_ATTR_ID, name.c_str());
83 setting.SetAttribute(XML_ATTR_TYPE, GetType());
85 if (!SerializeSetting(&setting))
86 return false;
88 parent->InsertEndChild(setting);
90 return true;
93 bool CSkinSetting::Deserialize(const TiXmlElement* element)
95 if (element == nullptr)
96 return false;
98 name = XMLUtils::GetAttribute(element, XML_ATTR_ID);
100 // backwards compatibility for guisettings.xml
101 if (name.empty())
102 name = XMLUtils::GetAttribute(element, XML_ATTR_NAME);
104 return true;
107 bool CSkinSettingString::Deserialize(const TiXmlElement* element)
109 value.clear();
111 if (!CSkinSetting::Deserialize(element))
112 return false;
114 if (element->FirstChild() != nullptr)
115 value = element->FirstChild()->Value();
117 return true;
120 bool CSkinSettingString::SerializeSetting(TiXmlElement* element) const
122 if (element == nullptr)
123 return false;
125 TiXmlText xmlValue(value);
126 element->InsertEndChild(xmlValue);
128 return true;
131 bool CSkinSettingBool::Deserialize(const TiXmlElement* element)
133 value = false;
135 if (!CSkinSetting::Deserialize(element))
136 return false;
138 if (element->FirstChild() != nullptr)
139 value = StringUtils::EqualsNoCase(element->FirstChild()->ValueStr(), "true");
141 return true;
144 bool CSkinSettingBool::SerializeSetting(TiXmlElement* element) const
146 if (element == nullptr)
147 return false;
149 TiXmlText xmlValue(value ? "true" : "false");
150 element->InsertEndChild(xmlValue);
152 return true;
155 CSkinInfo::CSkinInfo(const AddonInfoPtr& addonInfo,
156 const RESOLUTION_INFO& resolution /* = RESOLUTION_INFO() */)
157 : CAddon(addonInfo, AddonType::SKIN),
158 m_defaultRes(resolution),
159 m_effectsSlowDown(1.f),
160 m_debugging(false)
162 m_settingsUpdateHandler = std::make_unique<CSkinSettingUpdateHandler>(*this);
165 CSkinInfo::CSkinInfo(const AddonInfoPtr& addonInfo) : CAddon(addonInfo, AddonType::SKIN)
167 for (const auto& values : Type(AddonType::SKIN)->GetValues())
169 if (values.first != "res")
170 continue;
172 int width = values.second.GetValue("res@width").asInteger();
173 int height = values.second.GetValue("res@height").asInteger();
174 bool defRes = values.second.GetValue("res@default").asBoolean();
175 std::string folder = values.second.GetValue("res@folder").asString();
176 std::string strAspect = values.second.GetValue("res@aspect").asString();
177 float aspect = 0;
179 std::vector<std::string> fracs = StringUtils::Split(strAspect, ':');
180 if (fracs.size() == 2)
181 aspect = (float)(atof(fracs[0].c_str()) / atof(fracs[1].c_str()));
182 if (width > 0 && height > 0)
184 RESOLUTION_INFO res(width, height, aspect, folder);
185 res.strId = strAspect; // for skin usage, store aspect string in strId
186 if (defRes)
187 m_defaultRes = res;
188 m_resolutions.push_back(res);
192 m_effectsSlowDown = Type(AddonType::SKIN)->GetValue("@effectslowdown").asFloat();
193 if (m_effectsSlowDown == 0.0f)
194 m_effectsSlowDown = 1.f;
196 m_debugging = Type(AddonType::SKIN)->GetValue("@debugging").asBoolean();
198 m_settingsUpdateHandler = std::make_unique<CSkinSettingUpdateHandler>(*this);
199 LoadStartupWindows(addonInfo);
202 CSkinInfo::~CSkinInfo() = default;
204 struct closestRes
206 explicit closestRes(const RESOLUTION_INFO &target) : m_target(target) { };
207 bool operator()(const RESOLUTION_INFO &i, const RESOLUTION_INFO &j)
209 float diff = fabs(i.DisplayRatio() - m_target.DisplayRatio()) - fabs(j.DisplayRatio() - m_target.DisplayRatio());
210 if (diff < 0) return true;
211 if (diff > 0) return false;
212 diff = fabs((float)i.iHeight - m_target.iHeight) - fabs((float)j.iHeight - m_target.iHeight);
213 if (diff < 0) return true;
214 if (diff > 0) return false;
215 return fabs((float)i.iWidth - m_target.iWidth) < fabs((float)j.iWidth - m_target.iWidth);
217 RESOLUTION_INFO m_target;
220 void CSkinInfo::Start()
222 if (!LoadUserSettings())
223 CLog::Log(LOGWARNING, "CSkinInfo: failed to load skin settings");
225 if (!m_resolutions.size())
226 { // try falling back to whatever resolutions exist in the directory
227 CFileItemList items;
228 CDirectory::GetDirectory(Path(), items, "", DIR_FLAG_NO_FILE_DIRS);
229 for (int i = 0; i < items.Size(); i++)
231 RESOLUTION_INFO res;
232 if (items[i]->m_bIsFolder && TranslateResolution(items[i]->GetLabel(), res))
233 m_resolutions.push_back(res);
237 if (!m_resolutions.empty())
239 // find the closest resolution
240 const RESOLUTION_INFO &target = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
241 RESOLUTION_INFO& res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target));
242 m_currentAspect = res.strId;
246 std::string CSkinInfo::GetSkinPath(const std::string& strFile, RESOLUTION_INFO *res, const std::string& strBaseDir /* = "" */) const
248 if (m_resolutions.empty())
249 return ""; // invalid skin
251 std::string strPathToUse = Path();
252 if (!strBaseDir.empty())
253 strPathToUse = strBaseDir;
255 // if the caller doesn't care about the resolution just use a temporary
256 RESOLUTION_INFO tempRes;
257 if (!res)
258 res = &tempRes;
260 // find the closest resolution
261 const RESOLUTION_INFO &target = CServiceBroker::GetWinSystem()->GetGfxContext().GetResInfo();
262 *res = *std::min_element(m_resolutions.begin(), m_resolutions.end(), closestRes(target));
264 std::string strPath = URIUtils::AddFileToFolder(strPathToUse, res->strMode, strFile);
265 if (CFileUtils::Exists(strPath))
266 return strPath;
268 // use the default resolution
269 *res = m_defaultRes;
271 return URIUtils::AddFileToFolder(strPathToUse, res->strMode, strFile);
274 bool CSkinInfo::HasSkinFile(const std::string &strFile) const
276 return CFileUtils::Exists(GetSkinPath(strFile));
279 void CSkinInfo::LoadIncludes()
281 std::string includesPath =
282 CSpecialProtocol::TranslatePathConvertCase(GetSkinPath("Includes.xml"));
283 CLog::Log(LOGINFO, "Loading skin includes from {}", includesPath);
284 m_includes.Clear();
285 m_includes.Load(includesPath);
288 void CSkinInfo::LoadTimers()
290 m_skinTimerManager =
291 std::make_unique<CSkinTimerManager>(CServiceBroker::GetGUI()->GetInfoManager());
292 const std::string timersPath =
293 CSpecialProtocol::TranslatePathConvertCase(GetSkinPath("Timers.xml"));
294 CLog::LogF(LOGINFO, "Trying to load skin timers from {}", timersPath);
295 m_skinTimerManager->LoadTimers(timersPath);
298 void CSkinInfo::ProcessTimers()
300 m_skinTimerManager->Process();
302 void CSkinInfo::ResolveIncludes(TiXmlElement* node,
303 std::map<INFO::InfoPtr, bool>* xmlIncludeConditions /* = nullptr */)
305 if(xmlIncludeConditions)
306 xmlIncludeConditions->clear();
308 m_includes.Resolve(node, xmlIncludeConditions);
311 int CSkinInfo::GetStartWindow() const
313 int windowID = CServiceBroker::GetSettingsComponent()->GetSettings()->GetInt(CSettings::SETTING_LOOKANDFEEL_STARTUPWINDOW);
314 assert(m_startupWindows.size());
315 for (std::vector<CStartupWindow>::const_iterator it = m_startupWindows.begin(); it != m_startupWindows.end(); ++it)
317 if (windowID == (*it).m_id)
318 return windowID;
320 // return our first one
321 return m_startupWindows[0].m_id;
324 bool CSkinInfo::LoadStartupWindows(const AddonInfoPtr& addonInfo)
326 m_startupWindows.clear();
327 m_startupWindows.emplace_back(WINDOW_HOME, "513");
328 m_startupWindows.emplace_back(WINDOW_TV_CHANNELS, "19180");
329 m_startupWindows.emplace_back(WINDOW_TV_GUIDE, "19273");
330 m_startupWindows.emplace_back(WINDOW_RADIO_CHANNELS, "19183");
331 m_startupWindows.emplace_back(WINDOW_RADIO_GUIDE, "19274");
332 m_startupWindows.emplace_back(WINDOW_PROGRAMS, "0");
333 m_startupWindows.emplace_back(WINDOW_PICTURES, "1");
334 m_startupWindows.emplace_back(WINDOW_MUSIC_NAV, "2");
335 m_startupWindows.emplace_back(WINDOW_VIDEO_NAV, "3");
336 m_startupWindows.emplace_back(WINDOW_FILES, "7");
337 m_startupWindows.emplace_back(WINDOW_SETTINGS_MENU, "5");
338 m_startupWindows.emplace_back(WINDOW_WEATHER, "8");
339 m_startupWindows.emplace_back(WINDOW_FAVOURITES, "1036");
340 return true;
343 void CSkinInfo::GetSkinPaths(std::vector<std::string> &paths) const
345 RESOLUTION_INFO res;
346 GetSkinPath("Home.xml", &res);
347 if (!res.strMode.empty())
348 paths.push_back(URIUtils::AddFileToFolder(Path(), res.strMode));
349 if (res.strMode != m_defaultRes.strMode)
350 paths.push_back(URIUtils::AddFileToFolder(Path(), m_defaultRes.strMode));
353 bool CSkinInfo::TranslateResolution(const std::string &name, RESOLUTION_INFO &res)
355 std::string lower(name); StringUtils::ToLower(lower);
356 if (lower == "pal")
357 res = RESOLUTION_INFO(720, 576, 4.0f/3, "pal");
358 else if (lower == "pal16x9")
359 res = RESOLUTION_INFO(720, 576, 16.0f/9, "pal16x9");
360 else if (lower == "ntsc")
361 res = RESOLUTION_INFO(720, 480, 4.0f/3, "ntsc");
362 else if (lower == "ntsc16x9")
363 res = RESOLUTION_INFO(720, 480, 16.0f/9, "ntsc16x9");
364 else if (lower == "720p")
365 res = RESOLUTION_INFO(1280, 720, 0, "720p");
366 else if (lower == "1080i")
367 res = RESOLUTION_INFO(1920, 1080, 0, "1080i");
368 else
369 return false;
370 return true;
373 int CSkinInfo::GetFirstWindow() const
375 int startWindow = GetStartWindow();
376 if (HasSkinFile("Startup.xml"))
377 startWindow = WINDOW_STARTUP_ANIM;
378 return startWindow;
381 bool CSkinInfo::IsInUse() const
383 // Could extend this to prompt for reverting to the standard skin perhaps
384 return CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN) == ID();
387 const INFO::CSkinVariableString* CSkinInfo::CreateSkinVariable(const std::string& name, int context)
389 return m_includes.CreateSkinVariable(name, context);
392 void CSkinInfo::OnPreInstall()
394 bool skinLoaded = g_SkinInfo != nullptr;
395 if (IsInUse() && skinLoaded)
396 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
397 "UnloadSkin");
400 void CSkinInfo::OnPostInstall(bool update, bool modal)
402 if (!g_SkinInfo)
403 return;
405 if (IsInUse() || (!update && !modal &&
406 HELPERS::ShowYesNoDialogText(CVariant{Name()}, CVariant{24099}) ==
407 DialogResponse::CHOICE_YES))
409 CGUIDialogKaiToast *toast = CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogKaiToast>(WINDOW_DIALOG_KAI_TOAST);
410 if (toast)
412 toast->ResetTimer();
413 toast->Close(true);
415 if (CServiceBroker::GetSettingsComponent()->GetSettings()->GetString(CSettings::SETTING_LOOKANDFEEL_SKIN) == ID())
416 CServiceBroker::GetAppMessenger()->PostMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr,
417 "ReloadSkin");
418 else
419 CServiceBroker::GetSettingsComponent()->GetSettings()->SetString(CSettings::SETTING_LOOKANDFEEL_SKIN, ID());
423 void CSkinInfo::Unload()
425 m_skinTimerManager->Stop();
428 bool CSkinInfo::TimerIsRunning(const std::string& timer) const
430 return m_skinTimerManager->TimerIsRunning(timer);
433 float CSkinInfo::GetTimerElapsedSeconds(const std::string& timer) const
435 return m_skinTimerManager->GetTimerElapsedSeconds(timer);
438 void CSkinInfo::TimerStart(const std::string& timer) const
440 m_skinTimerManager->TimerStart(timer);
443 void CSkinInfo::TimerStop(const std::string& timer) const
445 m_skinTimerManager->TimerStop(timer);
448 void CSkinInfo::SettingOptionsSkinColorsFiller(const SettingConstPtr& setting,
449 std::vector<StringSettingOption>& list,
450 std::string& current,
451 void* data)
453 if (!g_SkinInfo)
454 return;
456 std::string settingValue = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
457 // Remove the .xml extension from the Themes
458 if (URIUtils::HasExtension(settingValue, ".xml"))
459 URIUtils::RemoveExtension(settingValue);
460 current = "SKINDEFAULT";
462 // There is a default theme (just defaults.xml)
463 // any other *.xml files are additional color themes on top of this one.
465 // add the default label
466 list.emplace_back(g_localizeStrings.Get(15109), "SKINDEFAULT"); // the standard defaults.xml will be used!
468 // Search for colors in the Current skin!
469 std::vector<std::string> vecColors;
470 std::string strPath = URIUtils::AddFileToFolder(g_SkinInfo->Path(), "colors");
472 CFileItemList items;
473 CDirectory::GetDirectory(CSpecialProtocol::TranslatePathConvertCase(strPath), items, ".xml", DIR_FLAG_DEFAULTS);
474 // Search for Themes in the Current skin!
475 for (int i = 0; i < items.Size(); ++i)
477 CFileItemPtr pItem = items[i];
478 if (!pItem->m_bIsFolder && !StringUtils::EqualsNoCase(pItem->GetLabel(), "defaults.xml"))
479 { // not the default one
480 vecColors.push_back(pItem->GetLabel().substr(0, pItem->GetLabel().size() - 4));
483 sort(vecColors.begin(), vecColors.end(), sortstringbyname());
484 for (int i = 0; i < (int) vecColors.size(); ++i)
485 list.emplace_back(vecColors[i], vecColors[i]);
487 // try to find the best matching value
488 for (const auto& elem : list)
490 if (StringUtils::EqualsNoCase(elem.value, settingValue))
491 current = settingValue;
495 namespace
497 void GetFontsetsFromFile(const std::string& fontsetFilePath,
498 std::vector<StringSettingOption>& list,
499 const std::string& settingValue,
500 bool* currentValueSet)
502 CXBMCTinyXML xmlDoc;
503 if (xmlDoc.LoadFile(fontsetFilePath))
505 TiXmlElement* rootElement = xmlDoc.RootElement();
506 g_SkinInfo->ResolveIncludes(rootElement);
507 if (rootElement && (rootElement->ValueStr() == "fonts"))
509 const TiXmlElement* fontsetElement = rootElement->FirstChildElement("fontset");
510 while (fontsetElement)
512 const char* idAttr = fontsetElement->Attribute("id");
513 const char* idLocAttr = fontsetElement->Attribute("idloc");
514 if (idAttr)
516 if (idLocAttr)
517 list.emplace_back(g_localizeStrings.Get(atoi(idLocAttr)), idAttr);
518 else
519 list.emplace_back(idAttr, idAttr);
521 if (StringUtils::EqualsNoCase(idAttr, settingValue))
522 *currentValueSet = true;
524 fontsetElement = fontsetElement->NextSiblingElement("fontset");
529 } // unnamed namespace
531 void CSkinInfo::SettingOptionsSkinFontsFiller(const SettingConstPtr& setting,
532 std::vector<StringSettingOption>& list,
533 std::string& current,
534 void* data)
536 if (!g_SkinInfo)
537 return;
539 const std::string settingValue =
540 std::static_pointer_cast<const CSettingString>(setting)->GetValue();
541 bool currentValueSet = false;
543 // Look for fontsets that are defined in the skin's Font.xml file
544 const std::string fontsetFilePath = g_SkinInfo->GetSkinPath("Font.xml");
545 GetFontsetsFromFile(fontsetFilePath, list, settingValue, &currentValueSet);
547 // Look for additional fontsets that are defined in .xml files in the skin's fonts directory
548 CFileItemList xmlFileItems;
549 CDirectory::GetDirectory(CSpecialProtocol::TranslatePath("special://skin/fonts"), xmlFileItems,
550 ".xml", DIR_FLAG_DEFAULTS);
551 for (int i = 0; i < xmlFileItems.Size(); i++)
552 GetFontsetsFromFile(xmlFileItems[i]->GetPath(), list, settingValue, &currentValueSet);
554 if (list.empty())
555 { // Since no fontset is defined, there is no selection of a fontset, so disable the component
556 CLog::LogF(LOGERROR, "No fontsets found");
557 list.emplace_back(g_localizeStrings.Get(13278), "");
558 current = "";
559 currentValueSet = true;
562 if (!currentValueSet)
563 current = list[0].value;
566 void CSkinInfo::SettingOptionsSkinThemesFiller(const SettingConstPtr& setting,
567 std::vector<StringSettingOption>& list,
568 std::string& current,
569 void* data)
571 // get the chosen theme and remove the extension from the current theme (backward compat)
572 std::string settingValue = std::static_pointer_cast<const CSettingString>(setting)->GetValue();
573 URIUtils::RemoveExtension(settingValue);
574 current = "SKINDEFAULT";
576 // there is a default theme (just Textures.xbt)
577 // any other *.xbt files are additional themes on top of this one.
579 // add the default Label
580 list.emplace_back(g_localizeStrings.Get(15109), "SKINDEFAULT"); // the standard Textures.xbt will be used
582 // search for themes in the current skin!
583 std::vector<std::string> vecTheme;
584 CUtil::GetSkinThemes(vecTheme);
586 // sort the themes for GUI and list them
587 for (int i = 0; i < (int) vecTheme.size(); ++i)
588 list.emplace_back(vecTheme[i], vecTheme[i]);
590 // try to find the best matching value
591 for (const auto& elem : list)
593 if (StringUtils::EqualsNoCase(elem.value, settingValue))
594 current = settingValue;
598 void CSkinInfo::SettingOptionsStartupWindowsFiller(const SettingConstPtr& setting,
599 std::vector<IntegerSettingOption>& list,
600 int& current,
601 void* data)
603 if (!g_SkinInfo)
604 return;
606 int settingValue = std::static_pointer_cast<const CSettingInt>(setting)->GetValue();
607 current = -1;
609 const std::vector<CStartupWindow> &startupWindows = g_SkinInfo->GetStartupWindows();
611 for (std::vector<CStartupWindow>::const_iterator it = startupWindows.begin(); it != startupWindows.end(); ++it)
613 std::string windowName = it->m_name;
614 if (StringUtils::IsNaturalNumber(windowName))
615 windowName = g_localizeStrings.Get(atoi(windowName.c_str()));
616 int windowID = it->m_id;
618 list.emplace_back(windowName, windowID);
620 if (settingValue == windowID)
621 current = settingValue;
624 // if the current value hasn't been properly set, set it to the first window in the list
625 if (current < 0)
626 current = list[0].value;
629 void CSkinInfo::ToggleDebug()
631 m_debugging = !m_debugging;
634 int CSkinInfo::TranslateString(const std::string &setting)
636 // run through and see if we have this setting
637 for (const auto& it : m_strings)
639 if (StringUtils::EqualsNoCase(setting, it.second->name))
640 return it.first;
643 // didn't find it - insert it
644 CSkinSettingStringPtr skinString(new CSkinSettingString());
645 skinString->name = setting;
647 int number = m_bools.size() + m_strings.size();
648 m_strings.insert(std::pair<int, CSkinSettingStringPtr>(number, skinString));
650 return number;
653 int CSkinInfo::GetInt(int setting) const
655 const std::string settingValue = GetString(setting);
656 if (settingValue.empty())
658 return -1;
660 int settingValueInt{-1};
661 std::from_chars(settingValue.data(), settingValue.data() + settingValue.size(), settingValueInt);
662 return settingValueInt;
665 const std::string& CSkinInfo::GetString(int setting) const
667 const auto& it = m_strings.find(setting);
668 if (it != m_strings.end())
669 return it->second->value;
671 return StringUtils::Empty;
674 void CSkinInfo::SetString(int setting, const std::string &label)
676 auto&& it = m_strings.find(setting);
677 if (it != m_strings.end())
679 it->second->value = label;
680 m_settingsUpdateHandler->TriggerSave();
681 return;
684 CLog::Log(LOGFATAL, "{}: unknown setting ({}) requested", __FUNCTION__, setting);
685 assert(false);
688 int CSkinInfo::TranslateBool(const std::string &setting)
690 // run through and see if we have this setting
691 for (const auto& it : m_bools)
693 if (StringUtils::EqualsNoCase(setting, it.second->name))
694 return it.first;
697 // didn't find it - insert it
698 CSkinSettingBoolPtr skinBool(new CSkinSettingBool());
699 skinBool->name = setting;
701 int number = m_bools.size() + m_strings.size();
702 m_bools.insert(std::pair<int, CSkinSettingBoolPtr>(number, skinBool));
703 m_settingsUpdateHandler->TriggerSave();
705 return number;
708 bool CSkinInfo::GetBool(int setting) const
710 const auto& it = m_bools.find(setting);
711 if (it != m_bools.end())
712 return it->second->value;
714 // default is to return false
715 return false;
718 void CSkinInfo::SetBool(int setting, bool set)
720 auto&& it = m_bools.find(setting);
721 if (it != m_bools.end())
723 it->second->value = set;
724 m_settingsUpdateHandler->TriggerSave();
725 return;
728 CLog::Log(LOGFATAL, "{}: unknown setting ({}) requested", __FUNCTION__, setting);
729 assert(false);
732 std::set<CSkinSettingPtr> CSkinInfo::GetSkinSettings() const
734 std::set<CSkinSettingPtr> settings;
736 for (const auto& setting : m_settings)
737 settings.insert(setting.second);
739 return settings;
742 CSkinSettingPtr CSkinInfo::GetSkinSetting(const std::string& settingId)
744 const auto& it = m_settings.find(settingId);
745 if (it != m_settings.end())
746 return it->second;
748 return nullptr;
751 std::shared_ptr<const CSkinSetting> CSkinInfo::GetSkinSetting(const std::string& settingId) const
753 const auto& it = m_settings.find(settingId);
754 if (it != m_settings.end())
755 return it->second;
757 return nullptr;
760 void CSkinInfo::Reset(const std::string &setting)
762 // run through and see if we have this setting as a string
763 for (auto& it : m_strings)
765 if (StringUtils::EqualsNoCase(setting, it.second->name))
767 it.second->value.clear();
768 m_settingsUpdateHandler->TriggerSave();
769 return;
773 // and now check for the skin bool
774 for (auto& it : m_bools)
776 if (StringUtils::EqualsNoCase(setting, it.second->name))
778 it.second->value = false;
779 m_settingsUpdateHandler->TriggerSave();
780 return;
785 void CSkinInfo::Reset()
787 // clear all the settings and strings from this skin.
788 for (auto& it : m_bools)
789 it.second->value = false;
791 for (auto& it : m_strings)
792 it.second->value.clear();
794 m_settingsUpdateHandler->TriggerSave();
797 std::set<CSkinSettingPtr> CSkinInfo::ParseSettings(const TiXmlElement* rootElement)
799 std::set<CSkinSettingPtr> settings;
800 if (rootElement == nullptr)
801 return settings;
803 const TiXmlElement *settingElement = rootElement->FirstChildElement(XML_SETTING);
804 while (settingElement != nullptr)
806 CSkinSettingPtr setting = ParseSetting(settingElement);
807 if (setting != nullptr)
808 settings.insert(setting);
810 settingElement = settingElement->NextSiblingElement(XML_SETTING);
813 return settings;
816 CSkinSettingPtr CSkinInfo::ParseSetting(const TiXmlElement* element)
818 if (element == nullptr)
819 return CSkinSettingPtr();
821 std::string settingType = XMLUtils::GetAttribute(element, XML_ATTR_TYPE);
822 CSkinSettingPtr setting;
823 if (settingType == "string")
824 setting = CSkinSettingPtr(new CSkinSettingString());
825 else if (settingType == "bool")
826 setting = CSkinSettingPtr(new CSkinSettingBool());
827 else
828 return CSkinSettingPtr();
830 if (setting == nullptr)
831 return CSkinSettingPtr();
833 if (!setting->Deserialize(element))
834 return CSkinSettingPtr();
836 return setting;
839 bool CSkinInfo::SettingsLoaded(AddonInstanceId id /* = ADDON_SETTINGS_ID */) const
841 if (id != ADDON_SETTINGS_ID)
842 return false;
844 return !m_strings.empty() || !m_bools.empty();
847 bool CSkinInfo::SettingsFromXML(const CXBMCTinyXML& doc,
848 bool loadDefaults,
849 AddonInstanceId id /* = ADDON_SETTINGS_ID */)
851 const TiXmlElement *rootElement = doc.RootElement();
852 if (rootElement == nullptr || rootElement->ValueStr().compare(XML_SETTINGS) != 0)
854 CLog::Log(LOGWARNING, "CSkinInfo: no <settings> tag found");
855 return false;
858 m_settings.clear();
859 m_strings.clear();
860 m_bools.clear();
862 int number = 0;
863 std::set<CSkinSettingPtr> settings = ParseSettings(rootElement);
864 for (const auto& setting : settings)
866 if (setting->GetType() == "string")
868 m_settings.insert(std::make_pair(setting->name, setting));
869 m_strings.insert(
870 std::make_pair(number++, std::dynamic_pointer_cast<CSkinSettingString>(setting)));
872 else if (setting->GetType() == "bool")
874 m_settings.insert(std::make_pair(setting->name, setting));
875 m_bools.insert(
876 std::make_pair(number++, std::dynamic_pointer_cast<CSkinSettingBool>(setting)));
878 else
879 CLog::Log(LOGWARNING, "CSkinInfo: ignoring setting of unknown type \"{}\"",
880 setting->GetType());
883 return true;
886 bool CSkinInfo::SettingsToXML(CXBMCTinyXML& doc, AddonInstanceId id /* = ADDON_SETTINGS_ID */) const
888 // add the <skinsettings> tag
889 TiXmlElement rootElement(XML_SETTINGS);
890 TiXmlNode *settingsNode = doc.InsertEndChild(rootElement);
891 if (settingsNode == nullptr)
893 CLog::Log(LOGWARNING, "CSkinInfo: could not create <settings> tag");
894 return false;
897 TiXmlElement* settingsElement = settingsNode->ToElement();
898 for (const auto& it : m_bools)
900 if (!it.second->Serialize(settingsElement))
901 CLog::Log(LOGWARNING, "CSkinInfo: failed to save string setting \"{}\"", it.second->name);
904 for (const auto& it : m_strings)
906 if (!it.second->Serialize(settingsElement))
907 CLog::Log(LOGWARNING, "CSkinInfo: failed to save bool setting \"{}\"", it.second->name);
910 return true;
913 void CSkinSettingUpdateHandler::OnTimeout()
915 m_addon.SaveSettings();
918 void CSkinSettingUpdateHandler::TriggerSave()
920 if (m_timer.IsRunning())
921 m_timer.Restart();
922 else
923 m_timer.Start(DELAY);
926 } /*namespace ADDON*/