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.
9 #include "AddonSettings.h"
12 #include "FileItemList.h"
13 #include "GUIInfoManager.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"
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
,
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
);
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);
92 template<class TSetting
>
93 SettingPtr
AddSettingWithoutDefinition(ADDON::CAddonSettings
& settings
,
94 const std::string
& settingId
,
95 typename
TSetting::Value defaultValue
,
98 if (settingId
.empty())
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");
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())
113 std::make_shared
<CSettingSection
>(settings
.GetAddonId(), settings
.GetSettingsManager());
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());
123 category
= categories
.back();
125 // check if we need to add a group on-the-fly
126 auto groups
= category
->GetGroups();
127 SettingGroupPtr group
;
129 group
= std::make_shared
<CSettingGroup
>("0", settings
.GetSettingsManager());
131 group
= groups
.back();
133 // create a new setting on-the-fly
135 InitializeFromOldSettingWithoutDefinition
<TSetting
>(settings
, settingId
, defaultValue
);
136 if (setting
== nullptr)
138 logger
->warn("failed to create setting \"{}\" on-the-fly", settingId
);
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
);
157 CAddonSettings::CAddonSettings(const std::shared_ptr
<IAddon
>& addon
, AddonInstanceId instanceId
)
159 m_addonId(addon
->ID()),
160 m_addonPath(addon
->Path()),
161 m_addonProfile(addon
->Profile()),
162 m_instanceId(instanceId
),
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")
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())
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
))
231 "CAddonSettings::{} - Add-on {} using instance setting values byself, Kodi's add ignored",
232 __func__
, m_addonId
);
236 auto mgr
= GetSettingsManager();
240 auto sections
= mgr
->GetSections();
241 if (sections
.empty())
244 SettingSectionPtr section
= *sections
.begin();
246 auto categories
= section
->GetCategories();
247 if (categories
.empty())
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())
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
))
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
))
285 bool CAddonSettings::Initialize(const CXBMCTinyXML
& doc
, bool allowEmpty
/* = false */)
287 std::unique_lock
<CCriticalSection
> lock(m_critical
);
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
)
303 // Add internal settings to set values about instance set
304 if (m_instanceId
> 0 && !AddInstanceSettings())
307 GetSettingsManager()->SetInitialized();
309 m_initialized
= true;
314 bool CAddonSettings::Load(const CXBMCTinyXML
& doc
)
316 std::unique_lock
<CCriticalSection
> lock(m_critical
);
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");
328 std::map
<std::string
, std::string
> settingValues
;
330 // for new/"normal" setting values use the standard process
334 if (!LoadValuesFromXml(doc
, updated
))
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())
344 auto id
= setting
->ToElement()->Attribute("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();
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");
386 // process all settings
387 for (const auto& setting
: settingValues
)
389 // ignore setting values without a setting identifier
390 if (setting
.first
.empty())
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
,
402 // try to load the old setting value
405 m_logger
->error("had null newSetting for value \"{}\" for setting {}", setting
.second
,
408 else if (!newSetting
->FromString(setting
.second
))
410 m_logger
->warn("failed to load value \"{}\" for setting {}", setting
.second
, setting
.first
);
419 bool CAddonSettings::Save(CXBMCTinyXML
& doc
) const
421 std::unique_lock
<CCriticalSection
> lock(m_critical
);
425 if (!SaveValuesToXml(doc
))
427 m_logger
->error("failed to save settings");
434 bool CAddonSettings::HasSettings() const
436 return IsInitialized() && GetSettingsManager()->HasSettings();
439 bool CAddonSettings::Save()
441 std::shared_ptr
<IAddon
> addon
= m_addon
.lock();
444 return addon
->SaveSettings();
449 std::string
CAddonSettings::GetSettingLabel(int label
) const
451 if (label
< UnknownSettingLabelIdStart
|| label
>= m_unknownSettingLabelId
)
454 const auto labelIt
= m_unknownSettingLabels
.find(label
);
455 if (labelIt
== m_unknownSettingLabels
.end())
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");
526 // for new/"normal" setting definitions use the standard process
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();
540 if (!StringUtils::EqualsNoCase(root
->ValueStr(), SETTING_XML_ROOT
))
542 m_logger
->error("error reading setting definitions: no <settings> tag");
546 version
= GetSettingsManager()->ParseVersion(root
);
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
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
);
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());
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
);
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")
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
,
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")
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")
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
);
650 m_logger
->warn("failed to parse old setting definition for \"{}\" of type \"{}\"", settingId
,
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
&&
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());
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();
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
);
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
,
758 setting
.deps
.push_back(dependencyEnable
);
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
,
772 setting
.deps
.push_back(dependencyVisible
);
776 "failed to parse visible condition \"{}\" of old setting definition for \"{}\"",
777 setting
.visibleCondition
, setting
.setting
->GetId());
782 setting
.setting
->SetDependencies(setting
.deps
);
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());
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
);
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();
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;
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
);
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
;
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
;
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
);
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
>());
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
>());
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
);
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");
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
);
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
);
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");
992 setting
->SetSources({source
});
995 const auto audioMask
= CServiceBroker::GetFileExtensionProvider().GetMusicExtensions();
996 const auto videoMask
= CServiceBroker::GetFileExtensionProvider().GetVideoExtensions();
997 const auto imageMask
= CServiceBroker::GetFileExtensionProvider().GetPictureExtensions();
999 #if defined(TARGET_WINDOWS)
1000 execMask
= ".exe|.bat|.cmd|.py";
1001 #endif // defined(TARGET_WINDOWS)
1003 std::string mask
= XMLUtils::GetAttribute(settingElement
, "mask");
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
);
1014 if (settingType
== "video")
1016 else if (settingType
== "audio")
1018 else if (settingType
== "image")
1020 else if (settingType
== "executable")
1023 setting
->SetMasking(mask
);
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");
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") !=
1042 control
->SetUseFileDirectories(std::find(options
.cbegin(), options
.cend(), "treatasfolder") !=
1045 control
->SetHeading(settingLabel
);
1046 setting
->SetControl(control
);
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
);
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
);
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
;
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
;
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
;
1131 // parse sources/shares
1132 const auto source
= XMLUtils::GetAttribute(settingElement
, "source");
1133 if (!source
.empty())
1134 setting
= InitializeFromOldSettingFileWithSource(settingId
, settingElement
, defaultValue
,
1137 m_logger
->warn("failed to parse old setting definition for \"{}\" of type \"select\"",
1141 if (setting
!= nullptr)
1143 auto control
= std::make_shared
<CSettingControlList
>();
1144 control
->SetHeading(settingLabel
);
1145 control
->SetFormat("string");
1146 setting
->SetControl(control
);
1152 SettingPtr
CAddonSettings::InitializeFromOldSettingAddon(const std::string
& settingId
,
1153 const TiXmlElement
* settingElement
,
1154 const std::string
& defaultValue
,
1155 const int settingLabel
)
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
);
1174 // TODO: support multiple addon types
1175 if (addonTypes
.size() > 1)
1177 m_logger
->error("multiple addon types are not supported (addon setting \"{}\")", settingId
);
1182 auto addonIds
= StringUtils::Split(defaultValue
, ",");
1184 // parse multiselect option
1185 bool multiselect
= false;
1186 settingElement
->QueryBoolAttribute("multiselect", &multiselect
);
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
;
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
);
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
++)
1236 CDateTime(2000, 1, 1, hour
, 0, 0).GetAsLocalizedTime(g_langInfo
.GetTimeFormat(), false));
1239 values
= StringUtils::Split(settingValues
, OldSettingValuesSeparator
);
1242 const auto settingEntries
= StringUtils::Split(XMLUtils::GetAttribute(settingElement
, "entries"),
1243 OldSettingValuesSeparator
);
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
];
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
);
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));
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
);
1288 settingInt
->SetOptionsSort(SettingOptionsSort::Ascending
);
1290 // set the default value
1291 if (settingInt
->FromString(defaultValue
))
1292 settingInt
->SetDefault(settingInt
->GetValue());
1294 setting
= settingInt
;
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
);
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
);
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
);
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
,
1354 auto control
= std::make_shared
<CSettingControlSpinner
>();
1355 control
->SetFormat("string");
1356 setting
->SetControl(control
);
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
);
1377 uint32_t elements
= 2;
1378 settingElement
->QueryUnsignedAttribute("elements", &elements
);
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
);
1394 SettingPtr
CAddonSettings::InitializeFromOldSettingSlider(const std::string
& settingId
,
1395 const TiXmlElement
* settingElement
,
1396 const std::string
& defaultValue
)
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);
1412 max
= strtod(range
[1].c_str(), nullptr);
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
);
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
);
1453 m_logger
->warn("ignoring old setting definition for \"{}\" of type \"slider\" because of unknown "
1460 SettingPtr
CAddonSettings::InitializeFromOldSettingFileWithSource(
1461 const std::string
& settingId
,
1462 const TiXmlElement
* settingElement
,
1463 const std::string
& defaultValue
,
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
);
1472 source
= URIUtils::AddFileToFolder(m_addonPath
, source
);
1474 setting
->SetSources({source
});
1476 // process the path/file mask
1477 setting
->SetMasking(XMLUtils::GetAttribute(settingElement
, "mask"));
1480 std::string option
= XMLUtils::GetAttribute(settingElement
, "option");
1481 setting
->SetHideExtension(StringUtils::EqualsNoCase(option
, "hideext"));
1483 setting
->SetOptionsFiller(FileEnumSettingOptionsFiller
);
1488 bool CAddonSettings::LoadOldSettingValues(const CXBMCTinyXML
& doc
,
1489 std::map
<std::string
, std::string
>& settings
) const
1491 if (!doc
.RootElement())
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
,
1522 if (element
== nullptr)
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
1535 labelId
= std::strtol(labelString
.c_str(), &endptr
, 10);
1536 if (endptr
== nullptr || *endptr
== '\0')
1539 // make sure the label string is not empty
1543 labelId
= m_unknownSettingLabelId
;
1544 m_unknownSettingLabelId
+= 1;
1545 m_unknownSettingLabels
.emplace(labelId
, labelString
);
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)
1558 if (condition
.empty())
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)
1567 return setting
->GetId() == otherSetting
->GetId();
1569 if (settingIt
== settings
.cend())
1571 m_logger
->warn("failed to parse old setting conditions \"{}\" for \"{}\"", condition
,
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();
1586 StringUtils::Tokenize(condition
, conditions
, '|');
1587 dependencyCombination
= dependeny
.Or();
1591 for (const auto& cond
: conditions
)
1593 ConditionExpression expression
;
1594 if (!ParseOldConditionExpression(cond
, expression
))
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
,
1611 const SettingConstPtr
& referencedSetting
= settings
.at(absoluteSettingIndex
);
1612 if (referencedSetting
== nullptr)
1615 "cannot reference separator setting in old setting condition \"{}\" for \"{}\"", cond
,
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
;
1640 case SettingOptionsType::StaticTranslatable
:
1642 const auto& options
= referencedSettingString
->GetTranslatableOptions();
1643 if (options
.size() > valueIndex
)
1644 expression
.m_value
= options
.at(valueIndex
).second
;
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()));
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
)
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
)
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
;
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);
1703 void CAddonSettings::FileEnumSettingOptionsFiller(const std::shared_ptr
<const CSetting
>& setting
,
1704 std::vector
<StringSettingOption
>& list
,
1705 std::string
& current
,
1708 if (setting
== nullptr)
1711 auto settingPath
= std::dynamic_pointer_cast
<const CSettingPath
>(setting
);
1712 if (settingPath
== nullptr)
1715 if (settingPath
->GetSources().empty())
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