1 /*
2 * Copyright (C) 2005-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 "GUIControlSettings.h"
10
11 #include "FileItem.h"
12 #include "ServiceBroker.h"
13 #include "Util.h"
14 #include "addons/AddonManager.h"
15 #include "addons/gui/GUIWindowAddonBrowser.h"
16 #include "addons/settings/SettingUrlEncodedString.h"
17 #include "dialogs/GUIDialogFileBrowser.h"
18 #include "dialogs/GUIDialogNumeric.h"
19 #include "dialogs/GUIDialogSelect.h"
20 #include "dialogs/GUIDialogSlider.h"
21 #include "guilib/GUIComponent.h"
22 #include "guilib/GUIEditControl.h"
23 #include "guilib/GUIImage.h"
24 #include "guilib/GUIKeyboardFactory.h"
25 #include "guilib/GUILabelControl.h"
26 #include "guilib/GUIRadioButtonControl.h"
27 #include "guilib/GUISettingsSliderControl.h"
28 #include "guilib/GUISpinControlEx.h"
29 #include "guilib/GUIWindowManager.h"
30 #include "guilib/LocalizeStrings.h"
31 #include "settings/MediaSourceSettings.h"
32 #include "settings/SettingAddon.h"
33 #include "settings/SettingControl.h"
34 #include "settings/SettingDateTime.h"
35 #include "settings/SettingPath.h"
36 #include "settings/SettingUtils.h"
37 #include "settings/lib/Setting.h"
38 #include "settings/lib/SettingDefinitions.h"
39 #include "storage/MediaManager.h"
40 #include "utils/FileExtensionProvider.h"
41 #include "utils/StringUtils.h"
42 #include "utils/Variant.h"
43 #include "utils/log.h"
44
45 #include <set>
46 #include <utility>
47
48 using namespace ADDON;
49
Localize(std::uint32_t code,ILocalizer * localizer,const std::string & addonId="")50 static std::string Localize(std::uint32_t code,
51 ILocalizer* localizer,
52 const std::string& addonId = "")
53 {
54 if (localizer == nullptr)
55 return "";
56
57 if (!addonId.empty())
58 {
59 std::string label = g_localizeStrings.GetAddonString(addonId, code);
60 if (!label.empty())
61 return label;
62 }
63
64 return localizer->Localize(code);
65 }
66
67 template<typename TValueType>
GetFileItem(const std::string & label,const TValueType & value,const std::vector<std::pair<std::string,CVariant>> & properties,const std::set<TValueType> & selectedValues)68 static CFileItemPtr GetFileItem(const std::string& label,
69 const TValueType& value,
70 const std::vector<std::pair<std::string, CVariant>>& properties,
71 const std::set<TValueType>& selectedValues)
72 {
73 CFileItemPtr item(new CFileItem(label));
74 item->SetProperty("value", value);
75
76 for (const auto& prop : properties)
77 item->SetProperty(prop.first, prop.second);
78
79 if (selectedValues.find(value) != selectedValues.end())
80 item->Select(true);
81
82 return item;
83 }
84
85 template<class SettingOption>
CompareSettingOptionAseconding(const SettingOption & lhs,const SettingOption & rhs)86 static bool CompareSettingOptionAseconding(const SettingOption& lhs, const SettingOption& rhs)
87 {
88 return StringUtils::CompareNoCase(lhs.label, rhs.label) < 0;
89 }
90
91 template<class SettingOption>
CompareSettingOptionDeseconding(const SettingOption & lhs,const SettingOption & rhs)92 static bool CompareSettingOptionDeseconding(const SettingOption& lhs, const SettingOption& rhs)
93 {
94 return StringUtils::CompareNoCase(lhs.label, rhs.label) > 0;
95 }
96
GetIntegerOptions(const SettingConstPtr & setting,IntegerSettingOptions & options,std::set<int> & selectedOptions,ILocalizer * localizer,bool updateOptions)97 static bool GetIntegerOptions(const SettingConstPtr& setting,
98 IntegerSettingOptions& options,
99 std::set<int>& selectedOptions,
100 ILocalizer* localizer,
101 bool updateOptions)
102 {
103 std::shared_ptr<const CSettingInt> pSettingInt = NULL;
104 if (setting->GetType() == SettingType::Integer)
105 pSettingInt = std::static_pointer_cast<const CSettingInt>(setting);
106 else if (setting->GetType() == SettingType::List)
107 {
108 std::shared_ptr<const CSettingList> settingList =
109 std::static_pointer_cast<const CSettingList>(setting);
110 if (settingList->GetElementType() != SettingType::Integer)
111 return false;
112
113 pSettingInt = std::static_pointer_cast<const CSettingInt>(settingList->GetDefinition());
114 }
115
116 switch (pSettingInt->GetOptionsType())
117 {
118 case SettingOptionsType::StaticTranslatable:
119 {
120 const TranslatableIntegerSettingOptions& settingOptions =
121 pSettingInt->GetTranslatableOptions();
122 for (const auto& option : settingOptions)
123 options.push_back(
124 IntegerSettingOption(Localize(option.label, localizer, option.addonId), option.value));
125 break;
126 }
127
128 case SettingOptionsType::Static:
129 {
130 const IntegerSettingOptions& settingOptions = pSettingInt->GetOptions();
131 options.insert(options.end(), settingOptions.begin(), settingOptions.end());
132 break;
133 }
134
135 case SettingOptionsType::Dynamic:
136 {
137 IntegerSettingOptions settingOptions;
138 if (updateOptions)
139 settingOptions = std::const_pointer_cast<CSettingInt>(pSettingInt)->UpdateDynamicOptions();
140 else
141 settingOptions = pSettingInt->GetDynamicOptions();
142 options.insert(options.end(), settingOptions.begin(), settingOptions.end());
143 break;
144 }
145
146 case SettingOptionsType::Unknown:
147 default:
148 {
149 std::shared_ptr<const CSettingControlFormattedRange> control =
150 std::static_pointer_cast<const CSettingControlFormattedRange>(pSettingInt->GetControl());
151 for (int i = pSettingInt->GetMinimum(); i <= pSettingInt->GetMaximum();
152 i += pSettingInt->GetStep())
153 {
154 std::string strLabel;
155 if (i == pSettingInt->GetMinimum() && control->GetMinimumLabel() > -1)
156 strLabel = Localize(control->GetMinimumLabel(), localizer);
157 else if (control->GetFormatLabel() > -1)
158 strLabel = StringUtils::Format(Localize(control->GetFormatLabel(), localizer).c_str(), i);
159 else
160 strLabel = StringUtils::Format(control->GetFormatString().c_str(), i);
161
162 options.push_back(IntegerSettingOption(strLabel, i));
163 }
164
165 break;
166 }
167 }
168
169 switch (pSettingInt->GetOptionsSort())
170 {
171 case SettingOptionsSort::Ascending:
172 std::sort(options.begin(), options.end(),
173 CompareSettingOptionAseconding<IntegerSettingOption>);
174 break;
175
176 case SettingOptionsSort::Descending:
177 std::sort(options.begin(), options.end(),
178 CompareSettingOptionDeseconding<IntegerSettingOption>);
179 break;
180
181 case SettingOptionsSort::NoSorting:
182 default:
183 break;
184 }
185
186 // this must be done after potentially calling CSettingInt::UpdateDynamicOptions() because it can
187 // change the value of the setting
188 if (setting->GetType() == SettingType::Integer)
189 selectedOptions.insert(pSettingInt->GetValue());
190 else if (setting->GetType() == SettingType::List)
191 {
192 std::vector<CVariant> list =
193 CSettingUtils::GetList(std::static_pointer_cast<const CSettingList>(setting));
194 for (const auto& itValue : list)
195 selectedOptions.insert((int)itValue.asInteger());
196 }
197 else
198 return false;
199
200 return true;
201 }
202
GetStringOptions(const SettingConstPtr & setting,StringSettingOptions & options,std::set<std::string> & selectedOptions,ILocalizer * localizer,bool updateOptions)203 static bool GetStringOptions(const SettingConstPtr& setting,
204 StringSettingOptions& options,
205 std::set<std::string>& selectedOptions,
206 ILocalizer* localizer,
207 bool updateOptions)
208 {
209 std::shared_ptr<const CSettingString> pSettingString = NULL;
210 if (setting->GetType() == SettingType::String)
211 pSettingString = std::static_pointer_cast<const CSettingString>(setting);
212 else if (setting->GetType() == SettingType::List)
213 {
214 std::shared_ptr<const CSettingList> settingList =
215 std::static_pointer_cast<const CSettingList>(setting);
216 if (settingList->GetElementType() != SettingType::String)
217 return false;
218
219 pSettingString = std::static_pointer_cast<const CSettingString>(settingList->GetDefinition());
220 }
221
222 switch (pSettingString->GetOptionsType())
223 {
224 case SettingOptionsType::StaticTranslatable:
225 {
226 const TranslatableStringSettingOptions& settingOptions =
227 pSettingString->GetTranslatableOptions();
228 for (const auto& option : settingOptions)
229 options.push_back(StringSettingOption(Localize(option.first, localizer), option.second));
230 break;
231 }
232
233 case SettingOptionsType::Static:
234 {
235 const StringSettingOptions& settingOptions = pSettingString->GetOptions();
236 options.insert(options.end(), settingOptions.begin(), settingOptions.end());
237 break;
238 }
239
240 case SettingOptionsType::Dynamic:
241 {
242 StringSettingOptions settingOptions;
243 if (updateOptions)
244 settingOptions =
245 std::const_pointer_cast<CSettingString>(pSettingString)->UpdateDynamicOptions();
246 else
247 settingOptions = pSettingString->GetDynamicOptions();
248 options.insert(options.end(), settingOptions.begin(), settingOptions.end());
249 break;
250 }
251
252 case SettingOptionsType::Unknown:
253 default:
254 return false;
255 }
256
257 switch (pSettingString->GetOptionsSort())
258 {
259 case SettingOptionsSort::Ascending:
260 std::sort(options.begin(), options.end(),
261 CompareSettingOptionAseconding<StringSettingOption>);
262 break;
263
264 case SettingOptionsSort::Descending:
265 std::sort(options.begin(), options.end(),
266 CompareSettingOptionDeseconding<StringSettingOption>);
267 break;
268
269 case SettingOptionsSort::NoSorting:
270 default:
271 break;
272 }
273
274 // this must be done after potentially calling CSettingString::UpdateDynamicOptions() because it
275 // can change the value of the setting
276 if (setting->GetType() == SettingType::String)
277 selectedOptions.insert(pSettingString->GetValue());
278 else if (setting->GetType() == SettingType::List)
279 {
280 std::vector<CVariant> list =
281 CSettingUtils::GetList(std::static_pointer_cast<const CSettingList>(setting));
282 for (const auto& itValue : list)
283 selectedOptions.insert(itValue.asString());
284 }
285 else
286 return false;
287
288 return true;
289 }
290
CGUIControlBaseSetting(int id,std::shared_ptr<CSetting> pSetting,ILocalizer * localizer)291 CGUIControlBaseSetting::CGUIControlBaseSetting(int id,
292 std::shared_ptr<CSetting> pSetting,
293 ILocalizer* localizer)
294 : m_id(id),
295 m_pSetting(std::move(pSetting)),
296 m_localizer(localizer),
297 m_delayed(false),
298 m_valid(true)
299 {
300 }
301
IsEnabled() const302 bool CGUIControlBaseSetting::IsEnabled() const
303 {
304 return m_pSetting != NULL && m_pSetting->IsEnabled();
305 }
306
UpdateFromControl()307 void CGUIControlBaseSetting::UpdateFromControl()
308 {
309 Update(true, true);
310 }
311
UpdateFromSetting(bool updateDisplayOnly)312 void CGUIControlBaseSetting::UpdateFromSetting(bool updateDisplayOnly /* = false */)
313 {
314 Update(false, updateDisplayOnly);
315 }
316
Localize(std::uint32_t code) const317 std::string CGUIControlBaseSetting::Localize(std::uint32_t code) const
318 {
319 return ::Localize(code, m_localizer);
320 }
321
Update(bool fromControl,bool updateDisplayOnly)322 void CGUIControlBaseSetting::Update(bool fromControl, bool updateDisplayOnly)
323 {
324 if (fromControl || updateDisplayOnly)
325 return;
326
327 CGUIControl* control = GetControl();
328 if (control == NULL)
329 return;
330
331 control->SetEnabled(IsEnabled());
332 if (m_pSetting)
333 control->SetVisible(m_pSetting->IsVisible());
334 SetValid(true);
335 }
336
CGUIControlRadioButtonSetting(CGUIRadioButtonControl * pRadioButton,int id,std::shared_ptr<CSetting> pSetting,ILocalizer * localizer)337 CGUIControlRadioButtonSetting::CGUIControlRadioButtonSetting(CGUIRadioButtonControl* pRadioButton,
338 int id,
339 std::shared_ptr<CSetting> pSetting,
340 ILocalizer* localizer)
341 : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
342 {
343 m_pRadioButton = pRadioButton;
344 if (m_pRadioButton == NULL)
345 return;
346
347 m_pRadioButton->SetID(id);
348 }
349
350 CGUIControlRadioButtonSetting::~CGUIControlRadioButtonSetting() = default;
351
OnClick()352 bool CGUIControlRadioButtonSetting::OnClick()
353 {
354 SetValid(std::static_pointer_cast<CSettingBool>(m_pSetting)
355 ->SetValue(!std::static_pointer_cast<CSettingBool>(m_pSetting)->GetValue()));
356 return IsValid();
357 }
358
Update(bool fromControl,bool updateDisplayOnly)359 void CGUIControlRadioButtonSetting::Update(bool fromControl, bool updateDisplayOnly)
360 {
361 if (fromControl || m_pRadioButton == NULL)
362 return;
363
364 CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
365
366 m_pRadioButton->SetSelected(std::static_pointer_cast<CSettingBool>(m_pSetting)->GetValue());
367 }
368
CGUIControlSpinExSetting(CGUISpinControlEx * pSpin,int id,std::shared_ptr<CSetting> pSetting,ILocalizer * localizer)369 CGUIControlSpinExSetting::CGUIControlSpinExSetting(CGUISpinControlEx* pSpin,
370 int id,
371 std::shared_ptr<CSetting> pSetting,
372 ILocalizer* localizer)
373 : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
374 {
375 m_pSpin = pSpin;
376 if (m_pSpin == NULL)
377 return;
378
379 m_pSpin->SetID(id);
380
381 const std::string& controlFormat = m_pSetting->GetControl()->GetFormat();
382 if (controlFormat == "number")
383 {
384 std::shared_ptr<CSettingNumber> pSettingNumber =
385 std::static_pointer_cast<CSettingNumber>(m_pSetting);
386 m_pSpin->SetType(SPIN_CONTROL_TYPE_FLOAT);
387 m_pSpin->SetFloatRange(static_cast<float>(pSettingNumber->GetMinimum()),
388 static_cast<float>(pSettingNumber->GetMaximum()));
389 m_pSpin->SetFloatInterval(static_cast<float>(pSettingNumber->GetStep()));
390 }
391 else if (controlFormat == "integer")
392 m_pSpin->SetType(SPIN_CONTROL_TYPE_TEXT);
393 else if (controlFormat == "string")
394 {
395 m_pSpin->SetType(SPIN_CONTROL_TYPE_TEXT);
396
397 if (m_pSetting->GetType() == SettingType::Integer)
398 FillIntegerSettingControl(false);
399 else if (m_pSetting->GetType() == SettingType::Number)
400 {
401 std::shared_ptr<CSettingNumber> pSettingNumber =
402 std::static_pointer_cast<CSettingNumber>(m_pSetting);
403 std::shared_ptr<const CSettingControlFormattedRange> control =
404 std::static_pointer_cast<const CSettingControlFormattedRange>(m_pSetting->GetControl());
405 int index = 0;
406 for (double value = pSettingNumber->GetMinimum(); value <= pSettingNumber->GetMaximum();
407 value += pSettingNumber->GetStep(), index++)
408 {
409 std::string strLabel;
410 if (value == pSettingNumber->GetMinimum() && control->GetMinimumLabel() > -1)
411 strLabel = Localize(control->GetMinimumLabel());
412 else if (control->GetFormatLabel() > -1)
413 strLabel = StringUtils::Format(Localize(control->GetFormatLabel()).c_str(), value);
414 else
415 strLabel = StringUtils::Format(control->GetFormatString().c_str(), value);
416
417 m_pSpin->AddLabel(strLabel, index);
418 }
419 }
420 }
421 }
422
423 CGUIControlSpinExSetting::~CGUIControlSpinExSetting() = default;
424
OnClick()425 bool CGUIControlSpinExSetting::OnClick()
426 {
427 if (m_pSpin == NULL)
428 return false;
429
430 switch (m_pSetting->GetType())
431 {
432 case SettingType::Integer:
433 SetValid(std::static_pointer_cast<CSettingInt>(m_pSetting)->SetValue(m_pSpin->GetValue()));
434 break;
435
436 case SettingType::Number:
437 {
438 auto pSettingNumber = std::static_pointer_cast<CSettingNumber>(m_pSetting);
439 const auto& controlFormat = m_pSetting->GetControl()->GetFormat();
440 if (controlFormat == "number")
441 SetValid(pSettingNumber->SetValue(m_pSpin->GetFloatValue()));
442 else
443 SetValid(pSettingNumber->SetValue(pSettingNumber->GetMinimum() +
444 pSettingNumber->GetStep() * m_pSpin->GetValue()));
445
446 break;
447 }
448
449 case SettingType::String:
450 SetValid(std::static_pointer_cast<CSettingString>(m_pSetting)
451 ->SetValue(m_pSpin->GetStringValue()));
452 break;
453
454 default:
455 return false;
456 }
457
458 return IsValid();
459 }
460
Update(bool fromControl,bool updateDisplayOnly)461 void CGUIControlSpinExSetting::Update(bool fromControl, bool updateDisplayOnly)
462 {
463 if (fromControl || m_pSpin == NULL)
464 return;
465
466 CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
467
468 FillControl(!updateDisplayOnly);
469
470 if (!updateDisplayOnly)
471 {
472 // disable the spinner if it has less than two items
473 if (!m_pSpin->IsDisabled() && (m_pSpin->GetMaximum() - m_pSpin->GetMinimum()) == 0)
474 m_pSpin->SetEnabled(false);
475 }
476 }
477
FillControl(bool updateValues)478 void CGUIControlSpinExSetting::FillControl(bool updateValues)
479 {
480 if (m_pSpin == NULL)
481 return;
482
483 if (updateValues)
484 m_pSpin->Clear();
485
486 const std::string& controlFormat = m_pSetting->GetControl()->GetFormat();
487 if (controlFormat == "number")
488 {
489 std::shared_ptr<CSettingNumber> pSettingNumber =
490 std::static_pointer_cast<CSettingNumber>(m_pSetting);
491 m_pSpin->SetFloatValue((float)pSettingNumber->GetValue());
492 }
493 else if (controlFormat == "integer")
494 FillIntegerSettingControl(updateValues);
495 else if (controlFormat == "string")
496 {
497 if (m_pSetting->GetType() == SettingType::Integer)
498 FillIntegerSettingControl(updateValues);
499 else if (m_pSetting->GetType() == SettingType::Number)
500 FillFloatSettingControl();
501 else if (m_pSetting->GetType() == SettingType::String)
502 FillStringSettingControl(updateValues);
503 }
504 }
505
FillIntegerSettingControl(bool updateValues)506 void CGUIControlSpinExSetting::FillIntegerSettingControl(bool updateValues)
507 {
508 IntegerSettingOptions options;
509 std::set<int> selectedValues;
510 // get the integer options
511 if (!GetIntegerOptions(m_pSetting, options, selectedValues, m_localizer, updateValues) ||
512 selectedValues.size() != 1)
513 return;
514
515 if (updateValues)
516 {
517 // add them to the spinner
518 for (const auto& option : options)
519 m_pSpin->AddLabel(option.label, option.value);
520 }
521
522 // and set the current value
523 m_pSpin->SetValue(*selectedValues.begin());
524 }
525
FillFloatSettingControl()526 void CGUIControlSpinExSetting::FillFloatSettingControl()
527 {
528 std::shared_ptr<CSettingNumber> pSettingNumber =
529 std::static_pointer_cast<CSettingNumber>(m_pSetting);
530 std::shared_ptr<const CSettingControlFormattedRange> control =
531 std::static_pointer_cast<const CSettingControlFormattedRange>(m_pSetting->GetControl());
532 int index = 0;
533 int currentIndex = 0;
534 for (double value = pSettingNumber->GetMinimum(); value <= pSettingNumber->GetMaximum();
535 value += pSettingNumber->GetStep(), index++)
536 {
537 if (value == pSettingNumber->GetValue())
538 {
539 currentIndex = index;
540 break;
541 }
542 }
543
544 m_pSpin->SetValue(currentIndex);
545 }
546
FillStringSettingControl(bool updateValues)547 void CGUIControlSpinExSetting::FillStringSettingControl(bool updateValues)
548 {
549 StringSettingOptions options;
550 std::set<std::string> selectedValues;
551 // get the string options
552 if (!GetStringOptions(m_pSetting, options, selectedValues, m_localizer, updateValues) ||
553 selectedValues.size() != 1)
554 return;
555
556 if (updateValues)
557 {
558 // add them to the spinner
559 for (const auto& option : options)
560 m_pSpin->AddLabel(option.label, option.value);
561 }
562
563 // and set the current value
564 m_pSpin->SetStringValue(*selectedValues.begin());
565 }
566
CGUIControlListSetting(CGUIButtonControl * pButton,int id,std::shared_ptr<CSetting> pSetting,ILocalizer * localizer)567 CGUIControlListSetting::CGUIControlListSetting(CGUIButtonControl* pButton,
568 int id,
569 std::shared_ptr<CSetting> pSetting,
570 ILocalizer* localizer)
571 : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
572 {
573 m_pButton = pButton;
574 if (m_pButton == NULL)
575 return;
576
577 m_pButton->SetID(id);
578 }
579
580 CGUIControlListSetting::~CGUIControlListSetting() = default;
581
OnClick()582 bool CGUIControlListSetting::OnClick()
583 {
584 if (m_pButton == NULL)
585 return false;
586
587 CGUIDialogSelect* dialog =
588 CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
589 WINDOW_DIALOG_SELECT);
590 if (dialog == NULL)
591 return false;
592
593 CFileItemList options;
594 std::shared_ptr<const CSettingControlList> control =
595 std::static_pointer_cast<const CSettingControlList>(m_pSetting->GetControl());
596 bool optionsValid = GetItems(m_pSetting, options, false);
597
598 bool bValueAdded = false;
599 bool bAllowNewOption = false;
600 if (m_pSetting->GetType() == SettingType::List)
601 {
602 std::shared_ptr<const CSettingList> settingList =
603 std::static_pointer_cast<const CSettingList>(m_pSetting);
604 if (settingList->GetElementType() == SettingType::String)
605 {
606 bAllowNewOption = std::static_pointer_cast<const CSettingString>(settingList->GetDefinition())
607 ->AllowNewOption();
608 }
609 }
610 if (!bAllowNewOption)
611 {
612 // Do not show dialog if
613 // * there are no items to be chosen or
614 // * only one value can be chosen and there are less than two items available
615 if (!optionsValid || options.Size() <= 0 || (!control->CanMultiSelect() && options.Size() <= 1))
616 return false;
617
618 dialog->Reset();
619 dialog->SetHeading(CVariant{Localize(m_pSetting->GetLabel())});
620 dialog->SetItems(options);
621 dialog->SetMultiSelection(control->CanMultiSelect());
622 dialog->Open();
623
624 if (!dialog->IsConfirmed())
625 return false;
626 }
627 else
628 {
629 // Possible to add items, as well as select from any options given
630 // Add any current values that are not options as items in list
631 std::vector<CVariant> list =
632 CSettingUtils::GetList(std::static_pointer_cast<const CSettingList>(m_pSetting));
633 for (const auto& value : list)
634 {
635 bool found = std::any_of(options.begin(), options.end(), [&](const auto& p) {
636 return p->GetProperty("value").asString() == value.asString();
637 });
638 if (!found)
639 {
640 CFileItemPtr item(new CFileItem(value.asString()));
641 item->SetProperty("value", value.asString());
642 item->Select(true);
643 options.Add(item);
644 }
645 }
646
647 bool bRepeat = true;
648 while (bRepeat)
649 {
650 std::string strAddButton = Localize(control->GetAddButtonLabel());
651 if (strAddButton.empty())
652 strAddButton = Localize(15019); // "ADD"
653 dialog->Reset(); // Clears AddButtonPressed
654 dialog->SetHeading(CVariant{ Localize(m_pSetting->GetLabel()) });
655 dialog->SetItems(options);
656 dialog->SetMultiSelection(control->CanMultiSelect());
657 dialog->EnableButton2(bAllowNewOption, strAddButton);
658
659 dialog->Open();
660
661 if (!dialog->IsConfirmed())
662 return false;
663
664 if (dialog->IsButton2Pressed())
665 {
666 // Get new list value
667 std::string strLabel;
668 bool bValidType = false;
669 while (!bValidType && CGUIKeyboardFactory::ShowAndGetInput(
670 strLabel, CVariant{ Localize(control->GetAddButtonLabel()) }, false))
671 {
672 // Validate new value is unique and truncate at any comma
673 StringUtils::Trim(strLabel);
674 strLabel = strLabel.substr(0, strLabel.find(','));
675 if (!strLabel.empty())
676 {
677 bValidType = !std::any_of(options.begin(), options.end(), [&](const auto& p) {
678 return p->GetProperty("value").asString() == strLabel;
679 });
680 }
681 if (bValidType)
682 { // Add new value to the list of options
683 CFileItemPtr pItem(new CFileItem(strLabel));
684 pItem->Select(true);
685 pItem->SetProperty("value", strLabel);
686 options.Add(pItem);
687 bValueAdded = true;
688 }
689 }
690 }
691 bRepeat = dialog->IsButton2Pressed();
692 }
693 }
694
695 std::vector<CVariant> values;
696 for (int i : dialog->GetSelectedItems())
697 {
698 const CFileItemPtr item = options.Get(i);
699 if (item == NULL || !item->HasProperty("value"))
700 return false;
701
702 values.push_back(item->GetProperty("value"));
703 }
704
705 bool ret = false;
706 switch (m_pSetting->GetType())
707 {
708 case SettingType::Integer:
709 if (values.size() > 1)
710 return false;
711 ret = std::static_pointer_cast<CSettingInt>(m_pSetting)
712 ->SetValue((int)values.at(0).asInteger());
713 break;
714
715 case SettingType::String:
716 if (values.size() > 1)
717 return false;
718 ret = std::static_pointer_cast<CSettingString>(m_pSetting)->SetValue(values.at(0).asString());
719 break;
720
721 case SettingType::List:
722 ret = CSettingUtils::SetList(std::static_pointer_cast<CSettingList>(m_pSetting), values);
723 break;
724
725 default:
726 return false;
727 }
728
729 if (ret)
730 UpdateFromSetting(!bValueAdded);
731 else
732 SetValid(false);
733
734 return IsValid();
735 }
736
Update(bool fromControl,bool updateDisplayOnly)737 void CGUIControlListSetting::Update(bool fromControl, bool updateDisplayOnly)
738 {
739 if (fromControl || m_pButton == NULL)
740 return;
741
742 CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
743
744 CFileItemList options;
745 std::shared_ptr<const CSettingControlList> control =
746 std::static_pointer_cast<const CSettingControlList>(m_pSetting->GetControl());
747 bool optionsValid = GetItems(m_pSetting, options, !updateDisplayOnly);
748
749 bool bAllowNewOption = false;
750 if (m_pSetting->GetType() == SettingType::List)
751 {
752 std::shared_ptr<const CSettingList> settingList =
753 std::static_pointer_cast<const CSettingList>(m_pSetting);
754 if (settingList->GetElementType() == SettingType::String)
755 {
756 bAllowNewOption = std::static_pointer_cast<const CSettingString>(settingList->GetDefinition())
757 ->AllowNewOption();
758 }
759 }
760
761 std::string label2;
762 if (optionsValid && !control->HideValue())
763 {
764 SettingControlListValueFormatter formatter = control->GetFormatter();
765 if (formatter)
766 label2 = formatter(m_pSetting);
767
768 if (label2.empty() && bAllowNewOption)
769 {
770 const std::shared_ptr<const CSettingList> settingList =
771 std::static_pointer_cast<const CSettingList>(m_pSetting);
772 label2 = settingList->ToString();
773 }
774
775 if (label2.empty())
776 {
777 std::vector<std::string> labels;
778 for (int index = 0; index < options.Size(); index++)
779 {
780 const CFileItemPtr pItem = options.Get(index);
781 if (pItem->IsSelected())
782 labels.push_back(pItem->GetLabel());
783 }
784
785 label2 = StringUtils::Join(labels, ", ");
786 }
787 }
788
789 m_pButton->SetLabel2(label2);
790
791 if (!updateDisplayOnly)
792 {
793 // Disable the control if no items can be added and
794 // * there are no items to be chosen
795 // * only one value can be chosen and there are less than two items available
796 if (!m_pButton->IsDisabled() && !bAllowNewOption &&
797 (options.Size() <= 0 || (!control->CanMultiSelect() && options.Size() <= 1)))
798 m_pButton->SetEnabled(false);
799 }
800 }
801
GetItems(const SettingConstPtr & setting,CFileItemList & items,bool updateItems) const802 bool CGUIControlListSetting::GetItems(const SettingConstPtr& setting,
803 CFileItemList& items,
804 bool updateItems) const
805 {
806 std::shared_ptr<const CSettingControlList> control =
807 std::static_pointer_cast<const CSettingControlList>(setting->GetControl());
808 const std::string& controlFormat = control->GetFormat();
809
810 if (controlFormat == "integer")
811 return GetIntegerItems(setting, items, updateItems);
812 else if (controlFormat == "string")
813 {
814 if (setting->GetType() == SettingType::Integer ||
815 (setting->GetType() == SettingType::List &&
816 std::static_pointer_cast<const CSettingList>(setting)->GetElementType() ==
817 SettingType::Integer))
818 return GetIntegerItems(setting, items, updateItems);
819 else if (setting->GetType() == SettingType::String ||
820 (setting->GetType() == SettingType::List &&
821 std::static_pointer_cast<const CSettingList>(setting)->GetElementType() ==
822 SettingType::String))
823 return GetStringItems(setting, items, updateItems);
824 }
825 else
826 return false;
827
828 return true;
829 }
830
GetIntegerItems(const SettingConstPtr & setting,CFileItemList & items,bool updateItems) const831 bool CGUIControlListSetting::GetIntegerItems(const SettingConstPtr& setting,
832 CFileItemList& items,
833 bool updateItems) const
834 {
835 IntegerSettingOptions options;
836 std::set<int> selectedValues;
837 // get the integer options
838 if (!GetIntegerOptions(setting, options, selectedValues, m_localizer, updateItems))
839 return false;
840
841 // turn them into CFileItems and add them to the item list
842 for (const auto& option : options)
843 items.Add(GetFileItem(option.label, option.value, option.properties, selectedValues));
844
845 return true;
846 }
847
GetStringItems(const SettingConstPtr & setting,CFileItemList & items,bool updateItems) const848 bool CGUIControlListSetting::GetStringItems(const SettingConstPtr& setting,
849 CFileItemList& items,
850 bool updateItems) const
851 {
852 StringSettingOptions options;
853 std::set<std::string> selectedValues;
854 // get the string options
855 if (!GetStringOptions(setting, options, selectedValues, m_localizer, updateItems))
856 return false;
857
858 // turn them into CFileItems and add them to the item list
859 for (const auto& option : options)
860 items.Add(GetFileItem(option.label, option.value, option.properties, selectedValues));
861
862 return true;
863 }
864
CGUIControlButtonSetting(CGUIButtonControl * pButton,int id,std::shared_ptr<CSetting> pSetting,ILocalizer * localizer)865 CGUIControlButtonSetting::CGUIControlButtonSetting(CGUIButtonControl* pButton,
866 int id,
867 std::shared_ptr<CSetting> pSetting,
868 ILocalizer* localizer)
869 : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
870 {
871 m_pButton = pButton;
872 if (m_pButton == NULL)
873 return;
874
875 m_pButton->SetID(id);
876 }
877
878 CGUIControlButtonSetting::~CGUIControlButtonSetting() = default;
879
OnClick()880 bool CGUIControlButtonSetting::OnClick()
881 {
882 if (m_pButton == NULL)
883 return false;
884
885 std::shared_ptr<const ISettingControl> control = m_pSetting->GetControl();
886 const std::string& controlType = control->GetType();
887 const std::string& controlFormat = control->GetFormat();
888 if (controlType == "button")
889 {
890 std::shared_ptr<const CSettingControlButton> buttonControl =
891 std::static_pointer_cast<const CSettingControlButton>(control);
892 if (controlFormat == "addon")
893 {
894 // prompt for the addon
895 std::shared_ptr<CSettingAddon> setting;
896 std::vector<std::string> addonIDs;
897 if (m_pSetting->GetType() == SettingType::List)
898 {
899 std::shared_ptr<CSettingList> settingList =
900 std::static_pointer_cast<CSettingList>(m_pSetting);
901 setting = std::static_pointer_cast<CSettingAddon>(settingList->GetDefinition());
902 for (const SettingPtr& addon : settingList->GetValue())
903 addonIDs.push_back(std::static_pointer_cast<CSettingAddon>(addon)->GetValue());
904 }
905 else
906 {
907 setting = std::static_pointer_cast<CSettingAddon>(m_pSetting);
908 addonIDs.push_back(setting->GetValue());
909 }
910
911 if (CGUIWindowAddonBrowser::SelectAddonID(
912 setting->GetAddonType(), addonIDs, setting->AllowEmpty(),
913 buttonControl->ShowAddonDetails(), m_pSetting->GetType() == SettingType::List,
914 buttonControl->ShowInstalledAddons(), buttonControl->ShowInstallableAddons(),
915 buttonControl->ShowMoreAddons()) != 1)
916 return false;
917
918 if (m_pSetting->GetType() == SettingType::List)
919 std::static_pointer_cast<CSettingList>(m_pSetting)->FromString(addonIDs);
920 else
921 SetValid(setting->SetValue(addonIDs[0]));
922 }
923 else if (controlFormat == "path" || controlFormat == "file" || controlFormat == "image")
924 SetValid(GetPath(std::static_pointer_cast<CSettingPath>(m_pSetting), m_localizer));
925 else if (controlFormat == "date")
926 {
927 std::shared_ptr<CSettingDate> settingDate =
928 std::static_pointer_cast<CSettingDate>(m_pSetting);
929
930 KODI::TIME::SystemTime systemdate;
931 settingDate->GetDate().GetAsSystemTime(systemdate);
932 if (CGUIDialogNumeric::ShowAndGetDate(systemdate, Localize(buttonControl->GetHeading())))
933 SetValid(settingDate->SetDate(CDateTime(systemdate)));
934 }
935 else if (controlFormat == "time")
936 {
937 std::shared_ptr<CSettingTime> settingTime =
938 std::static_pointer_cast<CSettingTime>(m_pSetting);
939
940 KODI::TIME::SystemTime systemtime;
941 settingTime->GetTime().GetAsSystemTime(systemtime);
942
943 if (CGUIDialogNumeric::ShowAndGetTime(systemtime, Localize(buttonControl->GetHeading())))
944 SetValid(settingTime->SetTime(CDateTime(systemtime)));
945 }
946 else if (controlFormat == "action")
947 {
948 // simply call the OnSettingAction callback and whoever knows what to
949 // do can do so (based on the setting's identification)
950 m_pSetting->OnSettingAction(m_pSetting);
951 SetValid(true);
952 }
953 }
954 else if (controlType == "slider")
955 {
956 float value, min, step, max;
957 if (m_pSetting->GetType() == SettingType::Integer)
958 {
959 std::shared_ptr<CSettingInt> settingInt = std::static_pointer_cast<CSettingInt>(m_pSetting);
960 value = (float)settingInt->GetValue();
961 min = (float)settingInt->GetMinimum();
962 step = (float)settingInt->GetStep();
963 max = (float)settingInt->GetMaximum();
964 }
965 else if (m_pSetting->GetType() == SettingType::Number)
966 {
967 std::shared_ptr<CSettingNumber> settingNumber =
968 std::static_pointer_cast<CSettingNumber>(m_pSetting);
969 value = (float)settingNumber->GetValue();
970 min = (float)settingNumber->GetMinimum();
971 step = (float)settingNumber->GetStep();
972 max = (float)settingNumber->GetMaximum();
973 }
974 else
975 return false;
976
977 std::shared_ptr<const CSettingControlSlider> sliderControl =
978 std::static_pointer_cast<const CSettingControlSlider>(control);
979 CGUIDialogSlider::ShowAndGetInput(Localize(sliderControl->GetHeading()), value, min, step, max,
980 this, NULL);
981 SetValid(true);
982 }
983
984 // update the displayed value
985 UpdateFromSetting(true);
986
987 return IsValid();
988 }
989
Update(bool fromControl,bool updateDisplayOnly)990 void CGUIControlButtonSetting::Update(bool fromControl, bool updateDisplayOnly)
991 {
992 if (fromControl || m_pButton == NULL)
993 return;
994
995 CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
996
997 std::string strText;
998 std::shared_ptr<const ISettingControl> control = m_pSetting->GetControl();
999 const std::string& controlType = control->GetType();
1000 const std::string& controlFormat = control->GetFormat();
1001
1002 if (controlType == "button")
1003 {
1004 if (!std::static_pointer_cast<const CSettingControlButton>(control)->HideValue())
1005 {
1006 auto setting = m_pSetting;
1007 if (m_pSetting->GetType() == SettingType::List)
1008 setting = std::static_pointer_cast<CSettingList>(m_pSetting)->GetDefinition();
1009
1010 switch (setting->GetType())
1011 {
1012 case SettingType::String:
1013 {
1014 if (controlFormat == "addon")
1015 {
1016 std::vector<std::string> addonIDs;
1017 if (m_pSetting->GetType() == SettingType::List)
1018 {
1019 for (const auto& addonSetting :
1020 std::static_pointer_cast<CSettingList>(m_pSetting)->GetValue())
1021 addonIDs.push_back(
1022 std::static_pointer_cast<CSettingAddon>(addonSetting)->GetValue());
1023 }
1024 else
1025 addonIDs.push_back(std::static_pointer_cast<CSettingString>(setting)->GetValue());
1026
1027 std::vector<std::string> addonNames;
1028 for (const auto& addonID : addonIDs)
1029 {
1030 ADDON::AddonPtr addon;
1031 if (CServiceBroker::GetAddonMgr().GetAddon(addonID, addon, ADDON::ADDON_UNKNOWN,
1032 ADDON::OnlyEnabled::YES))
1033 addonNames.push_back(addon->Name());
1034 }
1035
1036 if (addonNames.empty())
1037 strText = g_localizeStrings.Get(231); // None
1038 else
1039 strText = StringUtils::Join(addonNames, ", ");
1040 }
1041 else
1042 {
1043 std::string strValue = std::static_pointer_cast<CSettingString>(setting)->GetValue();
1044 if (controlFormat == "path" || controlFormat == "file" || controlFormat == "image")
1045 {
1046 std::string shortPath;
1047 if (CUtil::MakeShortenPath(strValue, shortPath, 30))
1048 strText = shortPath;
1049 }
1050 else if (controlFormat == "infolabel")
1051 {
1052 strText = strValue;
1053 if (strText.empty())
1054 strText = g_localizeStrings.Get(231); // None
1055 }
1056 else
1057 strText = strValue;
1058 }
1059
1060 break;
1061 }
1062
1063 case SettingType::Action:
1064 {
1065 // CSettingAction.
1066 // Note: This can be removed once all settings use a proper control & format combination.
1067 // CSettingAction is strictly speaking not designed to have a label2, it does not even have a value.
1068 strText = m_pButton->GetLabel2();
1069
1070 break;
1071 }
1072
1073 default:
1074 break;
1075 }
1076 }
1077 }
1078 else if (controlType == "slider")
1079 {
1080 switch (m_pSetting->GetType())
1081 {
1082 case SettingType::Integer:
1083 {
1084 std::shared_ptr<const CSettingInt> settingInt =
1085 std::static_pointer_cast<CSettingInt>(m_pSetting);
1086 strText = CGUIControlSliderSetting::GetText(m_pSetting, settingInt->GetValue(),
1087 settingInt->GetMinimum(), settingInt->GetStep(),
1088 settingInt->GetMaximum(), m_localizer);
1089 break;
1090 }
1091
1092 case SettingType::Number:
1093 {
1094 std::shared_ptr<const CSettingNumber> settingNumber =
1095 std::static_pointer_cast<CSettingNumber>(m_pSetting);
1096 strText = CGUIControlSliderSetting::GetText(
1097 m_pSetting, settingNumber->GetValue(), settingNumber->GetMinimum(),
1098 settingNumber->GetStep(), settingNumber->GetMaximum(), m_localizer);
1099 break;
1100 }
1101
1102 default:
1103 break;
1104 }
1105 }
1106
1107 m_pButton->SetLabel2(strText);
1108 }
1109
GetPath(const std::shared_ptr<CSettingPath> & pathSetting,ILocalizer * localizer)1110 bool CGUIControlButtonSetting::GetPath(const std::shared_ptr<CSettingPath>& pathSetting,
1111 ILocalizer* localizer)
1112 {
1113 if (pathSetting == NULL)
1114 return false;
1115
1116 std::string path = pathSetting->GetValue();
1117
1118 VECSOURCES shares;
1119 bool localSharesOnly = false;
1120 const std::vector<std::string>& sources = pathSetting->GetSources();
1121 for (const auto& source : sources)
1122 {
1123 if (StringUtils::EqualsNoCase(source, "local"))
1124 localSharesOnly = true;
1125 else
1126 {
1127 VECSOURCES* sources = CMediaSourceSettings::GetInstance().GetSources(source);
1128 if (sources != NULL)
1129 shares.insert(shares.end(), sources->begin(), sources->end());
1130 }
1131 }
1132
1133 CServiceBroker::GetMediaManager().GetLocalDrives(shares);
1134 if (!localSharesOnly)
1135 CServiceBroker::GetMediaManager().GetNetworkLocations(shares);
1136
1137 bool result = false;
1138 std::shared_ptr<const CSettingControlButton> control =
1139 std::static_pointer_cast<const CSettingControlButton>(pathSetting->GetControl());
1140 const auto heading = ::Localize(control->GetHeading(), localizer);
1141 if (control->GetFormat() == "file")
1142 result = CGUIDialogFileBrowser::ShowAndGetFile(
1143 shares, pathSetting->GetMasking(CServiceBroker::GetFileExtensionProvider()), heading, path,
1144 control->UseImageThumbs(), control->UseFileDirectories());
1145 else if (control->GetFormat() == "image")
1146 {
1147 /* Check setting contains own masking, to filter required image type.
1148 * e.g. png only needed
1149 * <constraints>
1150 * <masking>*.png</masking>
1151 * </constraints>
1152 * <control type="button" format="image">
1153 * ...
1154 */
1155 std::string ext = pathSetting->GetMasking(CServiceBroker::GetFileExtensionProvider());
1156 if (ext.empty())
1157 ext = CServiceBroker::GetFileExtensionProvider().GetPictureExtensions();
1158 result = CGUIDialogFileBrowser::ShowAndGetFile(shares, ext, heading, path, true);
1159 }
1160 else
1161 result =
1162 CGUIDialogFileBrowser::ShowAndGetDirectory(shares, heading, path, pathSetting->Writable());
1163
1164 if (!result)
1165 return false;
1166
1167 return pathSetting->SetValue(path);
1168 }
1169
OnSliderChange(void * data,CGUISliderControl * slider)1170 void CGUIControlButtonSetting::OnSliderChange(void* data, CGUISliderControl* slider)
1171 {
1172 if (slider == NULL)
1173 return;
1174
1175 std::string strText;
1176 switch (m_pSetting->GetType())
1177 {
1178 case SettingType::Integer:
1179 {
1180 std::shared_ptr<CSettingInt> settingInt = std::static_pointer_cast<CSettingInt>(m_pSetting);
1181 if (settingInt->SetValue(slider->GetIntValue()))
1182 strText = CGUIControlSliderSetting::GetText(m_pSetting, settingInt->GetValue(),
1183 settingInt->GetMinimum(), settingInt->GetStep(),
1184 settingInt->GetMaximum(), m_localizer);
1185 break;
1186 }
1187
1188 case SettingType::Number:
1189 {
1190 std::shared_ptr<CSettingNumber> settingNumber =
1191 std::static_pointer_cast<CSettingNumber>(m_pSetting);
1192 if (settingNumber->SetValue(static_cast<double>(slider->GetFloatValue())))
1193 strText = CGUIControlSliderSetting::GetText(
1194 m_pSetting, settingNumber->GetValue(), settingNumber->GetMinimum(),
1195 settingNumber->GetStep(), settingNumber->GetMaximum(), m_localizer);
1196 break;
1197 }
1198
1199 default:
1200 break;
1201 }
1202
1203 if (!strText.empty())
1204 slider->SetTextValue(strText);
1205 }
1206
CGUIControlEditSetting(CGUIEditControl * pEdit,int id,const std::shared_ptr<CSetting> & pSetting,ILocalizer * localizer)1207 CGUIControlEditSetting::CGUIControlEditSetting(CGUIEditControl* pEdit,
1208 int id,
1209 const std::shared_ptr<CSetting>& pSetting,
1210 ILocalizer* localizer)
1211 : CGUIControlBaseSetting(id, pSetting, localizer)
1212 {
1213 std::shared_ptr<const CSettingControlEdit> control =
1214 std::static_pointer_cast<const CSettingControlEdit>(pSetting->GetControl());
1215 m_pEdit = pEdit;
1216 if (m_pEdit == NULL)
1217 return;
1218
1219 m_pEdit->SetID(id);
1220 int heading = m_pSetting->GetLabel();
1221 if (control->GetHeading() > 0)
1222 heading = control->GetHeading();
1223 if (heading < 0)
1224 heading = 0;
1225
1226 CGUIEditControl::INPUT_TYPE inputType = CGUIEditControl::INPUT_TYPE_TEXT;
1227 const std::string& controlFormat = control->GetFormat();
1228 if (controlFormat == "string")
1229 {
1230 if (control->IsHidden())
1231 inputType = CGUIEditControl::INPUT_TYPE_PASSWORD;
1232 }
1233 else if (controlFormat == "integer" || controlFormat == "number")
1234 {
1235 if (control->VerifyNewValue())
1236 inputType = CGUIEditControl::INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW;
1237 else
1238 inputType = CGUIEditControl::INPUT_TYPE_NUMBER;
1239 }
1240 else if (controlFormat == "ip")
1241 inputType = CGUIEditControl::INPUT_TYPE_IPADDRESS;
1242 else if (controlFormat == "md5")
1243 inputType = CGUIEditControl::INPUT_TYPE_PASSWORD_MD5;
1244
1245 m_pEdit->SetInputType(inputType, heading);
1246
1247 // this will automatically trigger validation so it must be executed after
1248 // having set the value of the control based on the value of the setting
1249 m_pEdit->SetInputValidation(InputValidation, this);
1250 }
1251
1252 CGUIControlEditSetting::~CGUIControlEditSetting() = default;
1253
OnClick()1254 bool CGUIControlEditSetting::OnClick()
1255 {
1256 if (m_pEdit == NULL)
1257 return false;
1258
1259 // update our string
1260 if (m_pSetting->GetControl()->GetFormat() == "urlencoded")
1261 {
1262 std::shared_ptr<CSettingUrlEncodedString> urlEncodedSetting =
1263 std::static_pointer_cast<CSettingUrlEncodedString>(m_pSetting);
1264 SetValid(urlEncodedSetting->SetDecodedValue(m_pEdit->GetLabel2()));
1265 }
1266 else
1267 SetValid(m_pSetting->FromString(m_pEdit->GetLabel2()));
1268
1269 return IsValid();
1270 }
1271
Update(bool fromControl,bool updateDisplayOnly)1272 void CGUIControlEditSetting::Update(bool fromControl, bool updateDisplayOnly)
1273 {
1274 if (fromControl || m_pEdit == NULL)
1275 return;
1276
1277 CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
1278
1279 std::shared_ptr<const CSettingControlEdit> control =
1280 std::static_pointer_cast<const CSettingControlEdit>(m_pSetting->GetControl());
1281
1282 if (control->GetFormat() == "urlencoded")
1283 {
1284 std::shared_ptr<CSettingUrlEncodedString> urlEncodedSetting =
1285 std::static_pointer_cast<CSettingUrlEncodedString>(m_pSetting);
1286 m_pEdit->SetLabel2(urlEncodedSetting->GetDecodedValue());
1287 }
1288 else
1289 m_pEdit->SetLabel2(m_pSetting->ToString());
1290 }
1291
InputValidation(const std::string & input,void * data)1292 bool CGUIControlEditSetting::InputValidation(const std::string& input, void* data)
1293 {
1294 if (data == NULL)
1295 return true;
1296
1297 CGUIControlEditSetting* editControl = reinterpret_cast<CGUIControlEditSetting*>(data);
1298 if (editControl == NULL || editControl->GetSetting() == NULL)
1299 return true;
1300
1301 editControl->SetValid(editControl->GetSetting()->CheckValidity(input));
1302 return editControl->IsValid();
1303 }
1304
CGUIControlSliderSetting(CGUISettingsSliderControl * pSlider,int id,std::shared_ptr<CSetting> pSetting,ILocalizer * localizer)1305 CGUIControlSliderSetting::CGUIControlSliderSetting(CGUISettingsSliderControl* pSlider,
1306 int id,
1307 std::shared_ptr<CSetting> pSetting,
1308 ILocalizer* localizer)
1309 : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
1310 {
1311 m_pSlider = pSlider;
1312 if (m_pSlider == NULL)
1313 return;
1314
1315 m_pSlider->SetID(id);
1316
1317 switch (m_pSetting->GetType())
1318 {
1319 case SettingType::Integer:
1320 {
1321 std::shared_ptr<CSettingInt> settingInt = std::static_pointer_cast<CSettingInt>(m_pSetting);
1322 if (m_pSetting->GetControl()->GetFormat() == "percentage")
1323 m_pSlider->SetType(SLIDER_CONTROL_TYPE_PERCENTAGE);
1324 else
1325 {
1326 m_pSlider->SetType(SLIDER_CONTROL_TYPE_INT);
1327 m_pSlider->SetRange(settingInt->GetMinimum(), settingInt->GetMaximum());
1328 }
1329 m_pSlider->SetIntInterval(settingInt->GetStep());
1330 break;
1331 }
1332
1333 case SettingType::Number:
1334 {
1335 std::shared_ptr<CSettingNumber> settingNumber =
1336 std::static_pointer_cast<CSettingNumber>(m_pSetting);
1337 m_pSlider->SetType(SLIDER_CONTROL_TYPE_FLOAT);
1338 m_pSlider->SetFloatRange(static_cast<float>(settingNumber->GetMinimum()),
1339 static_cast<float>(settingNumber->GetMaximum()));
1340 m_pSlider->SetFloatInterval(static_cast<float>(settingNumber->GetStep()));
1341 break;
1342 }
1343
1344 default:
1345 break;
1346 }
1347 }
1348
1349 CGUIControlSliderSetting::~CGUIControlSliderSetting() = default;
1350
OnClick()1351 bool CGUIControlSliderSetting::OnClick()
1352 {
1353 if (m_pSlider == NULL)
1354 return false;
1355
1356 switch (m_pSetting->GetType())
1357 {
1358 case SettingType::Integer:
1359 SetValid(
1360 std::static_pointer_cast<CSettingInt>(m_pSetting)->SetValue(m_pSlider->GetIntValue()));
1361 break;
1362
1363 case SettingType::Number:
1364 SetValid(std::static_pointer_cast<CSettingNumber>(m_pSetting)
1365 ->SetValue(m_pSlider->GetFloatValue()));
1366 break;
1367
1368 default:
1369 return false;
1370 }
1371
1372 return IsValid();
1373 }
1374
Update(bool fromControl,bool updateDisplayOnly)1375 void CGUIControlSliderSetting::Update(bool fromControl, bool updateDisplayOnly)
1376 {
1377 if (m_pSlider == NULL)
1378 return;
1379
1380 CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
1381
1382 std::string strText;
1383 switch (m_pSetting->GetType())
1384 {
1385 case SettingType::Integer:
1386 {
1387 std::shared_ptr<const CSettingInt> settingInt =
1388 std::static_pointer_cast<CSettingInt>(m_pSetting);
1389 int value;
1390 if (fromControl)
1391 value = m_pSlider->GetIntValue();
1392 else
1393 {
1394 value = std::static_pointer_cast<CSettingInt>(m_pSetting)->GetValue();
1395 m_pSlider->SetIntValue(value);
1396 }
1397
1398 strText = CGUIControlSliderSetting::GetText(m_pSetting, value, settingInt->GetMinimum(),
1399 settingInt->GetStep(), settingInt->GetMaximum(),
1400 m_localizer);
1401 break;
1402 }
1403
1404 case SettingType::Number:
1405 {
1406 std::shared_ptr<const CSettingNumber> settingNumber =
1407 std::static_pointer_cast<CSettingNumber>(m_pSetting);
1408 double value;
1409 if (fromControl)
1410 value = m_pSlider->GetFloatValue();
1411 else
1412 {
1413 value = std::static_pointer_cast<CSettingNumber>(m_pSetting)->GetValue();
1414 m_pSlider->SetFloatValue((float)value);
1415 }
1416
1417 strText = CGUIControlSliderSetting::GetText(m_pSetting, value, settingNumber->GetMinimum(),
1418 settingNumber->GetStep(),
1419 settingNumber->GetMaximum(), m_localizer);
1420 break;
1421 }
1422
1423 default:
1424 break;
1425 }
1426
1427 if (!strText.empty())
1428 m_pSlider->SetTextValue(strText);
1429 }
1430
GetText(const std::shared_ptr<CSetting> & setting,const CVariant & value,const CVariant & minimum,const CVariant & step,const CVariant & maximum,ILocalizer * localizer)1431 std::string CGUIControlSliderSetting::GetText(const std::shared_ptr<CSetting>& setting,
1432 const CVariant& value,
1433 const CVariant& minimum,
1434 const CVariant& step,
1435 const CVariant& maximum,
1436 ILocalizer* localizer)
1437 {
1438 if (setting == NULL || !(value.isInteger() || value.isDouble()))
1439 return "";
1440
1441 const auto control = std::static_pointer_cast<const CSettingControlSlider>(setting->GetControl());
1442 if (control == NULL)
1443 return "";
1444
1445 SettingControlSliderFormatter formatter = control->GetFormatter();
1446 if (formatter != NULL)
1447 return formatter(control, value, minimum, step, maximum);
1448
1449 std::string formatString = control->GetFormatString();
1450 if (control->GetFormatLabel() > -1)
1451 formatString = ::Localize(control->GetFormatLabel(), localizer);
1452
1453 std::string formattedString;
1454 if (FormatText(formatString, value, setting->GetId(), formattedString))
1455 return formattedString;
1456
1457 // fall back to default formatting
1458 formatString = control->GetDefaultFormatString();
1459 if (FormatText(formatString, value, setting->GetId(), formattedString))
1460 return formattedString;
1461
1462 return "";
1463 }
1464
FormatText(const std::string & formatString,const CVariant & value,const std::string & settingId,std::string & formattedText)1465 bool CGUIControlSliderSetting::FormatText(const std::string& formatString,
1466 const CVariant& value,
1467 const std::string& settingId,
1468 std::string& formattedText)
1469 {
1470 try
1471 {
1472 if (value.isDouble())
1473 formattedText = StringUtils::Format(formatString.c_str(), value.asDouble());
1474 else
1475 formattedText =
1476 StringUtils::Format(formatString.c_str(), static_cast<int>(value.asInteger()));
1477 }
1478 catch (const std::runtime_error& err)
1479 {
1480 CLog::Log(LOGERROR, "Invalid formatting with string \"{}\" for setting \"{}\": {}",
1481 formatString, settingId, err.what());
1482 return false;
1483 }
1484
1485 return true;
1486 }
1487
CGUIControlRangeSetting(CGUISettingsSliderControl * pSlider,int id,std::shared_ptr<CSetting> pSetting,ILocalizer * localizer)1488 CGUIControlRangeSetting::CGUIControlRangeSetting(CGUISettingsSliderControl* pSlider,
1489 int id,
1490 std::shared_ptr<CSetting> pSetting,
1491 ILocalizer* localizer)
1492 : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
1493 {
1494 m_pSlider = pSlider;
1495 if (m_pSlider == NULL)
1496 return;
1497
1498 m_pSlider->SetID(id);
1499 m_pSlider->SetRangeSelection(true);
1500
1501 if (m_pSetting->GetType() == SettingType::List)
1502 {
1503 std::shared_ptr<CSettingList> settingList = std::static_pointer_cast<CSettingList>(m_pSetting);
1504 SettingConstPtr listDefintion = settingList->GetDefinition();
1505 switch (listDefintion->GetType())
1506 {
1507 case SettingType::Integer:
1508 {
1509 std::shared_ptr<const CSettingInt> listDefintionInt =
1510 std::static_pointer_cast<const CSettingInt>(listDefintion);
1511 if (m_pSetting->GetControl()->GetFormat() == "percentage")
1512 m_pSlider->SetType(SLIDER_CONTROL_TYPE_PERCENTAGE);
1513 else
1514 {
1515 m_pSlider->SetType(SLIDER_CONTROL_TYPE_INT);
1516 m_pSlider->SetRange(listDefintionInt->GetMinimum(), listDefintionInt->GetMaximum());
1517 }
1518 m_pSlider->SetIntInterval(listDefintionInt->GetStep());
1519 break;
1520 }
1521
1522 case SettingType::Number:
1523 {
1524 std::shared_ptr<const CSettingNumber> listDefinitionNumber =
1525 std::static_pointer_cast<const CSettingNumber>(listDefintion);
1526 m_pSlider->SetType(SLIDER_CONTROL_TYPE_FLOAT);
1527 m_pSlider->SetFloatRange(static_cast<float>(listDefinitionNumber->GetMinimum()),
1528 static_cast<float>(listDefinitionNumber->GetMaximum()));
1529 m_pSlider->SetFloatInterval(static_cast<float>(listDefinitionNumber->GetStep()));
1530 break;
1531 }
1532
1533 default:
1534 break;
1535 }
1536 }
1537 }
1538
1539 CGUIControlRangeSetting::~CGUIControlRangeSetting() = default;
1540
OnClick()1541 bool CGUIControlRangeSetting::OnClick()
1542 {
1543 if (m_pSlider == NULL || m_pSetting->GetType() != SettingType::List)
1544 return false;
1545
1546 std::shared_ptr<CSettingList> settingList = std::static_pointer_cast<CSettingList>(m_pSetting);
1547 const SettingList& settingListValues = settingList->GetValue();
1548 if (settingListValues.size() != 2)
1549 return false;
1550
1551 std::vector<CVariant> values;
1552 SettingConstPtr listDefintion = settingList->GetDefinition();
1553 switch (listDefintion->GetType())
1554 {
1555 case SettingType::Integer:
1556 values.emplace_back(m_pSlider->GetIntValue(CGUISliderControl::RangeSelectorLower));
1557 values.emplace_back(m_pSlider->GetIntValue(CGUISliderControl::RangeSelectorUpper));
1558 break;
1559
1560 case SettingType::Number:
1561 values.emplace_back(m_pSlider->GetFloatValue(CGUISliderControl::RangeSelectorLower));
1562 values.emplace_back(m_pSlider->GetFloatValue(CGUISliderControl::RangeSelectorUpper));
1563 break;
1564
1565 default:
1566 return false;
1567 }
1568
1569 if (values.size() != 2)
1570 return false;
1571
1572 SetValid(CSettingUtils::SetList(settingList, values));
1573 return IsValid();
1574 }
1575
Update(bool fromControl,bool updateDisplayOnly)1576 void CGUIControlRangeSetting::Update(bool fromControl, bool updateDisplayOnly)
1577 {
1578 if (m_pSlider == NULL || m_pSetting->GetType() != SettingType::List)
1579 return;
1580
1581 CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
1582
1583 std::shared_ptr<CSettingList> settingList = std::static_pointer_cast<CSettingList>(m_pSetting);
1584 const SettingList& settingListValues = settingList->GetValue();
1585 if (settingListValues.size() != 2)
1586 return;
1587
1588 SettingConstPtr listDefintion = settingList->GetDefinition();
1589 std::shared_ptr<const CSettingControlRange> controlRange =
1590 std::static_pointer_cast<const CSettingControlRange>(m_pSetting->GetControl());
1591 const std::string& controlFormat = controlRange->GetFormat();
1592
1593 std::string strText;
1594 std::string strTextLower, strTextUpper;
1595 std::string formatString =
1596 Localize(controlRange->GetFormatLabel() > -1 ? controlRange->GetFormatLabel() : 21469);
1597 std::string valueFormat = controlRange->GetValueFormat();
1598 if (controlRange->GetValueFormatLabel() > -1)
1599 valueFormat = Localize(controlRange->GetValueFormatLabel());
1600
1601 switch (listDefintion->GetType())
1602 {
1603 case SettingType::Integer:
1604 {
1605 int valueLower, valueUpper;
1606 if (fromControl)
1607 {
1608 valueLower = m_pSlider->GetIntValue(CGUISliderControl::RangeSelectorLower);
1609 valueUpper = m_pSlider->GetIntValue(CGUISliderControl::RangeSelectorUpper);
1610 }
1611 else
1612 {
1613 valueLower = std::static_pointer_cast<CSettingInt>(settingListValues[0])->GetValue();
1614 valueUpper = std::static_pointer_cast<CSettingInt>(settingListValues[1])->GetValue();
1615 m_pSlider->SetIntValue(valueLower, CGUISliderControl::RangeSelectorLower);
1616 m_pSlider->SetIntValue(valueUpper, CGUISliderControl::RangeSelectorUpper);
1617 }
1618
1619 if (controlFormat == "date" || controlFormat == "time")
1620 {
1621 CDateTime dateLower((time_t)valueLower);
1622 CDateTime dateUpper((time_t)valueUpper);
1623
1624 if (controlFormat == "date")
1625 {
1626 if (valueFormat.empty())
1627 {
1628 strTextLower = dateLower.GetAsLocalizedDate();
1629 strTextUpper = dateUpper.GetAsLocalizedDate();
1630 }
1631 else
1632 {
1633 strTextLower = dateLower.GetAsLocalizedDate(valueFormat);
1634 strTextUpper = dateUpper.GetAsLocalizedDate(valueFormat);
1635 }
1636 }
1637 else
1638 {
1639 if (valueFormat.empty())
1640 valueFormat = "mm:ss";
1641
1642 strTextLower = dateLower.GetAsLocalizedTime(valueFormat);
1643 strTextUpper = dateUpper.GetAsLocalizedTime(valueFormat);
1644 }
1645 }
1646 else
1647 {
1648 strTextLower = StringUtils::Format(valueFormat.c_str(), valueLower);
1649 strTextUpper = StringUtils::Format(valueFormat.c_str(), valueUpper);
1650 }
1651
1652 if (valueLower != valueUpper)
1653 strText =
1654 StringUtils::Format(formatString.c_str(), strTextLower.c_str(), strTextUpper.c_str());
1655 else
1656 strText = strTextLower;
1657 break;
1658 }
1659
1660 case SettingType::Number:
1661 {
1662 double valueLower, valueUpper;
1663 if (fromControl)
1664 {
1665 valueLower =
1666 static_cast<double>(m_pSlider->GetFloatValue(CGUISliderControl::RangeSelectorLower));
1667 valueUpper =
1668 static_cast<double>(m_pSlider->GetFloatValue(CGUISliderControl::RangeSelectorUpper));
1669 }
1670 else
1671 {
1672 valueLower = std::static_pointer_cast<CSettingNumber>(settingListValues[0])->GetValue();
1673 valueUpper = std::static_pointer_cast<CSettingNumber>(settingListValues[1])->GetValue();
1674 m_pSlider->SetFloatValue((float)valueLower, CGUISliderControl::RangeSelectorLower);
1675 m_pSlider->SetFloatValue((float)valueUpper, CGUISliderControl::RangeSelectorUpper);
1676 }
1677
1678 strTextLower = StringUtils::Format(valueFormat.c_str(), valueLower);
1679 if (valueLower != valueUpper)
1680 {
1681 strTextUpper = StringUtils::Format(valueFormat.c_str(), valueUpper);
1682 strText =
1683 StringUtils::Format(formatString.c_str(), strTextLower.c_str(), strTextUpper.c_str());
1684 }
1685 else
1686 strText = strTextLower;
1687 break;
1688 }
1689
1690 default:
1691 strText.clear();
1692 break;
1693 }
1694
1695 if (!strText.empty())
1696 m_pSlider->SetTextValue(strText);
1697 }
1698
CGUIControlSeparatorSetting(CGUIImage * pImage,int id,ILocalizer * localizer)1699 CGUIControlSeparatorSetting::CGUIControlSeparatorSetting(CGUIImage* pImage,
1700 int id,
1701 ILocalizer* localizer)
1702 : CGUIControlBaseSetting(id, NULL, localizer)
1703 {
1704 m_pImage = pImage;
1705 if (m_pImage == NULL)
1706 return;
1707
1708 m_pImage->SetID(id);
1709 }
1710
1711 CGUIControlSeparatorSetting::~CGUIControlSeparatorSetting() = default;
1712
CGUIControlGroupTitleSetting(CGUILabelControl * pLabel,int id,ILocalizer * localizer)1713 CGUIControlGroupTitleSetting::CGUIControlGroupTitleSetting(CGUILabelControl* pLabel,
1714 int id,
1715 ILocalizer* localizer)
1716 : CGUIControlBaseSetting(id, NULL, localizer)
1717 {
1718 m_pLabel = pLabel;
1719 if (m_pLabel == NULL)
1720 return;
1721
1722 m_pLabel->SetID(id);
1723 }
1724
1725 CGUIControlGroupTitleSetting::~CGUIControlGroupTitleSetting() = default;
1726
CGUIControlLabelSetting(CGUIButtonControl * pButton,int id,std::shared_ptr<CSetting> pSetting,ILocalizer * localizer)1727 CGUIControlLabelSetting::CGUIControlLabelSetting(CGUIButtonControl* pButton,
1728 int id,
1729 std::shared_ptr<CSetting> pSetting,
1730 ILocalizer* localizer)
1731 : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
1732 {
1733 m_pButton = pButton;
1734 if (m_pButton == NULL)
1735 return;
1736
1737 m_pButton->SetID(id);
1738 UpdateFromSetting();
1739 }
1740