Merge pull request #26117 from notspiff/infoscanner_inforet_enum_class
[xbmc.git] / xbmc / addons / settings / AddonSettings.cpp
blobb3bd9bb1245d649553f4264ed8b45f25e7d96d92
1 /*
2 * Copyright (C) 2017-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 "AddonSettings.h"
11 #include "FileItem.h"
12 #include "FileItemList.h"
13 #include "GUIInfoManager.h"
14 #include "LangInfo.h"
15 #include "ServiceBroker.h"
16 #include "addons/addoninfo/AddonInfo.h"
17 #include "addons/addoninfo/AddonType.h"
18 #include "addons/binary-addons/AddonInstanceHandler.h"
19 #include "addons/gui/GUIDialogAddonSettings.h"
20 #include "addons/settings/SettingUrlEncodedString.h"
21 #include "filesystem/Directory.h"
22 #include "guilib/GUIComponent.h"
23 #include "guilib/LocalizeStrings.h"
24 #include "messaging/ApplicationMessenger.h"
25 #include "settings/SettingAddon.h"
26 #include "settings/SettingConditions.h"
27 #include "settings/SettingControl.h"
28 #include "settings/SettingDateTime.h"
29 #include "settings/SettingPath.h"
30 #include "settings/lib/Setting.h"
31 #include "settings/lib/SettingDefinitions.h"
32 #include "settings/lib/SettingSection.h"
33 #include "settings/lib/SettingsManager.h"
34 #include "utils/FileExtensionProvider.h"
35 #include "utils/StringUtils.h"
36 #include "utils/URIUtils.h"
37 #include "utils/XBMCTinyXML.h"
38 #include "utils/XMLUtils.h"
39 #include "utils/log.h"
41 #include <algorithm>
42 #include <cassert>
43 #include <memory>
44 #include <mutex>
45 #include <vector>
47 namespace
50 constexpr auto OldSettingValuesSeparator = "|";
52 constexpr int UnknownSettingLabelIdStart = 100000;
54 bool InfoBool(const std::string& condition,
55 const std::string& value,
56 const SettingConstPtr& setting,
57 void* data)
59 return CServiceBroker::GetGUI()->GetInfoManager().EvaluateBool(value, INFO::DEFAULT_CONTEXT);
62 template<class TSetting>
63 SettingPtr InitializeFromOldSettingWithoutDefinition(ADDON::CAddonSettings& settings,
64 const std::string& settingId,
65 const typename TSetting::Value& defaultValue)
67 std::shared_ptr<TSetting> setting =
68 std::make_shared<TSetting>(settingId, settings.GetSettingsManager());
69 setting->SetLevel(SettingLevel::Internal);
70 setting->SetVisible(false);
71 setting->SetDefault(defaultValue);
73 return setting;
76 template<>
77 SettingPtr InitializeFromOldSettingWithoutDefinition<CSettingString>(
78 ADDON::CAddonSettings& settings,
79 const std::string& settingId,
80 const typename CSettingString::Value& defaultValue)
82 std::shared_ptr<CSettingString> setting =
83 std::make_shared<CSettingString>(settingId, settings.GetSettingsManager());
84 setting->SetLevel(SettingLevel::Internal);
85 setting->SetVisible(false);
86 setting->SetDefault(defaultValue);
87 setting->SetAllowEmpty(true);
89 return setting;
92 template<class TSetting>
93 SettingPtr AddSettingWithoutDefinition(ADDON::CAddonSettings& settings,
94 const std::string& settingId,
95 typename TSetting::Value defaultValue,
96 const Logger& logger)
98 if (settingId.empty())
99 return nullptr;
101 // if necessary try to initialize the settings manager on-the-fly without any definitions
102 if (!settings.IsInitialized() && !settings.Initialize(CXBMCTinyXML(), true))
104 logger->warn("failed to initialize settings on-the-fly");
105 return nullptr;
108 // check if we need to add a section on-the-fly
109 auto sections = settings.GetSettingsManager()->GetSections();
110 SettingSectionPtr section;
111 if (sections.empty())
112 section =
113 std::make_shared<CSettingSection>(settings.GetAddonId(), settings.GetSettingsManager());
114 else
115 section = sections.back();
117 // check if we need to add a category on-the-fly
118 auto categories = section->GetCategories();
119 SettingCategoryPtr category;
120 if (categories.empty())
121 category = std::make_shared<CSettingCategory>("category0", settings.GetSettingsManager());
122 else
123 category = categories.back();
125 // check if we need to add a group on-the-fly
126 auto groups = category->GetGroups();
127 SettingGroupPtr group;
128 if (groups.empty())
129 group = std::make_shared<CSettingGroup>("0", settings.GetSettingsManager());
130 else
131 group = groups.back();
133 // create a new setting on-the-fly
134 auto setting =
135 InitializeFromOldSettingWithoutDefinition<TSetting>(settings, settingId, defaultValue);
136 if (setting == nullptr)
138 logger->warn("failed to create setting \"{}\" on-the-fly", settingId);
139 return nullptr;
142 // add the setting (and if necessary the section, category and/or group)
143 if (!settings.GetSettingsManager()->AddSetting(setting, section, category, group))
145 logger->warn("failed to add setting \"{}\" on-the-fly", settingId);
146 return nullptr;
149 return setting;
152 } // namespace
154 namespace ADDON
157 CAddonSettings::CAddonSettings(const std::shared_ptr<IAddon>& addon, AddonInstanceId instanceId)
158 : CSettingsBase(),
159 m_addonId(addon->ID()),
160 m_addonPath(addon->Path()),
161 m_addonProfile(addon->Profile()),
162 m_instanceId(instanceId),
163 m_addon{addon},
164 m_unknownSettingLabelId(UnknownSettingLabelIdStart),
165 m_logger(CServiceBroker::GetLogging().GetLogger(
166 StringUtils::Format("CAddonSettings[{}@{}]", m_instanceId, m_addonId)))
170 std::shared_ptr<CSetting> CAddonSettings::CreateSetting(
171 const std::string& settingType,
172 const std::string& settingId,
173 CSettingsManager* settingsManager /* = nullptr */) const
175 if (StringUtils::EqualsNoCase(settingType, "urlencodedstring"))
176 return std::make_shared<CSettingUrlEncodedString>(settingId, settingsManager);
178 return CSettingCreator::CreateSetting(settingType, settingId, settingsManager);
181 void CAddonSettings::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
183 std::string actionData;
184 bool closeDialog = false;
186 // check if it's an action setting
187 if (setting->GetType() == SettingType::Action)
189 auto settingAction = std::dynamic_pointer_cast<const CSettingAction>(setting);
190 if (settingAction != nullptr && settingAction->HasData())
192 actionData = settingAction->GetData();
193 // replace $CWD with the url of the add-on
194 StringUtils::Replace(actionData, "$CWD", m_addonPath);
195 // replace $ID with the id of the add-on
196 StringUtils::Replace(actionData, "$ID", m_addonId);
200 // check if the setting control's is a button and its format is action
201 if (setting->GetControl()->GetType() == "button" &&
202 setting->GetControl()->GetFormat() == "action")
204 auto controlButton =
205 std::dynamic_pointer_cast<const CSettingControlButton>(setting->GetControl());
206 if (controlButton != nullptr)
208 if (actionData.empty() && controlButton->HasActionData())
209 actionData = controlButton->GetActionData();
211 closeDialog = controlButton->CloseDialog();
215 if (actionData.empty())
216 return;
218 if (closeDialog)
219 CGUIDialogAddonSettings::SaveAndClose();
221 CServiceBroker::GetAppMessenger()->SendMsg(TMSG_EXECUTE_BUILT_IN, -1, -1, nullptr, actionData);
224 bool CAddonSettings::AddInstanceSettings()
226 if (GetSetting(ADDON_SETTING_INSTANCE_NAME_VALUE) ||
227 GetSetting(ADDON_SETTING_INSTANCE_ENABLED_VALUE))
229 CLog::Log(
230 LOGDEBUG,
231 "CAddonSettings::{} - Add-on {} using instance setting values byself, Kodi's add ignored",
232 __func__, m_addonId);
233 return true;
236 auto mgr = GetSettingsManager();
237 if (!mgr)
238 return false;
240 auto sections = mgr->GetSections();
241 if (sections.empty())
242 return false;
244 SettingSectionPtr section = *sections.begin();
246 auto categories = section->GetCategories();
247 if (categories.empty())
248 return false;
250 SettingCategoryPtr category = *categories.begin();
252 auto groups = category->GetGroups();
253 auto itr = std::find_if(groups.begin(), groups.end(),
254 [](const SettingGroupPtr& group)
255 { return group->GetId() == ADDON_SETTING_INSTANCE_GROUP; });
257 SettingGroupPtr group;
258 if (itr != groups.end())
260 group = *itr;
262 else
264 group = std::make_shared<CSettingGroup>(ADDON_SETTING_INSTANCE_GROUP, mgr);
265 group->SetLabel(10017); // Add-on configuration
266 category->AddGroupToFront(group);
269 const std::shared_ptr<CSettingString> name =
270 std::make_shared<CSettingString>(ADDON_SETTING_INSTANCE_NAME_VALUE, 551, "", mgr); // Name
271 name->SetAllowEmpty(false);
272 name->SetControl(std::make_shared<CSettingControlEdit>());
273 if (!mgr->AddSetting(name, section, category, group))
274 return false;
276 const std::shared_ptr<CSettingBool> enabled = std::make_shared<CSettingBool>(
277 ADDON_SETTING_INSTANCE_ENABLED_VALUE, 305, true, mgr); // Enabled
278 enabled->SetControl(std::make_shared<CSettingControlCheckmark>());
279 if (!mgr->AddSetting(enabled, section, category, group))
280 return false;
282 return true;
285 bool CAddonSettings::Initialize(const CXBMCTinyXML& doc, bool allowEmpty /* = false */)
287 std::unique_lock<CCriticalSection> lock(m_critical);
288 if (m_initialized)
289 return false;
291 // register custom setting types
292 InitializeSettingTypes();
293 // register custom setting controls
294 InitializeControls();
296 // conditions need to be initialized before the setting definitions
297 InitializeConditions();
299 // load the settings definitions
300 if (!InitializeDefinitions(doc) && !allowEmpty)
301 return false;
303 // Add internal settings to set values about instance set
304 if (m_instanceId > 0 && !AddInstanceSettings())
305 return false;
307 GetSettingsManager()->SetInitialized();
309 m_initialized = true;
311 return true;
314 bool CAddonSettings::Load(const CXBMCTinyXML& doc)
316 std::unique_lock<CCriticalSection> lock(m_critical);
317 if (!m_initialized)
318 return false;
320 // figure out the version of the setting definitions
321 uint32_t version = 0;
322 if (!ParseSettingVersion(doc, version))
324 m_logger->error("failed to determine setting values version");
325 return false;
328 std::map<std::string, std::string> settingValues;
330 // for new/"normal" setting values use the standard process
331 if (version != 0)
333 bool updated;
334 if (!LoadValuesFromXml(doc, updated))
335 return false;
337 // helper lambda for parsing a setting's ID and value from XML
338 auto parseSettingValue = [&settingValues](const TiXmlNode* setting,
339 const std::string& categoryId = "") {
340 // put together the setting ID
341 auto settingId = categoryId;
342 if (!settingId.empty())
343 settingId += ".";
344 auto id = setting->ToElement()->Attribute("id");
345 if (id)
346 settingId += id;
348 // parse the setting value
349 std::string settingValue;
350 if (setting->FirstChild())
351 settingValue = setting->FirstChild()->ValueStr();
353 // add the setting to the map
354 settingValues.emplace(settingId, settingValue);
357 // check if there were any setting values without a definition
358 auto category = doc.RootElement()->FirstChild();
359 while (category != nullptr)
361 // check if this really is a category with setting elements
362 if (category->FirstChild() && category->FirstChild()->Type() == CXBMCTinyXML::TINYXML_ELEMENT)
364 const auto& categoryId = category->ValueStr();
365 auto setting = category->FirstChild();
366 while (setting != nullptr)
368 parseSettingValue(setting, categoryId);
370 setting = setting->NextSibling();
373 else
374 parseSettingValue(category);
376 category = category->NextSibling();
379 // for old setting values do it manually
380 else if (!LoadOldSettingValues(doc, settingValues))
382 m_logger->error("failed to determine setting values from old format");
383 return false;
386 // process all settings
387 for (const auto& setting : settingValues)
389 // ignore setting values without a setting identifier
390 if (setting.first.empty())
391 continue;
393 // try to find a matching setting
394 SettingPtr newSetting = GetSetting(setting.first);
395 if (newSetting == nullptr)
397 // create a hidden/internal string setting on-the-fly
398 newSetting = AddSettingWithoutDefinition<CSettingString>(*this, setting.first, setting.second,
399 m_logger);
402 // try to load the old setting value
403 if (!newSetting)
405 m_logger->error("had null newSetting for value \"{}\" for setting {}", setting.second,
406 setting.first);
408 else if (!newSetting->FromString(setting.second))
410 m_logger->warn("failed to load value \"{}\" for setting {}", setting.second, setting.first);
414 SetLoaded();
416 return true;
419 bool CAddonSettings::Save(CXBMCTinyXML& doc) const
421 std::unique_lock<CCriticalSection> lock(m_critical);
422 if (!m_initialized)
423 return false;
425 if (!SaveValuesToXml(doc))
427 m_logger->error("failed to save settings");
428 return false;
431 return true;
434 bool CAddonSettings::HasSettings() const
436 return IsInitialized() && GetSettingsManager()->HasSettings();
439 bool CAddonSettings::Save()
441 std::shared_ptr<IAddon> addon = m_addon.lock();
442 assert(addon);
443 if (addon)
444 return addon->SaveSettings();
445 else
446 return false;
449 std::string CAddonSettings::GetSettingLabel(int label) const
451 if (label < UnknownSettingLabelIdStart || label >= m_unknownSettingLabelId)
452 return "";
454 const auto labelIt = m_unknownSettingLabels.find(label);
455 if (labelIt == m_unknownSettingLabels.end())
456 return "";
458 return labelIt->second;
461 std::shared_ptr<CSetting> CAddonSettings::AddSetting(const std::string& settingId, bool value)
463 return AddSettingWithoutDefinition<CSettingBool>(*this, settingId, value, m_logger);
466 std::shared_ptr<CSetting> CAddonSettings::AddSetting(const std::string& settingId, int value)
468 return AddSettingWithoutDefinition<CSettingInt>(*this, settingId, value, m_logger);
471 std::shared_ptr<CSetting> CAddonSettings::AddSetting(const std::string& settingId, double value)
473 return AddSettingWithoutDefinition<CSettingNumber>(*this, settingId, value, m_logger);
476 std::shared_ptr<CSetting> CAddonSettings::AddSetting(const std::string& settingId,
477 const std::string& value)
479 return AddSettingWithoutDefinition<CSettingString>(*this, settingId, value, m_logger);
482 void CAddonSettings::InitializeSettingTypes()
484 GetSettingsManager()->RegisterSettingType("addon", this);
485 GetSettingsManager()->RegisterSettingType("date", this);
486 GetSettingsManager()->RegisterSettingType("path", this);
487 GetSettingsManager()->RegisterSettingType("time", this);
488 GetSettingsManager()->RegisterSettingType("urlencodedstring", this);
491 void CAddonSettings::InitializeControls()
493 GetSettingsManager()->RegisterSettingControl("toggle", this);
494 GetSettingsManager()->RegisterSettingControl("spinner", this);
495 GetSettingsManager()->RegisterSettingControl("edit", this);
496 GetSettingsManager()->RegisterSettingControl("button", this);
497 GetSettingsManager()->RegisterSettingControl("list", this);
498 GetSettingsManager()->RegisterSettingControl("slider", this);
499 GetSettingsManager()->RegisterSettingControl("range", this);
500 GetSettingsManager()->RegisterSettingControl("title", this);
501 GetSettingsManager()->RegisterSettingControl("colorbutton", this);
504 void CAddonSettings::InitializeConditions()
506 CSettingConditions::Initialize();
508 // add basic conditions
509 const std::set<std::string>& simpleConditions = CSettingConditions::GetSimpleConditions();
510 for (const auto& condition : simpleConditions)
511 GetSettingsManager()->AddCondition(condition);
513 GetSettingsManager()->AddDynamicCondition("InfoBool", InfoBool);
516 bool CAddonSettings::InitializeDefinitions(const CXBMCTinyXML& doc)
518 // figure out the version of the setting definitions
519 uint32_t version = 0;
520 if (!ParseSettingVersion(doc, version))
522 m_logger->error("failed to determine setting definitions version");
523 return false;
526 // for new/"normal" setting definitions use the standard process
527 if (version != 0)
528 return InitializeDefinitionsFromXml(doc);
530 // for old setting definitions do it manually
531 return InitializeFromOldSettingDefinitions(doc);
534 bool CAddonSettings::ParseSettingVersion(const CXBMCTinyXML& doc, uint32_t& version) const
536 const TiXmlElement* root = doc.RootElement();
537 if (root == nullptr)
538 return false;
540 if (!StringUtils::EqualsNoCase(root->ValueStr(), SETTING_XML_ROOT))
542 m_logger->error("error reading setting definitions: no <settings> tag");
543 return false;
546 version = GetSettingsManager()->ParseVersion(root);
547 return true;
550 std::shared_ptr<CSettingGroup> CAddonSettings::ParseOldSettingElement(
551 const TiXmlElement* categoryElement,
552 const std::shared_ptr<CSettingCategory>& category,
553 std::set<std::string>& settingIds)
555 // build a vector of settings from the same category
556 std::vector<std::shared_ptr<const CSetting>> categorySettings;
558 // prepare for settings with enable/visible conditions
559 struct SettingWithConditions
561 SettingPtr setting;
562 std::string enableCondition;
563 std::string visibleCondition;
564 SettingDependencies deps;
566 std::vector<SettingWithConditions> settingsWithConditions;
568 auto group = std::make_shared<CSettingGroup>("0", GetSettingsManager());
569 uint32_t groupId = 1;
571 // go through all settings in the category
572 const TiXmlElement* settingElement = categoryElement->FirstChildElement("setting");
573 while (settingElement != nullptr)
575 // read the possible attributes
576 const auto settingType = XMLUtils::GetAttribute(settingElement, "type");
577 const auto settingId = XMLUtils::GetAttribute(settingElement, "id");
578 const auto defaultValue = XMLUtils::GetAttribute(settingElement, "default");
579 const auto settingValues = XMLUtils::GetAttribute(settingElement, "values");
580 const auto settingLValues = StringUtils::Split(
581 XMLUtils::GetAttribute(settingElement, "lvalues"), OldSettingValuesSeparator);
582 int settingLabel = -1;
583 bool settingLabelParsed = ParseOldLabel(settingElement, settingId, settingLabel);
585 SettingPtr setting;
586 if (settingType == "sep" || settingType == "lsep")
588 // check if we need to create a new group
589 if (!group->GetSettings().empty())
591 // add the current group to the category
592 category->AddGroup(group);
594 // and create a new one
595 group = std::make_shared<CSettingGroup>(std::to_string(groupId), GetSettingsManager());
596 groupId += 1;
599 if (settingType == "lsep" && settingLabelParsed)
600 group->SetLabel(settingLabel);
602 else if (settingId.empty() || settingType == "action")
604 if (settingType == "action")
605 setting = InitializeFromOldSettingAction(settingId, settingElement, defaultValue);
606 else
607 setting = InitializeFromOldSettingLabel();
609 else if (settingType == "bool")
610 setting = InitializeFromOldSettingBool(settingId, settingElement, defaultValue);
611 else if (settingType == "text" || settingType == "ipaddress")
612 setting = InitializeFromOldSettingTextIpAddress(settingId, settingType, settingElement,
613 defaultValue, settingLabel);
614 else if (settingType == "number")
615 setting =
616 InitializeFromOldSettingNumber(settingId, settingElement, defaultValue, settingLabel);
617 else if (settingType == "video" || settingType == "audio" || settingType == "image" ||
618 settingType == "executable" || settingType == "file" || settingType == "folder")
619 setting = InitializeFromOldSettingPath(settingId, settingType, settingElement, defaultValue,
620 settingLabel);
621 else if (settingType == "date")
622 setting = InitializeFromOldSettingDate(settingId, settingElement, defaultValue, settingLabel);
623 else if (settingType == "time")
624 setting = InitializeFromOldSettingTime(settingId, settingElement, defaultValue, settingLabel);
625 else if (settingType == "select")
626 setting = InitializeFromOldSettingSelect(settingId, settingElement, defaultValue,
627 settingLabel, settingValues, settingLValues);
628 else if (settingType == "addon")
629 setting =
630 InitializeFromOldSettingAddon(settingId, settingElement, defaultValue, settingLabel);
631 else if (settingType == "enum" || settingType == "labelenum")
632 setting = InitializeFromOldSettingEnums(settingId, settingType, settingElement, defaultValue,
633 settingValues, settingLValues);
634 else if (settingType == "fileenum")
635 setting =
636 InitializeFromOldSettingFileEnum(settingId, settingElement, defaultValue, settingValues);
637 else if (settingType == "rangeofnum")
638 setting = InitializeFromOldSettingRangeOfNum(settingId, settingElement, defaultValue);
639 else if (settingType == "slider")
640 setting = InitializeFromOldSettingSlider(settingId, settingElement, defaultValue);
641 else if (settingType.empty())
643 // setting definitions without a type are considered as "text" / strings but are hidden
644 setting = InitializeFromOldSettingTextIpAddress(settingId, "text", settingElement,
645 defaultValue, settingLabel);
646 setting->SetLevel(SettingLevel::Internal);
648 else
650 m_logger->warn("failed to parse old setting definition for \"{}\" of type \"{}\"", settingId,
651 settingType);
654 // process general properties
655 if (setting != nullptr)
657 // set the default level to be Basic
658 if (setting->GetLevel() != SettingLevel::Internal)
660 setting->SetLevel(SettingLevel::Basic);
663 // use the setting's ID if there's no label
664 if (settingLabel < 0)
666 settingLabel = m_unknownSettingLabelId;
667 m_unknownSettingLabelId += 1;
669 m_unknownSettingLabels.emplace(settingLabel, settingId);
672 // set the setting's label
673 setting->SetLabel(settingLabel);
675 // handle subsettings
676 bool isSubsetting = false;
677 if (settingElement->QueryBoolAttribute("subsetting", &isSubsetting) == TIXML_SUCCESS &&
678 isSubsetting)
680 // find the last non-subsetting in the current group and use that as the parent setting
681 const auto groupSettings = group->GetSettings();
682 const auto parentSetting = std::find_if(
683 groupSettings.crbegin(), groupSettings.crend(),
684 [](const SettingConstPtr& setting) { return setting->GetParent().empty(); });
686 if (parentSetting != groupSettings.crend())
688 if ((*parentSetting)->IsReference())
689 setting->SetParent((*parentSetting)->GetReferencedId());
690 else
691 setting->SetParent((*parentSetting)->GetId());
695 SettingWithConditions settingWithConditions;
697 // parse enable status
698 const auto conditionEnable = XMLUtils::GetAttribute(settingElement, "enable");
699 if (StringUtils::EqualsNoCase(conditionEnable, "true"))
700 setting->SetEnabled(true);
701 else if (StringUtils::EqualsNoCase(conditionEnable, "false"))
702 setting->SetEnabled(false);
703 else if (!conditionEnable.empty())
704 settingWithConditions.enableCondition = conditionEnable;
706 // parse visible status
707 const auto conditionVisible = XMLUtils::GetAttribute(settingElement, "visible");
708 if (StringUtils::EqualsNoCase(conditionVisible, "true"))
709 setting->SetVisible(true);
710 else if (StringUtils::EqualsNoCase(conditionVisible, "false"))
711 setting->SetVisible(false);
712 else if (!conditionVisible.empty())
713 settingWithConditions.visibleCondition = conditionVisible;
715 // check if there already is a setting with the setting identifier
716 if (settingIds.find(settingId) != settingIds.end())
718 // turn the setting into a reference setting
719 setting->MakeReference();
721 else
723 // add the setting's identifier to the list of all identifiers
724 settingIds.insert(setting->GetId());
727 if (!settingWithConditions.enableCondition.empty() ||
728 !settingWithConditions.visibleCondition.empty())
730 settingWithConditions.setting = setting;
731 settingsWithConditions.push_back(settingWithConditions);
734 // add the setting to the list of settings from the same category
735 categorySettings.push_back(setting);
737 // add the setting to the current group
738 group->AddSetting(setting);
740 else
742 // add a dummy setting for the group / separator to the list of settings from the same category
743 categorySettings.push_back(nullptr);
746 // look for the next setting
747 settingElement = settingElement->NextSiblingElement("setting");
750 // process settings with enable/visible conditions
751 for (auto setting : settingsWithConditions)
753 if (!setting.enableCondition.empty())
755 CSettingDependency dependencyEnable(SettingDependencyType::Enable, GetSettingsManager());
756 if (ParseOldCondition(setting.setting, categorySettings, setting.enableCondition,
757 dependencyEnable))
758 setting.deps.push_back(dependencyEnable);
759 else
761 m_logger->warn(
762 "failed to parse enable condition \"{}\" of old setting definition for \"{}\"",
763 setting.enableCondition, setting.setting->GetId());
767 if (!setting.visibleCondition.empty())
769 CSettingDependency dependencyVisible(SettingDependencyType::Visible, GetSettingsManager());
770 if (ParseOldCondition(setting.setting, categorySettings, setting.visibleCondition,
771 dependencyVisible))
772 setting.deps.push_back(dependencyVisible);
773 else
775 m_logger->warn(
776 "failed to parse visible condition \"{}\" of old setting definition for \"{}\"",
777 setting.visibleCondition, setting.setting->GetId());
781 // set dependencies
782 setting.setting->SetDependencies(setting.deps);
785 return group;
788 std::shared_ptr<CSettingCategory> CAddonSettings::ParseOldCategoryElement(
789 uint32_t& categoryId, const TiXmlElement* categoryElement, std::set<std::string>& settingIds)
791 // create the category
792 auto category = std::make_shared<CSettingCategory>(StringUtils::Format("category{}", categoryId),
793 GetSettingsManager());
794 categoryId += 1;
796 // try to get the category's label and fall back to "General"
797 int categoryLabel = 128;
798 ParseOldLabel(categoryElement, g_localizeStrings.Get(categoryLabel), categoryLabel);
799 category->SetLabel(categoryLabel);
801 // prepare a setting group
802 auto group = ParseOldSettingElement(categoryElement, category, settingIds);
804 // add the group to the category
805 category->AddGroup(group);
807 return category;
810 bool CAddonSettings::InitializeFromOldSettingDefinitions(const CXBMCTinyXML& doc)
812 m_logger->debug("trying to load setting definitions from old format...");
814 const TiXmlElement* root = doc.RootElement();
815 if (root == nullptr)
816 return false;
818 std::shared_ptr<CSettingSection> section =
819 std::make_shared<CSettingSection>(m_addonId, GetSettingsManager());
821 std::shared_ptr<CSettingCategory> category;
822 uint32_t categoryId = 0;
824 // Settings id set
825 std::set<std::string> settingIds;
827 // Special case for no category settings
828 section->AddCategory(ParseOldCategoryElement(categoryId, root, settingIds));
830 const TiXmlElement* categoryElement = root->FirstChildElement("category");
831 while (categoryElement != nullptr)
833 section->AddCategory(ParseOldCategoryElement(categoryId, categoryElement, settingIds));
835 // look for the next category
836 categoryElement = categoryElement->NextSiblingElement("category");
839 // add the section to the settingsmanager
840 GetSettingsManager()->AddSection(section);
842 return true;
845 SettingPtr CAddonSettings::InitializeFromOldSettingAction(const std::string& settingId,
846 const TiXmlElement* settingElement,
847 const std::string& defaultValue)
849 // parse the action attribute
850 std::string action = XMLUtils::GetAttribute(settingElement, "action");
851 // replace $CWD with the url of the add-on
852 StringUtils::Replace(action, "$CWD", m_addonPath);
853 // replace $ID with the id of the add-on
854 StringUtils::Replace(action, "$ID", m_addonId);
856 // prepare the setting's control
857 auto control = std::make_shared<CSettingControlButton>();
858 control->SetFormat("action");
860 SettingPtr setting = nullptr;
861 // action settings don't require a setting id
862 if (settingId.empty())
864 auto actionSettingId = StringUtils::Format("action{}", m_unidentifiedSettingId);
865 m_unidentifiedSettingId += 1;
867 auto settingAction = std::make_shared<CSettingAction>(actionSettingId, GetSettingsManager());
868 settingAction->SetData(action);
870 setting = settingAction;
872 else
874 // assume that the setting might store a value as a string
875 auto settingString = std::make_shared<CSettingString>(settingId, GetSettingsManager());
876 settingString->SetDefault(defaultValue);
877 settingString->SetAllowEmpty(true);
879 control->SetActionData(action);
881 setting = settingString;
884 // get any options
885 std::string option = XMLUtils::GetAttribute(settingElement, "option");
886 // handle the "close" option
887 if (StringUtils::EqualsNoCase(option, "close"))
888 control->SetCloseDialog(true);
890 setting->SetControl(control);
892 return setting;
895 std::shared_ptr<CSetting> CAddonSettings::InitializeFromOldSettingLabel()
897 // label settings don't require a setting id
898 auto labelSettingId = StringUtils::Format("label{}", m_unidentifiedSettingId);
899 m_unidentifiedSettingId += 1;
901 auto settingLabel = std::make_shared<CSettingString>(labelSettingId, GetSettingsManager());
903 // create the setting's control
904 settingLabel->SetControl(std::make_shared<CSettingControlLabel>());
906 return settingLabel;
909 SettingPtr CAddonSettings::InitializeFromOldSettingBool(const std::string& settingId,
910 const TiXmlElement* settingElement,
911 const std::string& defaultValue)
913 auto setting = std::make_shared<CSettingBool>(settingId, GetSettingsManager());
914 if (setting->FromString(defaultValue))
915 setting->SetDefault(setting->GetValue());
917 setting->SetControl(std::make_shared<CSettingControlCheckmark>());
919 return setting;
922 SettingPtr CAddonSettings::InitializeFromOldSettingTextIpAddress(const std::string& settingId,
923 const std::string& settingType,
924 const TiXmlElement* settingElement,
925 const std::string& defaultValue,
926 const int settingLabel)
928 std::shared_ptr<CSettingString> setting;
929 auto control = std::make_shared<CSettingControlEdit>();
930 control->SetHeading(settingLabel);
932 // get any options
933 std::string option = XMLUtils::GetAttribute(settingElement, "option");
935 if (settingType == "ipaddress")
937 setting = std::make_shared<CSettingString>(settingId, GetSettingsManager());
938 control->SetFormat("ip");
940 else if (settingType == "text")
943 if (StringUtils::EqualsNoCase(option, "urlencoded"))
945 setting = std::make_shared<CSettingUrlEncodedString>(settingId, GetSettingsManager());
946 control->SetFormat("urlencoded");
948 else
950 setting = std::make_shared<CSettingString>(settingId, GetSettingsManager());
951 control->SetFormat("string");
952 control->SetHidden(StringUtils::EqualsNoCase(option, "hidden"));
956 setting->SetDefault(defaultValue);
957 setting->SetAllowEmpty(true);
958 setting->SetControl(control);
960 return setting;
963 SettingPtr CAddonSettings::InitializeFromOldSettingNumber(const std::string& settingId,
964 const TiXmlElement* settingElement,
965 const std::string& defaultValue,
966 const int settingLabel)
968 auto setting = std::make_shared<CSettingInt>(settingId, GetSettingsManager());
969 if (setting->FromString(defaultValue))
970 setting->SetDefault(setting->GetValue());
972 auto control = std::make_shared<CSettingControlEdit>();
973 control->SetHeading(settingLabel);
974 control->SetFormat("integer");
975 setting->SetControl(control);
977 return setting;
980 SettingPtr CAddonSettings::InitializeFromOldSettingPath(const std::string& settingId,
981 const std::string& settingType,
982 const TiXmlElement* settingElement,
983 const std::string& defaultValue,
984 const int settingLabel)
986 auto setting = std::make_shared<CSettingPath>(settingId, GetSettingsManager());
987 setting->SetDefault(defaultValue);
989 // parse sources/shares
990 const auto source = XMLUtils::GetAttribute(settingElement, "source");
991 if (!source.empty())
992 setting->SetSources({source});
994 // setup masking
995 const auto audioMask = CServiceBroker::GetFileExtensionProvider().GetMusicExtensions();
996 const auto videoMask = CServiceBroker::GetFileExtensionProvider().GetVideoExtensions();
997 const auto imageMask = CServiceBroker::GetFileExtensionProvider().GetPictureExtensions();
998 auto execMask = "";
999 #if defined(TARGET_WINDOWS)
1000 execMask = ".exe|.bat|.cmd|.py";
1001 #endif // defined(TARGET_WINDOWS)
1003 std::string mask = XMLUtils::GetAttribute(settingElement, "mask");
1004 if (!mask.empty())
1006 // convert mask qualifiers
1007 StringUtils::Replace(mask, "$AUDIO", audioMask);
1008 StringUtils::Replace(mask, "$VIDEO", videoMask);
1009 StringUtils::Replace(mask, "$IMAGE", imageMask);
1010 StringUtils::Replace(mask, "$EXECUTABLE", execMask);
1012 else
1014 if (settingType == "video")
1015 mask = videoMask;
1016 else if (settingType == "audio")
1017 mask = audioMask;
1018 else if (settingType == "image")
1019 mask = imageMask;
1020 else if (settingType == "executable")
1021 mask = execMask;
1023 setting->SetMasking(mask);
1025 // parse options
1026 const auto option = XMLUtils::GetAttribute(settingElement, "option");
1027 setting->SetWritable(StringUtils::EqualsNoCase(option, "writeable"));
1029 auto control = std::make_shared<CSettingControlButton>();
1030 if (settingType == "folder")
1031 control->SetFormat("path");
1032 else if (settingType == "image")
1033 control->SetFormat("image");
1034 else
1036 control->SetFormat("file");
1038 // parse the options
1039 const auto options = StringUtils::Split(option, OldSettingValuesSeparator);
1040 control->SetUseImageThumbs(std::find(options.cbegin(), options.cend(), "usethumbs") !=
1041 options.cend());
1042 control->SetUseFileDirectories(std::find(options.cbegin(), options.cend(), "treatasfolder") !=
1043 options.cend());
1045 control->SetHeading(settingLabel);
1046 setting->SetControl(control);
1048 return setting;
1051 SettingPtr CAddonSettings::InitializeFromOldSettingDate(const std::string& settingId,
1052 const TiXmlElement* settingElement,
1053 const std::string& defaultValue,
1054 const int settingLabel)
1056 auto setting = std::make_shared<CSettingDate>(settingId, GetSettingsManager());
1057 if (setting->FromString(defaultValue))
1058 setting->SetDefault(setting->GetValue());
1060 auto control = std::make_shared<CSettingControlButton>();
1061 control->SetFormat("date");
1062 control->SetHeading(settingLabel);
1063 setting->SetControl(control);
1065 return setting;
1068 SettingPtr CAddonSettings::InitializeFromOldSettingTime(const std::string& settingId,
1069 const TiXmlElement* settingElement,
1070 const std::string& defaultValue,
1071 const int settingLabel)
1073 auto setting = std::make_shared<CSettingTime>(settingId, GetSettingsManager());
1074 if (setting->FromString(defaultValue))
1075 setting->SetDefault(setting->GetValue());
1077 auto control = std::make_shared<CSettingControlButton>();
1078 control->SetFormat("time");
1079 control->SetHeading(settingLabel);
1080 setting->SetControl(control);
1082 return setting;
1085 SettingPtr CAddonSettings::InitializeFromOldSettingSelect(
1086 const std::string& settingId,
1087 const TiXmlElement* settingElement,
1088 const std::string& defaultValue,
1089 const int settingLabel,
1090 const std::string& settingValues,
1091 const std::vector<std::string>& settingLValues)
1093 // process values and lvalues
1094 std::vector<std::string> values;
1095 if (!settingLValues.empty())
1096 values = settingLValues;
1097 else
1098 values = StringUtils::Split(settingValues, OldSettingValuesSeparator);
1100 SettingPtr setting = nullptr;
1101 if (!values.empty())
1103 if (settingLValues.empty())
1105 auto settingString = std::make_shared<CSettingString>(settingId, GetSettingsManager());
1106 settingString->SetDefault(defaultValue);
1108 StringSettingOptions options;
1109 for (const auto& value : values)
1110 options.emplace_back(value, value);
1111 settingString->SetOptions(options);
1113 setting = settingString;
1115 else
1117 auto settingInt = std::make_shared<CSettingInt>(settingId, GetSettingsManager());
1118 if (settingInt->FromString(defaultValue))
1119 settingInt->SetDefault(settingInt->GetValue());
1121 TranslatableIntegerSettingOptions options;
1122 for (uint32_t i = 0; i < values.size(); ++i)
1123 options.emplace_back(static_cast<int>(strtol(values[i].c_str(), nullptr, 0)), i);
1124 settingInt->SetTranslatableOptions(options);
1126 setting = settingInt;
1129 else
1131 // parse sources/shares
1132 const auto source = XMLUtils::GetAttribute(settingElement, "source");
1133 if (!source.empty())
1134 setting = InitializeFromOldSettingFileWithSource(settingId, settingElement, defaultValue,
1135 settingValues);
1136 else
1137 m_logger->warn("failed to parse old setting definition for \"{}\" of type \"select\"",
1138 settingId);
1141 if (setting != nullptr)
1143 auto control = std::make_shared<CSettingControlList>();
1144 control->SetHeading(settingLabel);
1145 control->SetFormat("string");
1146 setting->SetControl(control);
1149 return setting;
1152 SettingPtr CAddonSettings::InitializeFromOldSettingAddon(const std::string& settingId,
1153 const TiXmlElement* settingElement,
1154 const std::string& defaultValue,
1155 const int settingLabel)
1157 // get addon types
1158 std::string addonTypeStr = XMLUtils::GetAttribute(settingElement, "addontype");
1159 const auto addonTypesStr = StringUtils::Split(addonTypeStr, ",");
1160 std::set<AddonType> addonTypes;
1161 for (auto addonType : addonTypesStr)
1163 auto type = ADDON::CAddonInfo::TranslateType(StringUtils::Trim(addonType));
1164 if (type != ADDON::AddonType::UNKNOWN)
1165 addonTypes.insert(type);
1168 if (addonTypes.empty())
1170 m_logger->error("missing addon type for addon setting \"{}\"", settingId);
1171 return nullptr;
1174 // TODO: support multiple addon types
1175 if (addonTypes.size() > 1)
1177 m_logger->error("multiple addon types are not supported (addon setting \"{}\")", settingId);
1178 return nullptr;
1181 // parse addon ids
1182 auto addonIds = StringUtils::Split(defaultValue, ",");
1184 // parse multiselect option
1185 bool multiselect = false;
1186 settingElement->QueryBoolAttribute("multiselect", &multiselect);
1188 // sanity check
1189 if (addonIds.size() > 1 && !multiselect)
1191 m_logger->warn("multiple default addon ids on non-multiselect addon setting \"{}\"", settingId);
1192 addonIds.erase(++addonIds.begin(), addonIds.end());
1195 auto settingAddon = std::make_shared<CSettingAddon>(settingId, GetSettingsManager());
1196 settingAddon->SetAddonType(*addonTypes.begin());
1198 SettingPtr setting = settingAddon;
1199 if (multiselect)
1201 auto settingList =
1202 std::make_shared<CSettingList>(settingId, settingAddon, GetSettingsManager());
1203 settingList->SetDelimiter(",");
1204 if (settingList->FromString(addonIds))
1205 settingList->SetDefault(settingList->GetValue());
1207 setting = settingList;
1209 else if (!addonIds.empty())
1210 settingAddon->SetDefault(addonIds.front());
1212 auto control = std::make_shared<CSettingControlButton>();
1213 control->SetFormat("addon");
1214 control->SetHeading(settingLabel);
1215 setting->SetControl(control);
1217 return setting;
1220 SettingPtr CAddonSettings::InitializeFromOldSettingEnums(
1221 const std::string& settingId,
1222 const std::string& settingType,
1223 const TiXmlElement* settingElement,
1224 const std::string& defaultValue,
1225 const std::string& settingValues,
1226 const std::vector<std::string>& settingLValues)
1228 // process values and lvalues
1229 std::vector<std::string> values;
1230 if (!settingLValues.empty())
1231 values = settingLValues;
1232 else if (settingValues == "$HOURS")
1234 for (uint32_t hour = 0; hour < 24; hour++)
1235 values.push_back(
1236 CDateTime(2000, 1, 1, hour, 0, 0).GetAsLocalizedTime(g_langInfo.GetTimeFormat(), false));
1238 else
1239 values = StringUtils::Split(settingValues, OldSettingValuesSeparator);
1241 // process entries
1242 const auto settingEntries = StringUtils::Split(XMLUtils::GetAttribute(settingElement, "entries"),
1243 OldSettingValuesSeparator);
1245 // process sort
1246 bool sortAscending = false;
1247 std::string sort = XMLUtils::GetAttribute(settingElement, "sort");
1248 if (sort == "true" || sort == "yes")
1249 sortAscending = true;
1251 SettingPtr setting = nullptr;
1252 if (settingType == "enum")
1254 auto settingInt = std::make_shared<CSettingInt>(settingId, GetSettingsManager());
1256 if (settingLValues.empty())
1258 IntegerSettingOptions options;
1259 for (uint32_t i = 0; i < values.size(); ++i)
1261 std::string label = values[i];
1262 int value = i;
1263 if (settingEntries.size() > i)
1264 value = static_cast<int>(strtol(settingEntries[i].c_str(), nullptr, 0));
1266 options.emplace_back(label, value);
1269 settingInt->SetOptions(options);
1271 else
1273 TranslatableIntegerSettingOptions options;
1274 for (uint32_t i = 0; i < values.size(); ++i)
1276 int label = static_cast<int>(strtol(values[i].c_str(), nullptr, 0));
1277 int value = i;
1278 if (settingEntries.size() > i)
1279 value = static_cast<int>(strtol(settingEntries[i].c_str(), nullptr, 0));
1281 options.emplace_back(label, value);
1284 settingInt->SetTranslatableOptions(options);
1287 if (sortAscending)
1288 settingInt->SetOptionsSort(SettingOptionsSort::Ascending);
1290 // set the default value
1291 if (settingInt->FromString(defaultValue))
1292 settingInt->SetDefault(settingInt->GetValue());
1294 setting = settingInt;
1296 else
1298 auto settingString = std::make_shared<CSettingString>(settingId, GetSettingsManager());
1300 if (settingLValues.empty())
1302 StringSettingOptions options;
1303 for (uint32_t i = 0; i < values.size(); ++i)
1305 std::string value = values[i];
1306 if (settingEntries.size() > i)
1307 value = settingEntries[i];
1309 options.emplace_back(value, value);
1312 settingString->SetOptions(options);
1314 else
1316 TranslatableStringSettingOptions options;
1317 for (uint32_t i = 0; i < values.size(); ++i)
1319 int label = static_cast<int>(strtol(values[i].c_str(), nullptr, 0));
1320 std::string value = g_localizeStrings.GetAddonString(m_addonId, label);
1321 if (settingEntries.size() > i)
1322 value = settingEntries[i];
1324 options.emplace_back(label, value);
1327 settingString->SetTranslatableOptions(options);
1330 if (sortAscending)
1331 settingString->SetOptionsSort(SettingOptionsSort::Ascending);
1333 // set the default value
1334 settingString->SetDefault(defaultValue);
1336 setting = settingString;
1339 auto control = std::make_shared<CSettingControlSpinner>();
1340 control->SetFormat("string");
1341 setting->SetControl(control);
1343 return setting;
1346 SettingPtr CAddonSettings::InitializeFromOldSettingFileEnum(const std::string& settingId,
1347 const TiXmlElement* settingElement,
1348 const std::string& defaultValue,
1349 const std::string& settingValues)
1351 auto setting = InitializeFromOldSettingFileWithSource(settingId, settingElement, defaultValue,
1352 settingValues);
1354 auto control = std::make_shared<CSettingControlSpinner>();
1355 control->SetFormat("string");
1356 setting->SetControl(control);
1358 return setting;
1361 SettingPtr CAddonSettings::InitializeFromOldSettingRangeOfNum(const std::string& settingId,
1362 const TiXmlElement* settingElement,
1363 const std::string& defaultValue)
1365 auto setting = std::make_shared<CSettingNumber>(settingId, GetSettingsManager());
1366 if (setting->FromString(defaultValue))
1367 setting->SetDefault(setting->GetValue());
1369 // parse rangestart and rangeend
1370 double rangeStart = 0.0, rangeEnd = 1.0;
1371 settingElement->QueryDoubleAttribute("rangestart", &rangeStart);
1372 settingElement->QueryDoubleAttribute("rangeend", &rangeEnd);
1373 setting->SetMinimum(rangeStart);
1374 setting->SetMaximum(rangeEnd);
1376 // parse elements
1377 uint32_t elements = 2;
1378 settingElement->QueryUnsignedAttribute("elements", &elements);
1379 if (elements > 1)
1380 setting->SetStep((rangeEnd - rangeStart) / (elements - 1));
1382 // parse valueformat
1383 int valueFormat = -1;
1384 settingElement->QueryIntAttribute("valueformat", &valueFormat);
1386 auto control = std::make_shared<CSettingControlSpinner>();
1387 control->SetFormat("string");
1388 control->SetFormatLabel(valueFormat);
1389 setting->SetControl(control);
1391 return setting;
1394 SettingPtr CAddonSettings::InitializeFromOldSettingSlider(const std::string& settingId,
1395 const TiXmlElement* settingElement,
1396 const std::string& defaultValue)
1398 // parse range
1399 double min = 0.0, max = 100.0, step = 1.0;
1400 const auto range = StringUtils::Split(XMLUtils::GetAttribute(settingElement, "range"), ',');
1402 if (range.size() > 1)
1404 min = strtod(range[0].c_str(), nullptr);
1406 if (range.size() > 2)
1408 max = strtod(range[2].c_str(), nullptr);
1409 step = strtod(range[1].c_str(), nullptr);
1411 else
1412 max = strtod(range[1].c_str(), nullptr);
1415 // parse option
1416 auto option = XMLUtils::GetAttribute(settingElement, "option");
1417 if (option.empty() || StringUtils::EqualsNoCase(option, "float"))
1419 auto setting = std::make_shared<CSettingNumber>(settingId, GetSettingsManager());
1420 if (setting->FromString(defaultValue))
1421 setting->SetDefault(setting->GetValue());
1423 setting->SetMinimum(min);
1424 setting->SetStep(step);
1425 setting->SetMaximum(max);
1427 auto control = std::make_shared<CSettingControlSlider>();
1428 control->SetFormat("number");
1429 control->SetPopup(false);
1430 setting->SetControl(control);
1432 return setting;
1435 if (StringUtils::EqualsNoCase(option, "int") || StringUtils::EqualsNoCase(option, "percent"))
1437 auto setting = std::make_shared<CSettingInt>(settingId, GetSettingsManager());
1438 if (setting->FromString(defaultValue))
1439 setting->SetDefault(setting->GetValue());
1441 setting->SetMinimum(static_cast<int>(min));
1442 setting->SetStep(static_cast<int>(step));
1443 setting->SetMaximum(static_cast<int>(max));
1445 auto control = std::make_shared<CSettingControlSlider>();
1446 control->SetFormat(StringUtils::EqualsNoCase(option, "int") ? "integer" : "percentage");
1447 control->SetPopup(false);
1448 setting->SetControl(control);
1450 return setting;
1453 m_logger->warn("ignoring old setting definition for \"{}\" of type \"slider\" because of unknown "
1454 "option \"{}\"",
1455 settingId, option);
1457 return nullptr;
1460 SettingPtr CAddonSettings::InitializeFromOldSettingFileWithSource(
1461 const std::string& settingId,
1462 const TiXmlElement* settingElement,
1463 const std::string& defaultValue,
1464 std::string source)
1466 auto setting = std::make_shared<CSettingPath>(settingId, GetSettingsManager());
1467 setting->SetDefault(defaultValue);
1469 if (source.find("$PROFILE") != std::string::npos)
1470 StringUtils::Replace(source, "$PROFILE", m_addonProfile);
1471 else
1472 source = URIUtils::AddFileToFolder(m_addonPath, source);
1474 setting->SetSources({source});
1476 // process the path/file mask
1477 setting->SetMasking(XMLUtils::GetAttribute(settingElement, "mask"));
1479 // process option
1480 std::string option = XMLUtils::GetAttribute(settingElement, "option");
1481 setting->SetHideExtension(StringUtils::EqualsNoCase(option, "hideext"));
1483 setting->SetOptionsFiller(FileEnumSettingOptionsFiller);
1485 return setting;
1488 bool CAddonSettings::LoadOldSettingValues(const CXBMCTinyXML& doc,
1489 std::map<std::string, std::string>& settings) const
1491 if (!doc.RootElement())
1492 return false;
1494 const TiXmlElement* category = doc.RootElement()->FirstChildElement("category");
1495 if (category == nullptr)
1496 category = doc.RootElement();
1498 while (category != nullptr)
1500 const TiXmlElement* setting = category->FirstChildElement("setting");
1501 while (setting != nullptr)
1503 const char* id = setting->Attribute("id");
1504 const char* value = setting->Attribute("value");
1505 if (id != nullptr && value != nullptr)
1506 settings[id] = value;
1508 setting = setting->NextSiblingElement("setting");
1511 category = category->NextSiblingElement("category");
1514 return !settings.empty();
1517 bool CAddonSettings::ParseOldLabel(const TiXmlElement* element,
1518 const std::string& settingId,
1519 int& labelId)
1521 labelId = -1;
1522 if (element == nullptr)
1523 return false;
1525 // label value as a string
1526 std::string labelString;
1527 element->QueryStringAttribute("label", &labelString);
1529 bool parsed = !labelString.empty();
1531 // try to parse the label as a pure number, i.e. a localized string
1532 if (parsed)
1534 char* endptr;
1535 labelId = std::strtol(labelString.c_str(), &endptr, 10);
1536 if (endptr == nullptr || *endptr == '\0')
1537 return true;
1539 // make sure the label string is not empty
1540 else
1541 labelString = " ";
1543 labelId = m_unknownSettingLabelId;
1544 m_unknownSettingLabelId += 1;
1545 m_unknownSettingLabels.emplace(labelId, labelString);
1547 return parsed;
1550 bool CAddonSettings::ParseOldCondition(const std::shared_ptr<const CSetting>& setting,
1551 const std::vector<std::shared_ptr<const CSetting>>& settings,
1552 const std::string& condition,
1553 CSettingDependency& dependeny) const
1555 if (setting == nullptr)
1556 return false;
1558 if (condition.empty())
1559 return true;
1561 // find the index of the setting in the list of all settings of the category
1562 auto settingIt = std::find_if(settings.cbegin(), settings.cend(),
1563 [setting](const SettingConstPtr& otherSetting) {
1564 if (otherSetting == nullptr)
1565 return false;
1567 return setting->GetId() == otherSetting->GetId();
1569 if (settingIt == settings.cend())
1571 m_logger->warn("failed to parse old setting conditions \"{}\" for \"{}\"", condition,
1572 setting->GetId());
1573 return false;
1575 int32_t currentSettingIndex = std::distance(settings.cbegin(), settingIt);
1577 CSettingDependencyConditionCombinationPtr dependencyCombination;
1578 std::vector<std::string> conditions;
1579 if (condition.find('+') != std::string::npos)
1581 StringUtils::Tokenize(condition, conditions, '+');
1582 dependencyCombination = dependeny.And();
1584 else
1586 StringUtils::Tokenize(condition, conditions, '|');
1587 dependencyCombination = dependeny.Or();
1590 bool error = false;
1591 for (const auto& cond : conditions)
1593 ConditionExpression expression;
1594 if (!ParseOldConditionExpression(cond, expression))
1595 continue;
1597 // determine the absolute setting index
1598 int32_t absoluteSettingIndex = currentSettingIndex + expression.m_relativeSettingIndex;
1600 // we cannot handle relative indices pointing to settings not belonging to the same category
1601 if (absoluteSettingIndex < 0 || static_cast<size_t>(absoluteSettingIndex) >= settings.size())
1603 m_logger->warn("cannot reference setting (relative index: {}; absolute index: {}) in another "
1604 "category in old setting condition \"{}\" for \"{}\"",
1605 expression.m_relativeSettingIndex, absoluteSettingIndex, cond,
1606 setting->GetId());
1607 error = true;
1608 continue;
1611 const SettingConstPtr& referencedSetting = settings.at(absoluteSettingIndex);
1612 if (referencedSetting == nullptr)
1614 m_logger->warn(
1615 "cannot reference separator setting in old setting condition \"{}\" for \"{}\"", cond,
1616 setting->GetId());
1617 error = true;
1618 continue;
1621 // try to handle some odd cases where the setting is of type string but the comparison value references the index of the value in the list of options
1622 if (referencedSetting->GetType() == SettingType::String &&
1623 StringUtils::IsNaturalNumber(expression.m_value))
1625 // try to parse the comparison value
1626 size_t valueIndex = static_cast<size_t>(strtoul(expression.m_value.c_str(), nullptr, 10));
1628 const auto referencedSettingString =
1629 std::static_pointer_cast<const CSettingString>(referencedSetting);
1630 switch (referencedSettingString->GetOptionsType())
1632 case SettingOptionsType::Static:
1634 const auto& options = referencedSettingString->GetOptions();
1635 if (options.size() > valueIndex)
1636 expression.m_value = options.at(valueIndex).value;
1637 break;
1640 case SettingOptionsType::StaticTranslatable:
1642 const auto& options = referencedSettingString->GetTranslatableOptions();
1643 if (options.size() > valueIndex)
1644 expression.m_value = options.at(valueIndex).second;
1645 break;
1648 default:
1649 break;
1653 // add the condition to the value of the referenced setting
1654 dependencyCombination->Add(std::make_shared<CSettingDependencyCondition>(
1655 referencedSetting->GetId(), expression.m_value, expression.m_operator, expression.m_negated,
1656 GetSettingsManager()));
1659 // if the condition doesn't depend on other settings it might be an infobool expression
1660 if (!error && dependencyCombination->GetOperations().empty() &&
1661 dependencyCombination->GetValues().empty())
1662 dependencyCombination->Add(std::make_shared<CSettingDependencyCondition>(
1663 "InfoBool", condition, "", false, GetSettingsManager()));
1665 return !error;
1668 bool CAddonSettings::ParseOldConditionExpression(std::string str, ConditionExpression& expression)
1670 StringUtils::Trim(str);
1672 size_t posOpen = str.find('(');
1673 size_t posSep = str.find(',', posOpen);
1674 size_t posClose = str.find(')', posSep);
1676 if (posOpen == std::string::npos || posSep == std::string::npos || posClose == std::string::npos)
1677 return false;
1679 auto op = str.substr(0, posOpen);
1681 // check if the operator is negated
1682 expression.m_negated = StringUtils::StartsWith(op, "!");
1683 if (expression.m_negated)
1684 op = op.substr(1);
1686 // parse the operator
1687 if (StringUtils::EqualsNoCase(op, "eq"))
1688 expression.m_operator = SettingDependencyOperator::Equals;
1689 else if (StringUtils::EqualsNoCase(op, "gt"))
1690 expression.m_operator = SettingDependencyOperator::GreaterThan;
1691 else if (StringUtils::EqualsNoCase(op, "lt"))
1692 expression.m_operator = SettingDependencyOperator::LessThan;
1693 else
1694 return false;
1696 expression.m_relativeSettingIndex = static_cast<int32_t>(
1697 strtol(str.substr(posOpen + 1, posSep - posOpen - 1).c_str(), nullptr, 10));
1698 expression.m_value = str.substr(posSep + 1, posClose - posSep - 1);
1700 return true;
1703 void CAddonSettings::FileEnumSettingOptionsFiller(const std::shared_ptr<const CSetting>& setting,
1704 std::vector<StringSettingOption>& list,
1705 std::string& current,
1706 void* data)
1708 if (setting == nullptr)
1709 return;
1711 auto settingPath = std::dynamic_pointer_cast<const CSettingPath>(setting);
1712 if (settingPath == nullptr)
1713 return;
1715 if (settingPath->GetSources().empty())
1716 return;
1718 const std::string& masking = settingPath->GetMasking(CServiceBroker::GetFileExtensionProvider());
1720 // fetch the matching files/directories
1721 CFileItemList items;
1722 XFILE::CDirectory::GetDirectory(settingPath->GetSources().front(), items, masking,
1723 XFILE::DIR_FLAG_NO_FILE_DIRS);
1725 // process the matching files/directories
1726 for (const auto& item : items)
1728 if ((masking == "/" && item->m_bIsFolder) || !item->m_bIsFolder)
1730 if (settingPath->HideExtension())
1731 item->RemoveExtension();
1732 list.emplace_back(item->GetLabel(), item->GetLabel());
1737 } // namespace ADDON