1 /*
2 * Copyright (C) 2013-2018 Team Kodi
3 * This file is part of Kodi - https://kodi.tv
4 *
5 * SPDX-License-Identifier: GPL-2.0-or-later
6 * See LICENSES/README.md for more information.
7 */
8
9 #include "Setting.h"
10
11 #include "ServiceBroker.h"
12 #include "SettingDefinitions.h"
13 #include "SettingsManager.h"
14 #include "utils/StringUtils.h"
15 #include "utils/XBMCTinyXML.h"
16 #include "utils/XMLUtils.h"
17 #include "utils/log.h"
18
19 #include <sstream>
20 #include <utility>
21
22 template<typename TKey, typename TValue>
CheckSettingOptionsValidity(const TValue & value,const std::vector<std::pair<TKey,TValue>> & options)23 bool CheckSettingOptionsValidity(const TValue& value, const std::vector<std::pair<TKey, TValue>>& options)
24 {
25 for (const auto& it : options)
26 {
27 if (it.second == value)
28 return true;
29 }
30
31 return false;
32 }
33
34 template<typename TKey, typename TValue>
CheckSettingOptionsValidity(const TValue & value,const std::vector<TKey> & options)35 bool CheckSettingOptionsValidity(const TValue& value, const std::vector<TKey>& options)
36 {
37 for (const auto& it : options)
38 {
39 if (it.value == value)
40 return true;
41 }
42
43 return false;
44 }
45
DeserializeOptionsSort(const TiXmlElement * optionsElement,SettingOptionsSort & optionsSort)46 bool DeserializeOptionsSort(const TiXmlElement* optionsElement, SettingOptionsSort& optionsSort)
47 {
48 optionsSort = SettingOptionsSort::NoSorting;
49
50 std::string sort;
51 if (optionsElement->QueryStringAttribute("sort", &sort) != TIXML_SUCCESS)
52 return true;
53
54 if (StringUtils::EqualsNoCase(sort, "false") || StringUtils::EqualsNoCase(sort, "off") ||
55 StringUtils::EqualsNoCase(sort, "no") || StringUtils::EqualsNoCase(sort, "disabled"))
56 optionsSort = SettingOptionsSort::NoSorting;
57 else if (StringUtils::EqualsNoCase(sort, "asc") || StringUtils::EqualsNoCase(sort, "ascending") ||
58 StringUtils::EqualsNoCase(sort, "true") || StringUtils::EqualsNoCase(sort, "on") ||
59 StringUtils::EqualsNoCase(sort, "yes") || StringUtils::EqualsNoCase(sort, "enabled"))
60 optionsSort = SettingOptionsSort::Ascending;
61 else if (StringUtils::EqualsNoCase(sort, "desc") || StringUtils::EqualsNoCase(sort, "descending"))
62 optionsSort = SettingOptionsSort::Descending;
63 else
64 return false;
65
66 return true;
67 }
68
69 Logger CSetting::s_logger;
70
CSetting(const std::string & id,CSettingsManager * settingsManager,const std::string & name)71 CSetting::CSetting(const std::string& id,
72 CSettingsManager* settingsManager /* = nullptr */,
73 const std::string& name /* = "CSetting" */)
74 : ISetting(id, settingsManager)
75 {
76 if (s_logger == nullptr)
77 s_logger = CServiceBroker::GetLogging().GetLogger(name);
78 }
79
CSetting(const std::string & id,const CSetting & setting,const std::string & name)80 CSetting::CSetting(const std::string& id,
81 const CSetting& setting,
82 const std::string& name /* = "CSetting" */)
83 : CSetting(id, setting.m_settingsManager, name)
84 {
85 Copy(setting);
86 }
87
MergeBasics(const CSetting & other)88 void CSetting::MergeBasics(const CSetting& other)
89 {
90 // ISetting
91 SetVisible(other.GetVisible());
92 SetLabel(other.GetLabel());
93 SetHelp(other.GetHelp());
94 SetRequirementsMet(other.MeetsRequirements());
95 // CSetting
96 SetEnabled(other.GetEnabled());
97 SetParent(other.GetParent());
98 SetLevel(other.GetLevel());
99 SetControl(const_cast<CSetting&>(other).GetControl());
100 SetDependencies(other.GetDependencies());
101 }
102
Deserialize(const TiXmlNode * node,bool update)103 bool CSetting::Deserialize(const TiXmlNode *node, bool update /* = false */)
104 {
105 // handle <visible> conditions
106 if (!ISetting::Deserialize(node, update))
107 return false;
108
109 auto element = node->ToElement();
110 if (element == nullptr)
111 return false;
112
113 auto parentSetting = element->Attribute(SETTING_XML_ATTR_PARENT);
114 if (parentSetting != nullptr)
115 m_parentSetting = parentSetting;
116
117 // get <enable>
118 bool value;
119 if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_ENABLED, value))
120 m_enabled = value;
121
122 // get the <level>
123 int level = -1;
124 if (XMLUtils::GetInt(node, SETTING_XML_ELM_LEVEL, level))
125 m_level = static_cast<SettingLevel>(level);
126
127 if (m_level < SettingLevel::Basic || m_level > SettingLevel::Internal)
128 m_level = SettingLevel::Standard;
129
130 auto dependencies = node->FirstChild(SETTING_XML_ELM_DEPENDENCIES);
131 if (dependencies != nullptr)
132 {
133 auto dependencyNode = dependencies->FirstChild(SETTING_XML_ELM_DEPENDENCY);
134 while (dependencyNode != nullptr)
135 {
136 CSettingDependency dependency(m_settingsManager);
137 if (dependency.Deserialize(dependencyNode))
138 m_dependencies.push_back(dependency);
139 else
140 s_logger->warn("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_DEPENDENCY, m_id);
141
142 dependencyNode = dependencyNode->NextSibling(SETTING_XML_ELM_DEPENDENCY);
143 }
144 }
145
146 auto control = node->FirstChildElement(SETTING_XML_ELM_CONTROL);
147 if (control != nullptr)
148 {
149 auto controlType = control->Attribute(SETTING_XML_ATTR_TYPE);
150 if (controlType == nullptr)
151 {
152 s_logger->error("error reading \"{}\" attribute of <control> tag of \"{}\"",
153 SETTING_XML_ATTR_TYPE, m_id);
154 return false;
155 }
156
157 m_control = m_settingsManager->CreateControl(controlType);
158 if (m_control == nullptr || !m_control->Deserialize(control, update))
159 {
160 s_logger->error("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_CONTROL, m_id);
161 return false;
162 }
163 }
164 else if (!update && m_level < SettingLevel::Internal && !IsReference())
165 {
166 s_logger->error("missing <{}> tag of \"{}\"", SETTING_XML_ELM_CONTROL, m_id);
167 return false;
168 }
169
170 auto updates = node->FirstChild(SETTING_XML_ELM_UPDATES);
171 if (updates != nullptr)
172 {
173 auto updateElem = updates->FirstChildElement(SETTING_XML_ELM_UPDATE);
174 while (updateElem != nullptr)
175 {
176 CSettingUpdate settingUpdate;
177 if (settingUpdate.Deserialize(updateElem))
178 {
179 if (!m_updates.insert(settingUpdate).second)
180 s_logger->warn("duplicate <{}> definition for \"{}\"", SETTING_XML_ELM_UPDATE, m_id);
181 }
182 else
183 s_logger->warn("error reading <{}> tag of \"{}\"", SETTING_XML_ELM_UPDATE, m_id);
184
185 updateElem = updateElem->NextSiblingElement(SETTING_XML_ELM_UPDATE);
186 }
187 }
188
189 return true;
190 }
191
IsEnabled() const192 bool CSetting::IsEnabled() const
193 {
194 if (m_dependencies.empty() && m_parentSetting.empty())
195 return m_enabled;
196
197 // if the setting has a parent setting and that parent setting is disabled
198 // the setting should automatically also be disabled
199 if (!m_parentSetting.empty())
200 {
201 SettingPtr parentSetting = m_settingsManager->GetSetting(m_parentSetting);
202 if (parentSetting != nullptr && !parentSetting->IsEnabled())
203 return false;
204 }
205
206 bool enabled = m_enabled;
207 for (const auto& dep : m_dependencies)
208 {
209 if (dep.GetType() != SettingDependencyType::Enable)
210 continue;
211
212 if (!dep.Check())
213 {
214 enabled = false;
215 break;
216 }
217 }
218
219 return enabled;
220 }
221
SetEnabled(bool enabled)222 void CSetting::SetEnabled(bool enabled)
223 {
224 if (!m_dependencies.empty() || m_enabled == enabled)
225 return;
226
227 m_enabled = enabled;
228 OnSettingPropertyChanged(shared_from_this(), "enabled");
229 }
230
MakeReference(const std::string & referencedId)231 void CSetting::MakeReference(const std::string& referencedId /* = "" */)
232 {
233 auto tmpReferencedId = referencedId;
234 if (referencedId.empty())
235 tmpReferencedId = m_id;
236
237 m_id = StringUtils::Format("#{}[{}]", tmpReferencedId, StringUtils::CreateUUID());
238 m_referencedId = tmpReferencedId;
239 }
240
IsVisible() const241 bool CSetting::IsVisible() const
242 {
243 if (!ISetting::IsVisible())
244 return false;
245
246 bool visible = true;
247 for (const auto& dep : m_dependencies)
248 {
249 if (dep.GetType() != SettingDependencyType::Visible)
250 continue;
251
252 if (!dep.Check())
253 {
254 visible = false;
255 break;
256 }
257 }
258
259 return visible;
260 }
261
OnSettingChanging(const std::shared_ptr<const CSetting> & setting)262 bool CSetting::OnSettingChanging(const std::shared_ptr<const CSetting>& setting)
263 {
264 if (m_callback == nullptr)
265 return true;
266
267 return m_callback->OnSettingChanging(setting);
268 }
269
OnSettingChanged(const std::shared_ptr<const CSetting> & setting)270 void CSetting::OnSettingChanged(const std::shared_ptr<const CSetting>& setting)
271 {
272 if (m_callback == nullptr)
273 return;
274
275 m_callback->OnSettingChanged(setting);
276 }
277
OnSettingAction(const std::shared_ptr<const CSetting> & setting)278 void CSetting::OnSettingAction(const std::shared_ptr<const CSetting>& setting)
279 {
280 if (m_callback == nullptr)
281 return;
282
283 m_callback->OnSettingAction(setting);
284 }
285
OnSettingUpdate(const std::shared_ptr<CSetting> & setting,const char * oldSettingId,const TiXmlNode * oldSettingNode)286 bool CSetting::OnSettingUpdate(const std::shared_ptr<CSetting>& setting,
287 const char* oldSettingId,
288 const TiXmlNode* oldSettingNode)
289 {
290 if (m_callback == nullptr)
291 return false;
292
293 return m_callback->OnSettingUpdate(setting, oldSettingId, oldSettingNode);
294 }
295
OnSettingPropertyChanged(const std::shared_ptr<const CSetting> & setting,const char * propertyName)296 void CSetting::OnSettingPropertyChanged(const std::shared_ptr<const CSetting>& setting,
297 const char* propertyName)
298 {
299 if (m_callback == nullptr)
300 return;
301
302 m_callback->OnSettingPropertyChanged(setting, propertyName);
303 }
304
Copy(const CSetting & setting)305 void CSetting::Copy(const CSetting &setting)
306 {
307 SetVisible(setting.IsVisible());
308 SetLabel(setting.GetLabel());
309 SetHelp(setting.GetHelp());
310 SetRequirementsMet(setting.MeetsRequirements());
311 m_callback = setting.m_callback;
312 m_level = setting.m_level;
313
314 if (setting.m_control != nullptr)
315 {
316 m_control = m_settingsManager->CreateControl(setting.m_control->GetType());
317 *m_control = *setting.m_control;
318 }
319 else
320 m_control = nullptr;
321
322 m_dependencies = setting.m_dependencies;
323 m_updates = setting.m_updates;
324 m_changed = setting.m_changed;
325 }
326
CSettingList(const std::string & id,std::shared_ptr<CSetting> settingDefinition,CSettingsManager * settingsManager)327 CSettingList::CSettingList(const std::string& id,
328 std::shared_ptr<CSetting> settingDefinition,
329 CSettingsManager* settingsManager /* = nullptr */)
330 : CSetting(id, settingsManager, "CSettingList"), m_definition(std::move(settingDefinition))
331 { }
332
CSettingList(const std::string & id,std::shared_ptr<CSetting> settingDefinition,int label,CSettingsManager * settingsManager)333 CSettingList::CSettingList(const std::string& id,
334 std::shared_ptr<CSetting> settingDefinition,
335 int label,
336 CSettingsManager* settingsManager /* = nullptr */)
337 : CSetting(id, settingsManager, "CSettingList"), m_definition(std::move(settingDefinition))
338 {
339 SetLabel(label);
340 }
341
CSettingList(const std::string & id,const CSettingList & setting)342 CSettingList::CSettingList(const std::string &id, const CSettingList &setting)
343 : CSetting(id, setting)
344 {
345 copy(setting);
346 }
347
Clone(const std::string & id) const348 SettingPtr CSettingList::Clone(const std::string &id) const
349 {
350 if (m_definition == nullptr)
351 return nullptr;
352
353 return std::make_shared<CSettingList>(id, *this);
354 }
355
MergeDetails(const CSetting & other)356 void CSettingList::MergeDetails(const CSetting& other)
357 {
358 if (other.GetType() != SettingType::List)
359 return;
360
361 const auto& listSetting = static_cast<const CSettingList&>(other);
362 if (m_definition == nullptr && listSetting.m_definition != nullptr)
363 m_definition = listSetting.m_definition;
364 if (m_defaults.empty() && !listSetting.m_defaults.empty())
365 m_defaults = listSetting.m_defaults;
366 if (m_values.empty() && !listSetting.m_values.empty())
367 m_values = listSetting.m_values;
368 if (m_delimiter == "|" && listSetting.m_delimiter != "|")
369 m_delimiter = listSetting.m_delimiter;
370 if (m_minimumItems == 0 && listSetting.m_minimumItems != 0)
371 m_minimumItems = listSetting.m_minimumItems;
372 if (m_maximumItems == -1 && listSetting.m_maximumItems != -1)
373 m_maximumItems = listSetting.m_maximumItems;
374 }
375
Deserialize(const TiXmlNode * node,bool update)376 bool CSettingList::Deserialize(const TiXmlNode *node, bool update /* = false */)
377 {
378 CExclusiveLock lock(m_critical);
379
380 if (m_definition == nullptr)
381 return false;
382
383 if (!CSetting::Deserialize(node, update))
384 return false;
385
386 auto element = node->ToElement();
387 if (element == nullptr)
388 {
389 s_logger->warn("unable to read type of list setting of {}", m_id);
390 return false;
391 }
392
393 // deserialize the setting definition in update mode because we don't care
394 // about an invalid <default> value (which is never used)
395 if (!m_definition->Deserialize(node, true))
396 return false;
397
398 auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
399 if (constraints != nullptr)
400 {
401 // read the delimiter
402 std::string delimiter;
403 if (XMLUtils::GetString(constraints, SETTING_XML_ELM_DELIMITER, delimiter) && !delimiter.empty())
404 m_delimiter = delimiter;
405
406 XMLUtils::GetInt(constraints, SETTING_XML_ELM_MINIMUM_ITEMS, m_minimumItems);
407 if (m_minimumItems < 0)
408 m_minimumItems = 0;
409 XMLUtils::GetInt(constraints, SETTING_XML_ELM_MAXIMUM_ITEMS, m_maximumItems);
410 if (m_maximumItems <= 0)
411 m_maximumItems = -1;
412 else if (m_maximumItems < m_minimumItems)
413 {
414 s_logger->warn("invalid <{}> ({}) and/or <{}> ({}) of {}", SETTING_XML_ELM_MINIMUM_ITEMS,
415 m_minimumItems, SETTING_XML_ELM_MAXIMUM_ITEMS, m_maximumItems, m_id);
416 return false;
417 }
418 }
419
420 // read the default and initial values
421 std::string values;
422 if (XMLUtils::GetString(node, SETTING_XML_ELM_DEFAULT, values))
423 {
424 if (!fromString(values, m_defaults))
425 {
426 s_logger->warn("invalid <{}> definition \"{}\" of {}", SETTING_XML_ELM_DEFAULT, values, m_id);
427 return false;
428 }
429 Reset();
430 }
431
432 return true;
433 }
434
GetElementType() const435 SettingType CSettingList::GetElementType() const
436 {
437 CSharedLock lock(m_critical);
438
439 if (m_definition == nullptr)
440 return SettingType::Unknown;
441
442 return m_definition->GetType();
443 }
444
FromString(const std::string & value)445 bool CSettingList::FromString(const std::string &value)
446 {
447 SettingList values;
448 if (!fromString(value, values))
449 return false;
450
451 return SetValue(values);
452 }
453
ToString() const454 std::string CSettingList::ToString() const
455 {
456 return toString(m_values);
457 }
458
Equals(const std::string & value) const459 bool CSettingList::Equals(const std::string &value) const
460 {
461 SettingList values;
462 if (!fromString(value, values) || values.size() != m_values.size())
463 return false;
464
465 bool ret = true;
466 for (size_t index = 0; index < values.size(); index++)
467 {
468 if (!m_values[index]->Equals(values[index]->ToString()))
469 {
470 ret = false;
471 break;
472 }
473 }
474
475 return ret;
476 }
477
CheckValidity(const std::string & value) const478 bool CSettingList::CheckValidity(const std::string &value) const
479 {
480 SettingList values;
481 return fromString(value, values);
482 }
483
Reset()484 void CSettingList::Reset()
485 {
486 CExclusiveLock lock(m_critical);
487 SettingList values;
488 for (const auto& it : m_defaults)
489 values.push_back(it->Clone(it->GetId()));
490
491 SetValue(values);
492 }
493
FromString(const std::vector<std::string> & value)494 bool CSettingList::FromString(const std::vector<std::string> &value)
495 {
496 SettingList values;
497 if (!fromValues(value, values))
498 return false;
499
500 return SetValue(values);
501 }
502
SetValue(const SettingList & values)503 bool CSettingList::SetValue(const SettingList &values)
504 {
505 CExclusiveLock lock(m_critical);
506
507 if ((int)values.size() < m_minimumItems ||
508 (m_maximumItems > 0 && (int)values.size() > m_maximumItems))
509 return false;
510
511 bool equal = values.size() == m_values.size();
512 for (size_t index = 0; index < values.size(); index++)
513 {
514 if (values[index]->GetType() != GetElementType())
515 return false;
516
517 if (equal &&
518 !values[index]->Equals(m_values[index]->ToString()))
519 equal = false;
520 }
521
522 if (equal)
523 return true;
524
525 SettingList oldValues = m_values;
526 m_values.clear();
527 m_values.insert(m_values.begin(), values.begin(), values.end());
528
529 if (!OnSettingChanging(shared_from_base<CSettingList>()))
530 {
531 m_values = oldValues;
532
533 // the setting couldn't be changed because one of the
534 // callback handlers failed the OnSettingChanging()
535 // callback so we need to let all the callback handlers
536 // know that the setting hasn't changed
537 OnSettingChanging(shared_from_base<CSettingList>());
538 return false;
539 }
540
541 m_changed = toString(m_values) != toString(m_defaults);
542 OnSettingChanged(shared_from_base<CSettingList>());
543 return true;
544 }
545
SetDefault(const SettingList & values)546 void CSettingList::SetDefault(const SettingList &values)
547 {
548 CExclusiveLock lock(m_critical);
549
550 m_defaults.clear();
551 m_defaults.insert(m_defaults.begin(), values.begin(), values.end());
552
553 if (!m_changed)
554 {
555 m_values.clear();
556 for (const auto& it : m_defaults)
557 m_values.push_back(it->Clone(it->GetId()));
558 }
559 }
560
copy(const CSettingList & setting)561 void CSettingList::copy(const CSettingList &setting)
562 {
563 CSetting::Copy(setting);
564
565 copy(setting.m_values, m_values);
566 copy(setting.m_defaults, m_defaults);
567
568 if (setting.m_definition != nullptr)
569 {
570 auto definitionCopy = setting.m_definition->Clone(m_id + ".definition");
571 if (definitionCopy != nullptr)
572 m_definition = definitionCopy;
573 }
574
575 m_delimiter = setting.m_delimiter;
576 m_minimumItems = setting.m_minimumItems;
577 m_maximumItems = setting.m_maximumItems;
578 }
579
copy(const SettingList & srcValues,SettingList & dstValues)580 void CSettingList::copy(const SettingList &srcValues, SettingList &dstValues)
581 {
582 dstValues.clear();
583
584 for (const auto& value : srcValues)
585 {
586 if (value == nullptr)
587 continue;
588
589 SettingPtr valueCopy = value->Clone(value->GetId());
590 if (valueCopy == nullptr)
591 continue;
592
593 dstValues.emplace_back(valueCopy);
594 }
595 }
596
fromString(const std::string & strValue,SettingList & values) const597 bool CSettingList::fromString(const std::string &strValue, SettingList &values) const
598 {
599 return fromValues(StringUtils::Split(strValue, m_delimiter), values);
600 }
601
fromValues(const std::vector<std::string> & strValues,SettingList & values) const602 bool CSettingList::fromValues(const std::vector<std::string> &strValues, SettingList &values) const
603 {
604 if ((int)strValues.size() < m_minimumItems ||
605 (m_maximumItems > 0 && (int)strValues.size() > m_maximumItems))
606 return false;
607
608 bool ret = true;
609 int index = 0;
610 for (const auto& value : strValues)
611 {
612 auto settingValue = m_definition->Clone(StringUtils::Format("{}.{}", m_id, index++));
613 if (settingValue == nullptr ||
614 !settingValue->FromString(value))
615 {
616 ret = false;
617 break;
618 }
619
620 values.emplace_back(settingValue);
621 }
622
623 if (!ret)
624 values.clear();
625
626 return ret;
627 }
628
toString(const SettingList & values) const629 std::string CSettingList::toString(const SettingList &values) const
630 {
631 std::vector<std::string> strValues;
632 for (const auto& value : values)
633 {
634 if (value != nullptr)
635 strValues.push_back(value->ToString());
636 }
637
638 return StringUtils::Join(strValues, m_delimiter);
639 }
640
CSettingBool(const std::string & id,CSettingsManager * settingsManager)641 CSettingBool::CSettingBool(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
642 : CTraitedSetting(id, settingsManager, "CSettingBool")
643 { }
644
CSettingBool(const std::string & id,const CSettingBool & setting)645 CSettingBool::CSettingBool(const std::string& id, const CSettingBool& setting)
646 : CTraitedSetting(id, setting, "CSettingBool")
647 {
648 copy(setting);
649 }
650
CSettingBool(const std::string & id,int label,bool value,CSettingsManager * settingsManager)651 CSettingBool::CSettingBool(const std::string& id,
652 int label,
653 bool value,
654 CSettingsManager* settingsManager /* = nullptr */)
655 : CTraitedSetting(id, settingsManager, "CSettingBool"), m_value(value), m_default(value)
656 {
657 SetLabel(label);
658 }
659
Clone(const std::string & id) const660 SettingPtr CSettingBool::Clone(const std::string &id) const
661 {
662 return std::make_shared<CSettingBool>(id, *this);
663 }
664
MergeDetails(const CSetting & other)665 void CSettingBool::MergeDetails(const CSetting& other)
666 {
667 if (other.GetType() != SettingType::Boolean)
668 return;
669
670 const auto& boolSetting = static_cast<const CSettingBool&>(other);
671 if (m_default == false && boolSetting.m_default == true)
672 m_default = boolSetting.m_default;
673 if (m_value == m_default && boolSetting.m_value != m_default)
674 m_value = boolSetting.m_value;
675 }
676
Deserialize(const TiXmlNode * node,bool update)677 bool CSettingBool::Deserialize(const TiXmlNode *node, bool update /* = false */)
678 {
679 CExclusiveLock lock(m_critical);
680
681 if (!CSetting::Deserialize(node, update))
682 return false;
683
684 // get the default value
685 bool value;
686 if (XMLUtils::GetBoolean(node, SETTING_XML_ELM_DEFAULT, value))
687 m_value = m_default = value;
688 else if (!update)
689 {
690 s_logger->error("error reading the default value of \"{}\"", m_id);
691 return false;
692 }
693
694 return true;
695 }
696
FromString(const std::string & value)697 bool CSettingBool::FromString(const std::string &value)
698 {
699 bool bValue;
700 if (!fromString(value, bValue))
701 return false;
702
703 return SetValue(bValue);
704 }
705
ToString() const706 std::string CSettingBool::ToString() const
707 {
708 return m_value ? "true" : "false";
709 }
710
Equals(const std::string & value) const711 bool CSettingBool::Equals(const std::string &value) const
712 {
713 bool bValue;
714 return (fromString(value, bValue) && m_value == bValue);
715 }
716
CheckValidity(const std::string & value) const717 bool CSettingBool::CheckValidity(const std::string &value) const
718 {
719 bool bValue;
720 return fromString(value, bValue);
721 }
722
SetValue(bool value)723 bool CSettingBool::SetValue(bool value)
724 {
725 CExclusiveLock lock(m_critical);
726
727 if (value == m_value)
728 return true;
729
730 bool oldValue = m_value;
731 m_value = value;
732
733 if (!OnSettingChanging(shared_from_base<CSettingBool>()))
734 {
735 m_value = oldValue;
736
737 // the setting couldn't be changed because one of the
738 // callback handlers failed the OnSettingChanging()
739 // callback so we need to let all the callback handlers
740 // know that the setting hasn't changed
741 OnSettingChanging(shared_from_base<CSettingBool>());
742 return false;
743 }
744
745 m_changed = m_value != m_default;
746 OnSettingChanged(shared_from_base<CSettingBool>());
747 return true;
748 }
749
SetDefault(bool value)750 void CSettingBool::SetDefault(bool value)
751 {
752 CExclusiveLock lock(m_critical);
753
754 m_default = value;
755 if (!m_changed)
756 m_value = m_default;
757 }
758
copy(const CSettingBool & setting)759 void CSettingBool::copy(const CSettingBool &setting)
760 {
761 CSetting::Copy(setting);
762
763 m_value = setting.m_value;
764 m_default = setting.m_default;
765 }
766
fromString(const std::string & strValue,bool & value) const767 bool CSettingBool::fromString(const std::string &strValue, bool &value) const
768 {
769 if (StringUtils::EqualsNoCase(strValue, "true"))
770 {
771 value = true;
772 return true;
773 }
774 if (StringUtils::EqualsNoCase(strValue, "false"))
775 {
776 value = false;
777 return true;
778 }
779
780 return false;
781 }
782
CSettingInt(const std::string & id,CSettingsManager * settingsManager)783 CSettingInt::CSettingInt(const std::string& id, CSettingsManager* settingsManager /* = nullptr */)
784 : CTraitedSetting(id, settingsManager, "CSettingInt")
785 { }
786
CSettingInt(const std::string & id,const CSettingInt & setting)787 CSettingInt::CSettingInt(const std::string& id, const CSettingInt& setting)
788 : CTraitedSetting(id, setting, "CSettingInt")
789 {
790 copy(setting);
791 }
792
CSettingInt(const std::string & id,int label,int value,CSettingsManager * settingsManager)793 CSettingInt::CSettingInt(const std::string& id,
794 int label,
795 int value,
796 CSettingsManager* settingsManager /* = nullptr */)
797 : CTraitedSetting(id, settingsManager, "CSettingInt"), m_value(value), m_default(value)
798 {
799 SetLabel(label);
800 }
801
CSettingInt(const std::string & id,int label,int value,int minimum,int step,int maximum,CSettingsManager * settingsManager)802 CSettingInt::CSettingInt(const std::string& id,
803 int label,
804 int value,
805 int minimum,
806 int step,
807 int maximum,
808 CSettingsManager* settingsManager /* = nullptr */)
809 : CTraitedSetting(id, settingsManager, "CSettingInt"),
810 m_value(value),
811 m_default(value),
812 m_min(minimum),
813 m_step(step),
814 m_max(maximum)
815 {
816 SetLabel(label);
817 }
818
CSettingInt(const std::string & id,int label,int value,const TranslatableIntegerSettingOptions & options,CSettingsManager * settingsManager)819 CSettingInt::CSettingInt(const std::string& id,
820 int label,
821 int value,
822 const TranslatableIntegerSettingOptions& options,
823 CSettingsManager* settingsManager /* = nullptr */)
824 : CTraitedSetting(id, settingsManager, "CSettingInt"),
825 m_value(value),
826 m_default(value),
827 m_translatableOptions(options)
828 {
829 SetLabel(label);
830 }
831
Clone(const std::string & id) const832 SettingPtr CSettingInt::Clone(const std::string &id) const
833 {
834 return std::make_shared<CSettingInt>(id, *this);
835 }
836
MergeDetails(const CSetting & other)837 void CSettingInt::MergeDetails(const CSetting& other)
838 {
839 if (other.GetType() != SettingType::Integer)
840 return;
841
842 const auto& intSetting = static_cast<const CSettingInt&>(other);
843 if (m_default == 0.0 && intSetting.m_default != 0.0)
844 m_default = intSetting.m_default;
845 if (m_value == m_default && intSetting.m_value != m_default)
846 m_value = intSetting.m_value;
847 if (m_min == 0.0 && intSetting.m_min != 0.0)
848 m_min = intSetting.m_min;
849 if (m_step == 1.0 && intSetting.m_step != 1.0)
850 m_step = intSetting.m_step;
851 if (m_max == 0.0 && intSetting.m_max != 0.0)
852 m_max = intSetting.m_max;
853 if (m_translatableOptions.empty() && !intSetting.m_translatableOptions.empty())
854 m_translatableOptions = intSetting.m_translatableOptions;
855 if (m_options.empty() && !intSetting.m_options.empty())
856 m_options = intSetting.m_options;
857 if (m_optionsFillerName.empty() && !intSetting.m_optionsFillerName.empty())
858 m_optionsFillerName = intSetting.m_optionsFillerName;
859 if (m_optionsFiller == nullptr && intSetting.m_optionsFiller != nullptr)
860 m_optionsFiller = intSetting.m_optionsFiller;
861 if (m_optionsFillerData == nullptr && intSetting.m_optionsFillerData != nullptr)
862 m_optionsFillerData = intSetting.m_optionsFillerData;
863 if (m_dynamicOptions.empty() && !intSetting.m_dynamicOptions.empty())
864 m_dynamicOptions = intSetting.m_dynamicOptions;
865 if (m_optionsSort == SettingOptionsSort::NoSorting &&
866 intSetting.m_optionsSort != SettingOptionsSort::NoSorting)
867 m_optionsSort = intSetting.m_optionsSort;
868 }
869
Deserialize(const TiXmlNode * node,bool update)870 bool CSettingInt::Deserialize(const TiXmlNode *node, bool update /* = false */)
871 {
872 CExclusiveLock lock(m_critical);
873
874 if (!CSetting::Deserialize(node, update))
875 return false;
876
877 // get the default value
878 int value;
879 if (XMLUtils::GetInt(node, SETTING_XML_ELM_DEFAULT, value))
880 m_value = m_default = value;
881 else if (!update)
882 {
883 s_logger->error("error reading the default value of \"{}\"", m_id);
884 return false;
885 }
886
887 auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
888 if (constraints != nullptr)
889 {
890 // get the entries
891 auto options = constraints->FirstChildElement(SETTING_XML_ELM_OPTIONS);
892 if (options != nullptr && options->FirstChild() != nullptr)
893 {
894 if (!DeserializeOptionsSort(options, m_optionsSort))
895 s_logger->warn("invalid \"sort\" attribute of <" SETTING_XML_ELM_OPTIONS "> for \"{}\"",
896 m_id);
897
898 if (options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
899 {
900 m_optionsFillerName = options->FirstChild()->ValueStr();
901 if (!m_optionsFillerName.empty())
902 {
903 m_optionsFiller = reinterpret_cast<IntegerSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingInt>()));
904 }
905 }
906 else
907 {
908 m_translatableOptions.clear();
909 auto optionElement = options->FirstChildElement(SETTING_XML_ELM_OPTION);
910 while (optionElement != nullptr)
911 {
912 TranslatableIntegerSettingOption entry;
913 if (optionElement->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &entry.label) ==
914 TIXML_SUCCESS &&
915 entry.label > 0)
916 {
917 entry.value = strtol(optionElement->FirstChild()->Value(), nullptr, 10);
918 m_translatableOptions.push_back(entry);
919 }
920 else
921 {
922 std::string label;
923 if (optionElement->QueryStringAttribute(SETTING_XML_ATTR_LABEL, &label) ==
924 TIXML_SUCCESS)
925 {
926 int value = strtol(optionElement->FirstChild()->Value(), nullptr, 10);
927 m_options.emplace_back(label, value);
928 }
929 }
930
931 optionElement = optionElement->NextSiblingElement(SETTING_XML_ELM_OPTION);
932 }
933 }
934 }
935
936 // get minimum
937 XMLUtils::GetInt(constraints, SETTING_XML_ELM_MINIMUM, m_min);
938 // get step
939 XMLUtils::GetInt(constraints, SETTING_XML_ELM_STEP, m_step);
940 // get maximum
941 XMLUtils::GetInt(constraints, SETTING_XML_ELM_MAXIMUM, m_max);
942 }
943
944 return true;
945 }
946
FromString(const std::string & value)947 bool CSettingInt::FromString(const std::string &value)
948 {
949 int iValue;
950 if (!fromString(value, iValue))
951 return false;
952
953 return SetValue(iValue);
954 }
955
ToString() const956 std::string CSettingInt::ToString() const
957 {
958 std::ostringstream oss;
959 oss << m_value;
960
961 return oss.str();
962 }
963
Equals(const std::string & value) const964 bool CSettingInt::Equals(const std::string &value) const
965 {
966 int iValue;
967 return (fromString(value, iValue) && m_value == iValue);
968 }
969
CheckValidity(const std::string & value) const970 bool CSettingInt::CheckValidity(const std::string &value) const
971 {
972 int iValue;
973 if (!fromString(value, iValue))
974 return false;
975
976 return CheckValidity(iValue);
977 }
978
CheckValidity(int value) const979 bool CSettingInt::CheckValidity(int value) const
980 {
981 if (!m_translatableOptions.empty())
982 {
983 if (!CheckSettingOptionsValidity(value, m_translatableOptions))
984 return false;
985 }
986 else if (!m_options.empty())
987 {
988 if (!CheckSettingOptionsValidity(value, m_options))
989 return false;
990 }
991 else if (m_optionsFillerName.empty() && m_optionsFiller == nullptr &&
992 m_min != m_max && (value < m_min || value > m_max))
993 return false;
994
995 return true;
996 }
997
SetValue(int value)998 bool CSettingInt::SetValue(int value)
999 {
1000 CExclusiveLock lock(m_critical);
1001
1002 if (value == m_value)
1003 return true;
1004
1005 if (!CheckValidity(value))
1006 return false;
1007
1008 int oldValue = m_value;
1009 m_value = value;
1010
1011 if (!OnSettingChanging(shared_from_base<CSettingInt>()))
1012 {
1013 m_value = oldValue;
1014
1015 // the setting couldn't be changed because one of the
1016 // callback handlers failed the OnSettingChanging()
1017 // callback so we need to let all the callback handlers
1018 // know that the setting hasn't changed
1019 OnSettingChanging(shared_from_base<CSettingInt>());
1020 return false;
1021 }
1022
1023 m_changed = m_value != m_default;
1024 OnSettingChanged(shared_from_base<CSettingInt>());
1025 return true;
1026 }
1027
SetDefault(int value)1028 void CSettingInt::SetDefault(int value)
1029 {
1030 CExclusiveLock lock(m_critical);
1031
1032 m_default = value;
1033 if (!m_changed)
1034 m_value = m_default;
1035 }
1036
GetOptionsType() const1037 SettingOptionsType CSettingInt::GetOptionsType() const
1038 {
1039 CSharedLock lock(m_critical);
1040 if (!m_translatableOptions.empty())
1041 return SettingOptionsType::StaticTranslatable;
1042 if (!m_options.empty())
1043 return SettingOptionsType::Static;
1044 if (!m_optionsFillerName.empty() || m_optionsFiller != nullptr)
1045 return SettingOptionsType::Dynamic;
1046
1047 return SettingOptionsType::Unknown;
1048 }
1049
UpdateDynamicOptions()1050 IntegerSettingOptions CSettingInt::UpdateDynamicOptions()
1051 {
1052 CExclusiveLock lock(m_critical);
1053 IntegerSettingOptions options;
1054 if (m_optionsFiller == nullptr &&
1055 (m_optionsFillerName.empty() || m_settingsManager == nullptr))
1056 return options;
1057
1058 if (m_optionsFiller == nullptr)
1059 {
1060 m_optionsFiller = reinterpret_cast<IntegerSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingInt>()));
1061 if (m_optionsFiller == nullptr)
1062 {
1063 s_logger->warn("unknown options filler \"{}\" of \"{}\"", m_optionsFillerName, m_id);
1064 return options;
1065 }
1066 }
1067
1068 int bestMatchingValue = m_value;
1069 m_optionsFiller(shared_from_base<CSettingInt>(), options, bestMatchingValue, m_optionsFillerData);
1070
1071 if (bestMatchingValue != m_value)
1072 SetValue(bestMatchingValue);
1073
1074 bool changed = m_dynamicOptions.size() != options.size();
1075 if (!changed)
1076 {
1077 for (size_t index = 0; index < options.size(); index++)
1078 {
1079 if (options[index].label.compare(m_dynamicOptions[index].label) != 0 ||
1080 options[index].value != m_dynamicOptions[index].value)
1081 {
1082 changed = true;
1083 break;
1084 }
1085 }
1086 }
1087
1088 if (changed)
1089 {
1090 m_dynamicOptions = options;
1091 OnSettingPropertyChanged(shared_from_base<CSettingInt>(), "options");
1092 }
1093
1094 return options;
1095 }
1096
copy(const CSettingInt & setting)1097 void CSettingInt::copy(const CSettingInt &setting)
1098 {
1099 CSetting::Copy(setting);
1100
1101 CExclusiveLock lock(m_critical);
1102
1103 m_value = setting.m_value;
1104 m_default = setting.m_default;
1105 m_min = setting.m_min;
1106 m_step = setting.m_step;
1107 m_max = setting.m_max;
1108 m_translatableOptions = setting.m_translatableOptions;
1109 m_options = setting.m_options;
1110 m_optionsFillerName = setting.m_optionsFillerName;
1111 m_optionsFiller = setting.m_optionsFiller;
1112 m_optionsFillerData = setting.m_optionsFillerData;
1113 m_dynamicOptions = setting.m_dynamicOptions;
1114 }
1115
fromString(const std::string & strValue,int & value)1116 bool CSettingInt::fromString(const std::string &strValue, int &value)
1117 {
1118 if (strValue.empty())
1119 return false;
1120
1121 char *end = nullptr;
1122 value = (int)strtol(strValue.c_str(), &end, 10);
1123 if (end != nullptr && *end != '\0')
1124 return false;
1125
1126 return true;
1127 }
1128
CSettingNumber(const std::string & id,CSettingsManager * settingsManager)1129 CSettingNumber::CSettingNumber(const std::string& id,
1130 CSettingsManager* settingsManager /* = nullptr */)
1131 : CTraitedSetting(id, settingsManager, "CSettingNumber")
1132 { }
1133
CSettingNumber(const std::string & id,const CSettingNumber & setting)1134 CSettingNumber::CSettingNumber(const std::string& id, const CSettingNumber& setting)
1135 : CTraitedSetting(id, setting, "CSettingNumber")
1136 {
1137 copy(setting);
1138 }
1139
CSettingNumber(const std::string & id,int label,float value,CSettingsManager * settingsManager)1140 CSettingNumber::CSettingNumber(const std::string& id,
1141 int label,
1142 float value,
1143 CSettingsManager* settingsManager /* = nullptr */)
1144 : CTraitedSetting(id, settingsManager, "CSettingNumber"), m_value(value), m_default(value)
1145 {
1146 SetLabel(label);
1147 }
1148
CSettingNumber(const std::string & id,int label,float value,float minimum,float step,float maximum,CSettingsManager * settingsManager)1149 CSettingNumber::CSettingNumber(const std::string& id,
1150 int label,
1151 float value,
1152 float minimum,
1153 float step,
1154 float maximum,
1155 CSettingsManager* settingsManager /* = nullptr */)
1156 : CTraitedSetting(id, settingsManager, "CSettingNumber"),
1157 m_value(value),
1158 m_default(value),
1159 m_min(minimum),
1160 m_step(step),
1161 m_max(maximum)
1162 {
1163 SetLabel(label);
1164 }
1165
Clone(const std::string & id) const1166 SettingPtr CSettingNumber::Clone(const std::string &id) const
1167 {
1168 return std::make_shared<CSettingNumber>(id, *this);
1169 }
1170
MergeDetails(const CSetting & other)1171 void CSettingNumber::MergeDetails(const CSetting& other)
1172 {
1173 if (other.GetType() != SettingType::Number)
1174 return;
1175
1176 const auto& numberSetting = static_cast<const CSettingNumber&>(other);
1177 if (m_default == 0.0 && numberSetting.m_default != 0.0)
1178 m_default = numberSetting.m_default;
1179 if (m_value == m_default && numberSetting.m_value != m_default)
1180 m_value = numberSetting.m_value;
1181 if (m_min == 0.0 && numberSetting.m_min != 0.0)
1182 m_min = numberSetting.m_min;
1183 if (m_step == 1.0 && numberSetting.m_step != 1.0)
1184 m_step = numberSetting.m_step;
1185 if (m_max == 0.0 && numberSetting.m_max != 0.0)
1186 m_max = numberSetting.m_max;
1187 }
1188
Deserialize(const TiXmlNode * node,bool update)1189 bool CSettingNumber::Deserialize(const TiXmlNode *node, bool update /* = false */)
1190 {
1191 CExclusiveLock lock(m_critical);
1192
1193 if (!CSetting::Deserialize(node, update))
1194 return false;
1195
1196 // get the default value
1197 double value;
1198 if (XMLUtils::GetDouble(node, SETTING_XML_ELM_DEFAULT, value))
1199 m_value = m_default = value;
1200 else if (!update)
1201 {
1202 s_logger->error("error reading the default value of \"{}\"", m_id);
1203 return false;
1204 }
1205
1206 auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
1207 if (constraints != nullptr)
1208 {
1209 // get the minimum value
1210 XMLUtils::GetDouble(constraints, SETTING_XML_ELM_MINIMUM, m_min);
1211 // get the step value
1212 XMLUtils::GetDouble(constraints, SETTING_XML_ELM_STEP, m_step);
1213 // get the maximum value
1214 XMLUtils::GetDouble(constraints, SETTING_XML_ELM_MAXIMUM, m_max);
1215 }
1216
1217 return true;
1218 }
1219
FromString(const std::string & value)1220 bool CSettingNumber::FromString(const std::string &value)
1221 {
1222 double dValue;
1223 if (!fromString(value, dValue))
1224 return false;
1225
1226 return SetValue(dValue);
1227 }
1228
ToString() const1229 std::string CSettingNumber::ToString() const
1230 {
1231 std::ostringstream oss;
1232 oss << m_value;
1233
1234 return oss.str();
1235 }
1236
Equals(const std::string & value) const1237 bool CSettingNumber::Equals(const std::string &value) const
1238 {
1239 double dValue;
1240 CSharedLock lock(m_critical);
1241 return (fromString(value, dValue) && m_value == dValue);
1242 }
1243
CheckValidity(const std::string & value) const1244 bool CSettingNumber::CheckValidity(const std::string &value) const
1245 {
1246 double dValue;
1247 if (!fromString(value, dValue))
1248 return false;
1249
1250 return CheckValidity(dValue);
1251 }
1252
CheckValidity(double value) const1253 bool CSettingNumber::CheckValidity(double value) const
1254 {
1255 CSharedLock lock(m_critical);
1256 if (m_min != m_max &&
1257 (value < m_min || value > m_max))
1258 return false;
1259
1260 return true;
1261 }
1262
SetValue(double value)1263 bool CSettingNumber::SetValue(double value)
1264 {
1265 CExclusiveLock lock(m_critical);
1266
1267 if (value == m_value)
1268 return true;
1269
1270 if (!CheckValidity(value))
1271 return false;
1272
1273 double oldValue = m_value;
1274 m_value = value;
1275
1276 if (!OnSettingChanging(shared_from_base<CSettingNumber>()))
1277 {
1278 m_value = oldValue;
1279
1280 // the setting couldn't be changed because one of the
1281 // callback handlers failed the OnSettingChanging()
1282 // callback so we need to let all the callback handlers
1283 // know that the setting hasn't changed
1284 OnSettingChanging(shared_from_base<CSettingNumber>());
1285 return false;
1286 }
1287
1288 m_changed = m_value != m_default;
1289 OnSettingChanged(shared_from_base<CSettingNumber>());
1290 return true;
1291 }
1292
SetDefault(double value)1293 void CSettingNumber::SetDefault(double value)
1294 {
1295 CExclusiveLock lock(m_critical);
1296
1297 m_default = value;
1298 if (!m_changed)
1299 m_value = m_default;
1300 }
1301
copy(const CSettingNumber & setting)1302 void CSettingNumber::copy(const CSettingNumber &setting)
1303 {
1304 CSetting::Copy(setting);
1305 CExclusiveLock lock(m_critical);
1306
1307 m_value = setting.m_value;
1308 m_default = setting.m_default;
1309 m_min = setting.m_min;
1310 m_step = setting.m_step;
1311 m_max = setting.m_max;
1312 }
1313
fromString(const std::string & strValue,double & value)1314 bool CSettingNumber::fromString(const std::string &strValue, double &value)
1315 {
1316 if (strValue.empty())
1317 return false;
1318
1319 char *end = nullptr;
1320 value = strtod(strValue.c_str(), &end);
1321 if (end != nullptr && *end != '\0')
1322 return false;
1323
1324 return true;
1325 }
1326
CSettingString(const std::string & id,CSettingsManager * settingsManager)1327 CSettingString::CSettingString(const std::string& id,
1328 CSettingsManager* settingsManager /* = nullptr */)
1329 : CTraitedSetting(id, settingsManager, "CSettingString")
1330 { }
1331
CSettingString(const std::string & id,const CSettingString & setting)1332 CSettingString::CSettingString(const std::string& id, const CSettingString& setting)
1333 : CTraitedSetting(id, setting, "CSettingString")
1334 {
1335 copy(setting);
1336 }
1337
CSettingString(const std::string & id,int label,const std::string & value,CSettingsManager * settingsManager)1338 CSettingString::CSettingString(const std::string& id,
1339 int label,
1340 const std::string& value,
1341 CSettingsManager* settingsManager /* = nullptr */)
1342 : CTraitedSetting(id, settingsManager, "CSettingString"), m_value(value), m_default(value)
1343 {
1344 SetLabel(label);
1345 }
1346
Clone(const std::string & id) const1347 SettingPtr CSettingString::Clone(const std::string &id) const
1348 {
1349 return std::make_shared<CSettingString>(id, *this);
1350 }
1351
MergeDetails(const CSetting & other)1352 void CSettingString::MergeDetails(const CSetting& other)
1353 {
1354 if (other.GetType() != SettingType::String)
1355 return;
1356
1357 const auto& stringSetting = static_cast<const CSettingString&>(other);
1358 if (m_default.empty() && !stringSetting.m_default.empty())
1359 m_default = stringSetting.m_default;
1360 if (m_value == m_default && stringSetting.m_value != m_default)
1361 m_value = stringSetting.m_value;
1362 if (m_allowEmpty == false && stringSetting.m_allowEmpty == true)
1363 m_allowEmpty = stringSetting.m_allowEmpty;
1364 if (m_allowNewOption == false && stringSetting.m_allowNewOption == true)
1365 m_allowNewOption = stringSetting.m_allowNewOption;
1366 if (m_translatableOptions.empty() && !stringSetting.m_translatableOptions.empty())
1367 m_translatableOptions = stringSetting.m_translatableOptions;
1368 if (m_options.empty() && !stringSetting.m_options.empty())
1369 m_options = stringSetting.m_options;
1370 if (m_optionsFillerName.empty() && !stringSetting.m_optionsFillerName.empty())
1371 m_optionsFillerName = stringSetting.m_optionsFillerName;
1372 if (m_optionsFiller == nullptr && stringSetting.m_optionsFiller != nullptr)
1373 m_optionsFiller = stringSetting.m_optionsFiller;
1374 if (m_optionsFillerData == nullptr && stringSetting.m_optionsFillerData != nullptr)
1375 m_optionsFillerData = stringSetting.m_optionsFillerData;
1376 if (m_dynamicOptions.empty() && !stringSetting.m_dynamicOptions.empty())
1377 m_dynamicOptions = stringSetting.m_dynamicOptions;
1378 if (m_optionsSort == SettingOptionsSort::NoSorting &&
1379 stringSetting.m_optionsSort != SettingOptionsSort::NoSorting)
1380 m_optionsSort = stringSetting.m_optionsSort;
1381 }
1382
Deserialize(const TiXmlNode * node,bool update)1383 bool CSettingString::Deserialize(const TiXmlNode *node, bool update /* = false */)
1384 {
1385 CExclusiveLock lock(m_critical);
1386
1387 if (!CSetting::Deserialize(node, update))
1388 return false;
1389
1390 auto constraints = node->FirstChild(SETTING_XML_ELM_CONSTRAINTS);
1391 if (constraints != nullptr)
1392 {
1393 // get allowempty (needs to be parsed before parsing the default value)
1394 XMLUtils::GetBoolean(constraints, SETTING_XML_ELM_ALLOWEMPTY, m_allowEmpty);
1395
1396 // Values other than those in options contraints allowed to be added
1397 XMLUtils::GetBoolean(constraints, SETTING_XML_ELM_ALLOWNEWOPTION, m_allowNewOption);
1398
1399 // get the entries
1400 auto options = constraints->FirstChildElement(SETTING_XML_ELM_OPTIONS);
1401 if (options != nullptr && options->FirstChild() != nullptr)
1402 {
1403 if (!DeserializeOptionsSort(options, m_optionsSort))
1404 s_logger->warn("invalid \"sort\" attribute of <" SETTING_XML_ELM_OPTIONS "> for \"{}\"",
1405 m_id);
1406
1407 if (options->FirstChild()->Type() == TiXmlNode::TINYXML_TEXT)
1408 {
1409 m_optionsFillerName = options->FirstChild()->ValueStr();
1410 if (!m_optionsFillerName.empty())
1411 {
1412 m_optionsFiller = reinterpret_cast<StringSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingString>()));
1413 }
1414 }
1415 else
1416 {
1417 m_translatableOptions.clear();
1418 auto optionElement = options->FirstChildElement(SETTING_XML_ELM_OPTION);
1419 while (optionElement != nullptr)
1420 {
1421 TranslatableStringSettingOption entry;
1422 if (optionElement->QueryIntAttribute(SETTING_XML_ATTR_LABEL, &entry.first) == TIXML_SUCCESS && entry.first > 0)
1423 {
1424 entry.second = optionElement->FirstChild()->Value();
1425 m_translatableOptions.push_back(entry);
1426 }
1427 else
1428 {
1429 const std::string value = optionElement->FirstChild()->Value();
1430 // if a specific "label" attribute is present use it otherwise use the value as label
1431 std::string label = value;
1432 optionElement->QueryStringAttribute(SETTING_XML_ATTR_LABEL, &label);
1433
1434 m_options.emplace_back(label, value);
1435 }
1436
1437 optionElement = optionElement->NextSiblingElement(SETTING_XML_ELM_OPTION);
1438 }
1439 }
1440 }
1441 }
1442
1443 // get the default value
1444 std::string value;
1445 if (XMLUtils::GetString(node, SETTING_XML_ELM_DEFAULT, value) &&
1446 (!value.empty() || m_allowEmpty))
1447 m_value = m_default = value;
1448 else if (!update && !m_allowEmpty)
1449 {
1450 s_logger->error("error reading the default value of \"{}\"", m_id);
1451 return false;
1452 }
1453
1454 return true;
1455 }
1456
CheckValidity(const std::string & value) const1457 bool CSettingString::CheckValidity(const std::string &value) const
1458 {
1459 CSharedLock lock(m_critical);
1460 if (!m_allowEmpty && value.empty())
1461 return false;
1462
1463 if (!m_translatableOptions.empty())
1464 {
1465 if (!CheckSettingOptionsValidity(value, m_translatableOptions))
1466 return false;
1467 }
1468 else if (!m_options.empty() && !m_allowNewOption)
1469 {
1470 if (!CheckSettingOptionsValidity(value, m_options))
1471 return false;
1472 }
1473
1474 return true;
1475 }
1476
SetValue(const std::string & value)1477 bool CSettingString::SetValue(const std::string &value)
1478 {
1479 CExclusiveLock lock(m_critical);
1480
1481 if (value == m_value)
1482 return true;
1483
1484 if (!CheckValidity(value))
1485 return false;
1486
1487 std::string oldValue = m_value;
1488 m_value = value;
1489
1490 if (!OnSettingChanging(shared_from_base<CSettingString>()))
1491 {
1492 m_value = oldValue;
1493
1494 // the setting couldn't be changed because one of the
1495 // callback handlers failed the OnSettingChanging()
1496 // callback so we need to let all the callback handlers
1497 // know that the setting hasn't changed
1498 OnSettingChanging(shared_from_base<CSettingString>());
1499 return false;
1500 }
1501
1502 m_changed = m_value != m_default;
1503 OnSettingChanged(shared_from_base<CSettingString>());
1504 return true;
1505 }
1506
SetDefault(const std::string & value)1507 void CSettingString::SetDefault(const std::string &value)
1508 {
1509 CSharedLock lock(m_critical);
1510
1511 m_default = value;
1512 if (!m_changed)
1513 m_value = m_default;
1514 }
1515
GetOptionsType() const1516 SettingOptionsType CSettingString::GetOptionsType() const
1517 {
1518 CSharedLock lock(m_critical);
1519 if (!m_translatableOptions.empty())
1520 return SettingOptionsType::StaticTranslatable;
1521 if (!m_options.empty())
1522 return SettingOptionsType::Static;
1523 if (!m_optionsFillerName.empty() || m_optionsFiller != nullptr)
1524 return SettingOptionsType::Dynamic;
1525
1526 return SettingOptionsType::Unknown;
1527 }
1528
UpdateDynamicOptions()1529 StringSettingOptions CSettingString::UpdateDynamicOptions()
1530 {
1531 CExclusiveLock lock(m_critical);
1532 StringSettingOptions options;
1533 if (m_optionsFiller == nullptr &&
1534 (m_optionsFillerName.empty() || m_settingsManager == nullptr))
1535 return options;
1536
1537 if (m_optionsFiller == nullptr)
1538 {
1539 m_optionsFiller = reinterpret_cast<StringSettingOptionsFiller>(m_settingsManager->GetSettingOptionsFiller(shared_from_base<CSettingString>()));
1540 if (m_optionsFiller == nullptr)
1541 {
1542 s_logger->error("unknown options filler \"{}\" of \"{}\"", m_optionsFillerName, m_id);
1543 return options;
1544 }
1545 }
1546
1547 std::string bestMatchingValue = m_value;
1548 m_optionsFiller(shared_from_base<CSettingString>(), options, bestMatchingValue, m_optionsFillerData);
1549
1550 if (bestMatchingValue != m_value)
1551 SetValue(bestMatchingValue);
1552
1553 // check if the list of items has changed
1554 bool changed = m_dynamicOptions.size() != options.size();
1555 if (!changed)
1556 {
1557 for (size_t index = 0; index < options.size(); index++)
1558 {
1559 if (options[index].label.compare(m_dynamicOptions[index].label) != 0 ||
1560 options[index].value.compare(m_dynamicOptions[index].value) != 0)
1561 {
1562 changed = true;
1563 break;
1564 }
1565 }
1566 }
1567
1568 if (changed)
1569 {
1570 m_dynamicOptions = options;
1571 OnSettingPropertyChanged(shared_from_base<CSettingString>(), "options");
1572 }
1573
1574 return options;
1575 }
1576
copy(const CSettingString & setting)1577 void CSettingString::copy(const CSettingString &setting)
1578 {
1579 CSetting::Copy(setting);
1580
1581 CExclusiveLock lock(m_critical);
1582 m_value = setting.m_value;
1583 m_default = setting.m_default;
1584 m_allowEmpty = setting.m_allowEmpty;
1585 m_allowNewOption = setting.m_allowNewOption;
1586 m_translatableOptions = setting.m_translatableOptions;
1587 m_options = setting.m_options;
1588 m_optionsFillerName = setting.m_optionsFillerName;
1589 m_optionsFiller = setting.m_optionsFiller;
1590 m_optionsFillerData = setting.m_optionsFillerData;
1591 m_dynamicOptions = setting.m_dynamicOptions;
1592 }
1593
CSettingAction(const std::string & id,CSettingsManager * settingsManager)1594 CSettingAction::CSettingAction(const std::string& id,
1595 CSettingsManager* settingsManager /* = nullptr */)
1596 : CSetting(id, settingsManager, "CSettingAction")
1597 { }
1598
CSettingAction(const std::string & id,int label,CSettingsManager * settingsManager)1599 CSettingAction::CSettingAction(const std::string& id,
1600 int label,
1601 CSettingsManager* settingsManager /* = nullptr */)
1602 : CSetting(id, settingsManager, "CSettingAction")
1603 {
1604 SetLabel(label);
1605 }
1606
CSettingAction(const std::string & id,const CSettingAction & setting)1607 CSettingAction::CSettingAction(const std::string& id, const CSettingAction& setting)
1608 : CSetting(id, setting, "CSettingAction"), m_data(setting.m_data)
1609 { }
1610
Clone(const std::string & id) const1611 SettingPtr CSettingAction::Clone(const std::string &id) const
1612 {
1613 return std::make_shared<CSettingAction>(id, *this);
1614 }
1615
MergeDetails(const CSetting & other)1616 void CSettingAction::MergeDetails(const CSetting& other)
1617 {
1618 if (other.GetType() != SettingType::Action)
1619 return;
1620
1621 const auto& actionSetting = static_cast<const CSettingAction&>(other);
1622 if (!HasData() && actionSetting.HasData())
1623 SetData(actionSetting.GetData());
1624 }
1625
Deserialize(const TiXmlNode * node,bool update)1626 bool CSettingAction::Deserialize(const TiXmlNode *node, bool update /* = false */)
1627 {
1628 CSharedLock lock(m_critical);
1629
1630 if (!CSetting::Deserialize(node, update))
1631 return false;
1632
1633 m_data = XMLUtils::GetString(node, SETTING_XML_ELM_DATA);
1634
1635 return true;
1636 }
1637