summaryrefslogtreecommitdiffstats
path: root/xbmc/settings/windows/GUIControlSettings.cpp
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--xbmc/settings/windows/GUIControlSettings.cpp1798
1 files changed, 1798 insertions, 0 deletions
diff --git a/xbmc/settings/windows/GUIControlSettings.cpp b/xbmc/settings/windows/GUIControlSettings.cpp
new file mode 100644
index 0000000..fcbf1fa
--- /dev/null
+++ b/xbmc/settings/windows/GUIControlSettings.cpp
@@ -0,0 +1,1798 @@
+/*
+ * Copyright (C) 2005-2018 Team Kodi
+ * This file is part of Kodi - https://kodi.tv
+ *
+ * SPDX-License-Identifier: GPL-2.0-or-later
+ * See LICENSES/README.md for more information.
+ */
+
+#include "GUIControlSettings.h"
+
+#include "FileItem.h"
+#include "ServiceBroker.h"
+#include "Util.h"
+#include "addons/AddonManager.h"
+#include "addons/gui/GUIWindowAddonBrowser.h"
+#include "addons/settings/SettingUrlEncodedString.h"
+#include "dialogs/GUIDialogColorPicker.h"
+#include "dialogs/GUIDialogFileBrowser.h"
+#include "dialogs/GUIDialogNumeric.h"
+#include "dialogs/GUIDialogSelect.h"
+#include "dialogs/GUIDialogSlider.h"
+#include "guilib/GUIColorButtonControl.h"
+#include "guilib/GUIComponent.h"
+#include "guilib/GUIEditControl.h"
+#include "guilib/GUIImage.h"
+#include "guilib/GUIKeyboardFactory.h"
+#include "guilib/GUILabelControl.h"
+#include "guilib/GUIRadioButtonControl.h"
+#include "guilib/GUISettingsSliderControl.h"
+#include "guilib/GUISpinControlEx.h"
+#include "guilib/GUIWindowManager.h"
+#include "guilib/LocalizeStrings.h"
+#include "settings/MediaSourceSettings.h"
+#include "settings/SettingAddon.h"
+#include "settings/SettingControl.h"
+#include "settings/SettingDateTime.h"
+#include "settings/SettingPath.h"
+#include "settings/SettingUtils.h"
+#include "settings/lib/Setting.h"
+#include "settings/lib/SettingDefinitions.h"
+#include "storage/MediaManager.h"
+#include "utils/FileExtensionProvider.h"
+#include "utils/StringUtils.h"
+#include "utils/Variant.h"
+#include "utils/log.h"
+
+#include <set>
+#include <utility>
+
+using namespace ADDON;
+
+static std::string Localize(std::uint32_t code,
+ ILocalizer* localizer,
+ const std::string& addonId = "")
+{
+ if (localizer == nullptr)
+ return "";
+
+ if (!addonId.empty())
+ {
+ std::string label = g_localizeStrings.GetAddonString(addonId, code);
+ if (!label.empty())
+ return label;
+ }
+
+ return localizer->Localize(code);
+}
+
+template<typename TValueType>
+static CFileItemPtr GetFileItem(const std::string& label,
+ const std::string& label2,
+ const TValueType& value,
+ const std::vector<std::pair<std::string, CVariant>>& properties,
+ const std::set<TValueType>& selectedValues)
+{
+ CFileItemPtr item(new CFileItem(label));
+ item->SetProperty("value", value);
+ item->SetLabel2(label2);
+
+ for (const auto& prop : properties)
+ item->SetProperty(prop.first, prop.second);
+
+ if (selectedValues.find(value) != selectedValues.end())
+ item->Select(true);
+
+ return item;
+}
+
+template<class SettingOption>
+static bool CompareSettingOptionAseconding(const SettingOption& lhs, const SettingOption& rhs)
+{
+ return StringUtils::CompareNoCase(lhs.label, rhs.label) < 0;
+}
+
+template<class SettingOption>
+static bool CompareSettingOptionDeseconding(const SettingOption& lhs, const SettingOption& rhs)
+{
+ return StringUtils::CompareNoCase(lhs.label, rhs.label) > 0;
+}
+
+static bool GetIntegerOptions(const SettingConstPtr& setting,
+ IntegerSettingOptions& options,
+ std::set<int>& selectedOptions,
+ ILocalizer* localizer,
+ bool updateOptions)
+{
+ std::shared_ptr<const CSettingInt> pSettingInt = NULL;
+ if (setting->GetType() == SettingType::Integer)
+ pSettingInt = std::static_pointer_cast<const CSettingInt>(setting);
+ else if (setting->GetType() == SettingType::List)
+ {
+ std::shared_ptr<const CSettingList> settingList =
+ std::static_pointer_cast<const CSettingList>(setting);
+ if (settingList->GetElementType() != SettingType::Integer)
+ return false;
+
+ pSettingInt = std::static_pointer_cast<const CSettingInt>(settingList->GetDefinition());
+ }
+
+ switch (pSettingInt->GetOptionsType())
+ {
+ case SettingOptionsType::StaticTranslatable:
+ {
+ const TranslatableIntegerSettingOptions& settingOptions =
+ pSettingInt->GetTranslatableOptions();
+ for (const auto& option : settingOptions)
+ options.push_back(
+ IntegerSettingOption(Localize(option.label, localizer, option.addonId), option.value));
+ break;
+ }
+
+ case SettingOptionsType::Static:
+ {
+ const IntegerSettingOptions& settingOptions = pSettingInt->GetOptions();
+ options.insert(options.end(), settingOptions.begin(), settingOptions.end());
+ break;
+ }
+
+ case SettingOptionsType::Dynamic:
+ {
+ IntegerSettingOptions settingOptions;
+ if (updateOptions)
+ settingOptions = std::const_pointer_cast<CSettingInt>(pSettingInt)->UpdateDynamicOptions();
+ else
+ settingOptions = pSettingInt->GetDynamicOptions();
+ options.insert(options.end(), settingOptions.begin(), settingOptions.end());
+ break;
+ }
+
+ case SettingOptionsType::Unknown:
+ default:
+ {
+ std::shared_ptr<const CSettingControlFormattedRange> control =
+ std::static_pointer_cast<const CSettingControlFormattedRange>(pSettingInt->GetControl());
+ for (int i = pSettingInt->GetMinimum(); i <= pSettingInt->GetMaximum();
+ i += pSettingInt->GetStep())
+ {
+ std::string strLabel;
+ if (i == pSettingInt->GetMinimum() && control->GetMinimumLabel() > -1)
+ strLabel = Localize(control->GetMinimumLabel(), localizer);
+ else if (control->GetFormatLabel() > -1)
+ strLabel = StringUtils::Format(Localize(control->GetFormatLabel(), localizer), i);
+ else
+ strLabel = StringUtils::Format(control->GetFormatString(), i);
+
+ options.push_back(IntegerSettingOption(strLabel, i));
+ }
+
+ break;
+ }
+ }
+
+ switch (pSettingInt->GetOptionsSort())
+ {
+ case SettingOptionsSort::Ascending:
+ std::sort(options.begin(), options.end(),
+ CompareSettingOptionAseconding<IntegerSettingOption>);
+ break;
+
+ case SettingOptionsSort::Descending:
+ std::sort(options.begin(), options.end(),
+ CompareSettingOptionDeseconding<IntegerSettingOption>);
+ break;
+
+ case SettingOptionsSort::NoSorting:
+ default:
+ break;
+ }
+
+ // this must be done after potentially calling CSettingInt::UpdateDynamicOptions() because it can
+ // change the value of the setting
+ if (setting->GetType() == SettingType::Integer)
+ selectedOptions.insert(pSettingInt->GetValue());
+ else if (setting->GetType() == SettingType::List)
+ {
+ std::vector<CVariant> list =
+ CSettingUtils::GetList(std::static_pointer_cast<const CSettingList>(setting));
+ for (const auto& itValue : list)
+ selectedOptions.insert((int)itValue.asInteger());
+ }
+ else
+ return false;
+
+ return true;
+}
+
+static bool GetStringOptions(const SettingConstPtr& setting,
+ StringSettingOptions& options,
+ std::set<std::string>& selectedOptions,
+ ILocalizer* localizer,
+ bool updateOptions)
+{
+ std::shared_ptr<const CSettingString> pSettingString = NULL;
+ if (setting->GetType() == SettingType::String)
+ pSettingString = std::static_pointer_cast<const CSettingString>(setting);
+ else if (setting->GetType() == SettingType::List)
+ {
+ std::shared_ptr<const CSettingList> settingList =
+ std::static_pointer_cast<const CSettingList>(setting);
+ if (settingList->GetElementType() != SettingType::String)
+ return false;
+
+ pSettingString = std::static_pointer_cast<const CSettingString>(settingList->GetDefinition());
+ }
+
+ switch (pSettingString->GetOptionsType())
+ {
+ case SettingOptionsType::StaticTranslatable:
+ {
+ const TranslatableStringSettingOptions& settingOptions =
+ pSettingString->GetTranslatableOptions();
+ for (const auto& option : settingOptions)
+ options.push_back(StringSettingOption(Localize(option.first, localizer), option.second));
+ break;
+ }
+
+ case SettingOptionsType::Static:
+ {
+ const StringSettingOptions& settingOptions = pSettingString->GetOptions();
+ options.insert(options.end(), settingOptions.begin(), settingOptions.end());
+ break;
+ }
+
+ case SettingOptionsType::Dynamic:
+ {
+ StringSettingOptions settingOptions;
+ if (updateOptions)
+ settingOptions =
+ std::const_pointer_cast<CSettingString>(pSettingString)->UpdateDynamicOptions();
+ else
+ settingOptions = pSettingString->GetDynamicOptions();
+ options.insert(options.end(), settingOptions.begin(), settingOptions.end());
+ break;
+ }
+
+ case SettingOptionsType::Unknown:
+ default:
+ return false;
+ }
+
+ switch (pSettingString->GetOptionsSort())
+ {
+ case SettingOptionsSort::Ascending:
+ std::sort(options.begin(), options.end(),
+ CompareSettingOptionAseconding<StringSettingOption>);
+ break;
+
+ case SettingOptionsSort::Descending:
+ std::sort(options.begin(), options.end(),
+ CompareSettingOptionDeseconding<StringSettingOption>);
+ break;
+
+ case SettingOptionsSort::NoSorting:
+ default:
+ break;
+ }
+
+ // this must be done after potentially calling CSettingString::UpdateDynamicOptions() because it
+ // can change the value of the setting
+ if (setting->GetType() == SettingType::String)
+ selectedOptions.insert(pSettingString->GetValue());
+ else if (setting->GetType() == SettingType::List)
+ {
+ std::vector<CVariant> list =
+ CSettingUtils::GetList(std::static_pointer_cast<const CSettingList>(setting));
+ for (const auto& itValue : list)
+ selectedOptions.insert(itValue.asString());
+ }
+ else
+ return false;
+
+ return true;
+}
+
+CGUIControlBaseSetting::CGUIControlBaseSetting(int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer)
+ : m_id(id),
+ m_pSetting(std::move(pSetting)),
+ m_localizer(localizer),
+ m_delayed(false),
+ m_valid(true)
+{
+}
+
+bool CGUIControlBaseSetting::IsEnabled() const
+{
+ return m_pSetting != NULL && m_pSetting->IsEnabled();
+}
+
+void CGUIControlBaseSetting::UpdateFromControl()
+{
+ Update(true, true);
+}
+
+void CGUIControlBaseSetting::UpdateFromSetting(bool updateDisplayOnly /* = false */)
+{
+ Update(false, updateDisplayOnly);
+}
+
+std::string CGUIControlBaseSetting::Localize(std::uint32_t code) const
+{
+ return ::Localize(code, m_localizer);
+}
+
+void CGUIControlBaseSetting::Update(bool fromControl, bool updateDisplayOnly)
+{
+ if (fromControl || updateDisplayOnly)
+ return;
+
+ CGUIControl* control = GetControl();
+ if (control == NULL)
+ return;
+
+ control->SetEnabled(IsEnabled());
+ if (m_pSetting)
+ control->SetVisible(m_pSetting->IsVisible());
+ SetValid(true);
+}
+
+CGUIControlRadioButtonSetting::CGUIControlRadioButtonSetting(CGUIRadioButtonControl* pRadioButton,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
+{
+ m_pRadioButton = pRadioButton;
+ if (m_pRadioButton == NULL)
+ return;
+
+ m_pRadioButton->SetID(id);
+}
+
+CGUIControlRadioButtonSetting::~CGUIControlRadioButtonSetting() = default;
+
+bool CGUIControlRadioButtonSetting::OnClick()
+{
+ SetValid(std::static_pointer_cast<CSettingBool>(m_pSetting)
+ ->SetValue(!std::static_pointer_cast<CSettingBool>(m_pSetting)->GetValue()));
+ return IsValid();
+}
+
+void CGUIControlRadioButtonSetting::Update(bool fromControl, bool updateDisplayOnly)
+{
+ if (fromControl || m_pRadioButton == NULL)
+ return;
+
+ CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
+
+ m_pRadioButton->SetSelected(std::static_pointer_cast<CSettingBool>(m_pSetting)->GetValue());
+}
+
+CGUIControlColorButtonSetting::CGUIControlColorButtonSetting(
+ CGUIColorButtonControl* pColorControl,
+ int id,
+ const std::shared_ptr<CSetting>& pSetting,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, pSetting, localizer)
+{
+ m_pColorButton = pColorControl;
+ if (!m_pColorButton)
+ return;
+
+ m_pColorButton->SetID(id);
+}
+
+CGUIControlColorButtonSetting::~CGUIControlColorButtonSetting() = default;
+
+bool CGUIControlColorButtonSetting::OnClick()
+{
+ if (!m_pColorButton)
+ return false;
+
+ std::shared_ptr<CSettingString> settingHexColor =
+ std::static_pointer_cast<CSettingString>(m_pSetting);
+
+ CGUIDialogColorPicker* dialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogColorPicker>(
+ WINDOW_DIALOG_COLOR_PICKER);
+ if (!dialog)
+ return false;
+
+ dialog->Reset();
+ dialog->SetHeading(CVariant{Localize(m_pSetting->GetLabel())});
+ dialog->LoadColors();
+ std::string hexColor;
+ if (settingHexColor)
+ hexColor = settingHexColor.get()->GetValue();
+ dialog->SetSelectedColor(hexColor);
+ dialog->Open();
+
+ if (!dialog->IsConfirmed())
+ return false;
+
+ SetValid(
+ std::static_pointer_cast<CSettingString>(m_pSetting)->SetValue(dialog->GetSelectedColor()));
+ return IsValid();
+}
+
+void CGUIControlColorButtonSetting::Update(bool fromControl, bool updateDisplayOnly)
+{
+ if (fromControl || !m_pColorButton)
+ return;
+
+ CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
+ // Set the color to apply to the preview color box
+ m_pColorButton->SetImageBoxColor(
+ std::static_pointer_cast<CSettingString>(m_pSetting)->GetValue());
+}
+
+CGUIControlSpinExSetting::CGUIControlSpinExSetting(CGUISpinControlEx* pSpin,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
+{
+ m_pSpin = pSpin;
+ if (m_pSpin == NULL)
+ return;
+
+ m_pSpin->SetID(id);
+
+ const std::string& controlFormat = m_pSetting->GetControl()->GetFormat();
+ if (controlFormat == "number")
+ {
+ std::shared_ptr<CSettingNumber> pSettingNumber =
+ std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ m_pSpin->SetType(SPIN_CONTROL_TYPE_FLOAT);
+ m_pSpin->SetFloatRange(static_cast<float>(pSettingNumber->GetMinimum()),
+ static_cast<float>(pSettingNumber->GetMaximum()));
+ m_pSpin->SetFloatInterval(static_cast<float>(pSettingNumber->GetStep()));
+ }
+ else if (controlFormat == "integer")
+ m_pSpin->SetType(SPIN_CONTROL_TYPE_TEXT);
+ else if (controlFormat == "string")
+ {
+ m_pSpin->SetType(SPIN_CONTROL_TYPE_TEXT);
+
+ if (m_pSetting->GetType() == SettingType::Integer)
+ FillIntegerSettingControl(false);
+ else if (m_pSetting->GetType() == SettingType::Number)
+ {
+ std::shared_ptr<CSettingNumber> pSettingNumber =
+ std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ std::shared_ptr<const CSettingControlFormattedRange> control =
+ std::static_pointer_cast<const CSettingControlFormattedRange>(m_pSetting->GetControl());
+ int index = 0;
+ for (double value = pSettingNumber->GetMinimum(); value <= pSettingNumber->GetMaximum();
+ value += pSettingNumber->GetStep(), index++)
+ {
+ std::string strLabel;
+ if (value == pSettingNumber->GetMinimum() && control->GetMinimumLabel() > -1)
+ strLabel = Localize(control->GetMinimumLabel());
+ else if (control->GetFormatLabel() > -1)
+ strLabel = StringUtils::Format(Localize(control->GetFormatLabel()), value);
+ else
+ strLabel = StringUtils::Format(control->GetFormatString(), value);
+
+ m_pSpin->AddLabel(strLabel, index);
+ }
+ }
+ }
+}
+
+CGUIControlSpinExSetting::~CGUIControlSpinExSetting() = default;
+
+bool CGUIControlSpinExSetting::OnClick()
+{
+ if (m_pSpin == NULL)
+ return false;
+
+ switch (m_pSetting->GetType())
+ {
+ case SettingType::Integer:
+ SetValid(std::static_pointer_cast<CSettingInt>(m_pSetting)->SetValue(m_pSpin->GetValue()));
+ break;
+
+ case SettingType::Number:
+ {
+ auto pSettingNumber = std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ const auto& controlFormat = m_pSetting->GetControl()->GetFormat();
+ if (controlFormat == "number")
+ SetValid(pSettingNumber->SetValue(static_cast<double>(m_pSpin->GetFloatValue())));
+ else
+ SetValid(pSettingNumber->SetValue(pSettingNumber->GetMinimum() +
+ pSettingNumber->GetStep() * m_pSpin->GetValue()));
+
+ break;
+ }
+
+ case SettingType::String:
+ SetValid(std::static_pointer_cast<CSettingString>(m_pSetting)
+ ->SetValue(m_pSpin->GetStringValue()));
+ break;
+
+ default:
+ return false;
+ }
+
+ return IsValid();
+}
+
+void CGUIControlSpinExSetting::Update(bool fromControl, bool updateDisplayOnly)
+{
+ if (fromControl || m_pSpin == NULL)
+ return;
+
+ CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
+
+ FillControl(!updateDisplayOnly);
+
+ if (!updateDisplayOnly)
+ {
+ // disable the spinner if it has less than two items
+ if (!m_pSpin->IsDisabled() && (m_pSpin->GetMaximum() - m_pSpin->GetMinimum()) == 0)
+ m_pSpin->SetEnabled(false);
+ }
+}
+
+void CGUIControlSpinExSetting::FillControl(bool updateValues)
+{
+ if (m_pSpin == NULL)
+ return;
+
+ if (updateValues)
+ m_pSpin->Clear();
+
+ const std::string& controlFormat = m_pSetting->GetControl()->GetFormat();
+ if (controlFormat == "number")
+ {
+ std::shared_ptr<CSettingNumber> pSettingNumber =
+ std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ m_pSpin->SetFloatValue((float)pSettingNumber->GetValue());
+ }
+ else if (controlFormat == "integer")
+ FillIntegerSettingControl(updateValues);
+ else if (controlFormat == "string")
+ {
+ if (m_pSetting->GetType() == SettingType::Integer)
+ FillIntegerSettingControl(updateValues);
+ else if (m_pSetting->GetType() == SettingType::Number)
+ FillFloatSettingControl();
+ else if (m_pSetting->GetType() == SettingType::String)
+ FillStringSettingControl(updateValues);
+ }
+}
+
+void CGUIControlSpinExSetting::FillIntegerSettingControl(bool updateValues)
+{
+ IntegerSettingOptions options;
+ std::set<int> selectedValues;
+ // get the integer options
+ if (!GetIntegerOptions(m_pSetting, options, selectedValues, m_localizer, updateValues) ||
+ selectedValues.size() != 1)
+ return;
+
+ if (updateValues)
+ {
+ // add them to the spinner
+ for (const auto& option : options)
+ m_pSpin->AddLabel(option.label, option.value);
+ }
+
+ // and set the current value
+ m_pSpin->SetValue(*selectedValues.begin());
+}
+
+void CGUIControlSpinExSetting::FillFloatSettingControl()
+{
+ std::shared_ptr<CSettingNumber> pSettingNumber =
+ std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ std::shared_ptr<const CSettingControlFormattedRange> control =
+ std::static_pointer_cast<const CSettingControlFormattedRange>(m_pSetting->GetControl());
+ int index = 0;
+ int currentIndex = 0;
+ for (double value = pSettingNumber->GetMinimum(); value <= pSettingNumber->GetMaximum();
+ value += pSettingNumber->GetStep(), index++)
+ {
+ if (value == pSettingNumber->GetValue())
+ {
+ currentIndex = index;
+ break;
+ }
+ }
+
+ m_pSpin->SetValue(currentIndex);
+}
+
+void CGUIControlSpinExSetting::FillStringSettingControl(bool updateValues)
+{
+ StringSettingOptions options;
+ std::set<std::string> selectedValues;
+ // get the string options
+ if (!GetStringOptions(m_pSetting, options, selectedValues, m_localizer, updateValues) ||
+ selectedValues.size() != 1)
+ return;
+
+ if (updateValues)
+ {
+ // add them to the spinner
+ for (const auto& option : options)
+ m_pSpin->AddLabel(option.label, option.value);
+ }
+
+ // and set the current value
+ m_pSpin->SetStringValue(*selectedValues.begin());
+}
+
+CGUIControlListSetting::CGUIControlListSetting(CGUIButtonControl* pButton,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
+{
+ m_pButton = pButton;
+ if (m_pButton == NULL)
+ return;
+
+ m_pButton->SetID(id);
+}
+
+CGUIControlListSetting::~CGUIControlListSetting() = default;
+
+bool CGUIControlListSetting::OnClick()
+{
+ if (m_pButton == NULL)
+ return false;
+
+ CGUIDialogSelect* dialog =
+ CServiceBroker::GetGUI()->GetWindowManager().GetWindow<CGUIDialogSelect>(
+ WINDOW_DIALOG_SELECT);
+ if (dialog == NULL)
+ return false;
+
+ CFileItemList options;
+ std::shared_ptr<const CSettingControlList> control =
+ std::static_pointer_cast<const CSettingControlList>(m_pSetting->GetControl());
+ bool optionsValid = GetItems(m_pSetting, options, false);
+
+ bool bValueAdded = false;
+ bool bAllowNewOption = false;
+ if (m_pSetting->GetType() == SettingType::List)
+ {
+ std::shared_ptr<const CSettingList> settingList =
+ std::static_pointer_cast<const CSettingList>(m_pSetting);
+ if (settingList->GetElementType() == SettingType::String)
+ {
+ bAllowNewOption = std::static_pointer_cast<const CSettingString>(settingList->GetDefinition())
+ ->AllowNewOption();
+ }
+ }
+ if (!bAllowNewOption)
+ {
+ // Do not show dialog if
+ // there are no items to be chosen
+ if (!optionsValid || options.Size() <= 0)
+ return false;
+
+ dialog->Reset();
+ dialog->SetHeading(CVariant{Localize(m_pSetting->GetLabel())});
+ dialog->SetItems(options);
+ dialog->SetMultiSelection(control->CanMultiSelect());
+ dialog->SetUseDetails(control->UseDetails());
+ dialog->Open();
+
+ if (!dialog->IsConfirmed())
+ return false;
+ }
+ else
+ {
+ // Possible to add items, as well as select from any options given
+ // Add any current values that are not options as items in list
+ std::vector<CVariant> list =
+ CSettingUtils::GetList(std::static_pointer_cast<const CSettingList>(m_pSetting));
+ for (const auto& value : list)
+ {
+ bool found = std::any_of(options.begin(), options.end(), [&](const auto& p) {
+ return p->GetProperty("value").asString() == value.asString();
+ });
+ if (!found)
+ {
+ CFileItemPtr item(new CFileItem(value.asString()));
+ item->SetProperty("value", value.asString());
+ item->Select(true);
+ options.Add(item);
+ }
+ }
+
+ bool bRepeat = true;
+ while (bRepeat)
+ {
+ std::string strAddButton = Localize(control->GetAddButtonLabel());
+ if (strAddButton.empty())
+ strAddButton = Localize(15019); // "ADD"
+ dialog->Reset(); // Clears AddButtonPressed
+ dialog->SetHeading(CVariant{ Localize(m_pSetting->GetLabel()) });
+ dialog->SetItems(options);
+ dialog->SetMultiSelection(control->CanMultiSelect());
+ dialog->EnableButton2(bAllowNewOption, strAddButton);
+
+ dialog->Open();
+
+ if (!dialog->IsConfirmed())
+ return false;
+
+ if (dialog->IsButton2Pressed())
+ {
+ // Get new list value
+ std::string strLabel;
+ bool bValidType = false;
+ while (!bValidType && CGUIKeyboardFactory::ShowAndGetInput(
+ strLabel, CVariant{ Localize(control->GetAddButtonLabel()) }, false))
+ {
+ // Validate new value is unique and truncate at any comma
+ StringUtils::Trim(strLabel);
+ strLabel = strLabel.substr(0, strLabel.find(','));
+ if (!strLabel.empty())
+ {
+ bValidType = !std::any_of(options.begin(), options.end(), [&](const auto& p) {
+ return p->GetProperty("value").asString() == strLabel;
+ });
+ }
+ if (bValidType)
+ { // Add new value to the list of options
+ CFileItemPtr pItem(new CFileItem(strLabel));
+ pItem->Select(true);
+ pItem->SetProperty("value", strLabel);
+ options.Add(pItem);
+ bValueAdded = true;
+ }
+ }
+ }
+ bRepeat = dialog->IsButton2Pressed();
+ }
+ }
+
+ std::vector<CVariant> values;
+ for (int i : dialog->GetSelectedItems())
+ {
+ const CFileItemPtr item = options.Get(i);
+ if (item == NULL || !item->HasProperty("value"))
+ return false;
+
+ values.push_back(item->GetProperty("value"));
+ }
+
+ bool ret = false;
+ switch (m_pSetting->GetType())
+ {
+ case SettingType::Integer:
+ if (values.size() > 1)
+ return false;
+ ret = std::static_pointer_cast<CSettingInt>(m_pSetting)
+ ->SetValue((int)values.at(0).asInteger());
+ break;
+
+ case SettingType::String:
+ if (values.size() > 1)
+ return false;
+ ret = std::static_pointer_cast<CSettingString>(m_pSetting)->SetValue(values.at(0).asString());
+ break;
+
+ case SettingType::List:
+ ret = CSettingUtils::SetList(std::static_pointer_cast<CSettingList>(m_pSetting), values);
+ break;
+
+ default:
+ return false;
+ }
+
+ if (ret)
+ UpdateFromSetting(!bValueAdded);
+ else
+ SetValid(false);
+
+ return IsValid();
+}
+
+void CGUIControlListSetting::Update(bool fromControl, bool updateDisplayOnly)
+{
+ if (fromControl || m_pButton == NULL)
+ return;
+
+ CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
+
+ CFileItemList options;
+ std::shared_ptr<const CSettingControlList> control =
+ std::static_pointer_cast<const CSettingControlList>(m_pSetting->GetControl());
+ bool optionsValid = GetItems(m_pSetting, options, !updateDisplayOnly);
+
+ bool bAllowNewOption = false;
+ if (m_pSetting->GetType() == SettingType::List)
+ {
+ std::shared_ptr<const CSettingList> settingList =
+ std::static_pointer_cast<const CSettingList>(m_pSetting);
+ if (settingList->GetElementType() == SettingType::String)
+ {
+ bAllowNewOption = std::static_pointer_cast<const CSettingString>(settingList->GetDefinition())
+ ->AllowNewOption();
+ }
+ }
+
+ std::string label2;
+ if (optionsValid && !control->HideValue())
+ {
+ SettingControlListValueFormatter formatter = control->GetFormatter();
+ if (formatter)
+ label2 = formatter(m_pSetting);
+
+ if (label2.empty() && bAllowNewOption)
+ {
+ const std::shared_ptr<const CSettingList> settingList =
+ std::static_pointer_cast<const CSettingList>(m_pSetting);
+ label2 = settingList->ToString();
+ }
+
+ if (label2.empty())
+ {
+ std::vector<std::string> labels;
+ for (int index = 0; index < options.Size(); index++)
+ {
+ const CFileItemPtr pItem = options.Get(index);
+ if (pItem->IsSelected())
+ labels.push_back(pItem->GetLabel());
+ }
+
+ label2 = StringUtils::Join(labels, ", ");
+ }
+ }
+
+ m_pButton->SetLabel2(label2);
+
+ if (!updateDisplayOnly)
+ {
+ // Disable the control if no items can be added and
+ // there are no items to be chosen
+ if (!m_pButton->IsDisabled() && !bAllowNewOption && (options.Size() <= 0))
+ m_pButton->SetEnabled(false);
+ }
+}
+
+bool CGUIControlListSetting::GetItems(const SettingConstPtr& setting,
+ CFileItemList& items,
+ bool updateItems) const
+{
+ std::shared_ptr<const CSettingControlList> control =
+ std::static_pointer_cast<const CSettingControlList>(setting->GetControl());
+ const std::string& controlFormat = control->GetFormat();
+
+ if (controlFormat == "integer")
+ return GetIntegerItems(setting, items, updateItems);
+ else if (controlFormat == "string")
+ {
+ if (setting->GetType() == SettingType::Integer ||
+ (setting->GetType() == SettingType::List &&
+ std::static_pointer_cast<const CSettingList>(setting)->GetElementType() ==
+ SettingType::Integer))
+ return GetIntegerItems(setting, items, updateItems);
+ else if (setting->GetType() == SettingType::String ||
+ (setting->GetType() == SettingType::List &&
+ std::static_pointer_cast<const CSettingList>(setting)->GetElementType() ==
+ SettingType::String))
+ return GetStringItems(setting, items, updateItems);
+ }
+ else
+ return false;
+
+ return true;
+}
+
+bool CGUIControlListSetting::GetIntegerItems(const SettingConstPtr& setting,
+ CFileItemList& items,
+ bool updateItems) const
+{
+ IntegerSettingOptions options;
+ std::set<int> selectedValues;
+ // get the integer options
+ if (!GetIntegerOptions(setting, options, selectedValues, m_localizer, updateItems))
+ return false;
+
+ // turn them into CFileItems and add them to the item list
+ for (const auto& option : options)
+ items.Add(
+ GetFileItem(option.label, option.label2, option.value, option.properties, selectedValues));
+
+ return true;
+}
+
+bool CGUIControlListSetting::GetStringItems(const SettingConstPtr& setting,
+ CFileItemList& items,
+ bool updateItems) const
+{
+ StringSettingOptions options;
+ std::set<std::string> selectedValues;
+ // get the string options
+ if (!GetStringOptions(setting, options, selectedValues, m_localizer, updateItems))
+ return false;
+
+ // turn them into CFileItems and add them to the item list
+ for (const auto& option : options)
+ items.Add(
+ GetFileItem(option.label, option.label2, option.value, option.properties, selectedValues));
+
+ return true;
+}
+
+CGUIControlButtonSetting::CGUIControlButtonSetting(CGUIButtonControl* pButton,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
+{
+ m_pButton = pButton;
+ if (m_pButton == NULL)
+ return;
+
+ m_pButton->SetID(id);
+}
+
+CGUIControlButtonSetting::~CGUIControlButtonSetting() = default;
+
+bool CGUIControlButtonSetting::OnClick()
+{
+ if (m_pButton == NULL)
+ return false;
+
+ std::shared_ptr<const ISettingControl> control = m_pSetting->GetControl();
+ const std::string& controlType = control->GetType();
+ const std::string& controlFormat = control->GetFormat();
+ if (controlType == "button")
+ {
+ std::shared_ptr<const CSettingControlButton> buttonControl =
+ std::static_pointer_cast<const CSettingControlButton>(control);
+ if (controlFormat == "addon")
+ {
+ // prompt for the addon
+ std::shared_ptr<CSettingAddon> setting;
+ std::vector<std::string> addonIDs;
+ if (m_pSetting->GetType() == SettingType::List)
+ {
+ std::shared_ptr<CSettingList> settingList =
+ std::static_pointer_cast<CSettingList>(m_pSetting);
+ setting = std::static_pointer_cast<CSettingAddon>(settingList->GetDefinition());
+ for (const SettingPtr& addon : settingList->GetValue())
+ addonIDs.push_back(std::static_pointer_cast<CSettingAddon>(addon)->GetValue());
+ }
+ else
+ {
+ setting = std::static_pointer_cast<CSettingAddon>(m_pSetting);
+ addonIDs.push_back(setting->GetValue());
+ }
+
+ if (CGUIWindowAddonBrowser::SelectAddonID(
+ setting->GetAddonType(), addonIDs, setting->AllowEmpty(),
+ buttonControl->ShowAddonDetails(), m_pSetting->GetType() == SettingType::List,
+ buttonControl->ShowInstalledAddons(), buttonControl->ShowInstallableAddons(),
+ buttonControl->ShowMoreAddons()) != 1)
+ return false;
+
+ if (m_pSetting->GetType() == SettingType::List)
+ std::static_pointer_cast<CSettingList>(m_pSetting)->FromString(addonIDs);
+ else
+ SetValid(setting->SetValue(addonIDs[0]));
+ }
+ else if (controlFormat == "path" || controlFormat == "file" || controlFormat == "image")
+ SetValid(GetPath(std::static_pointer_cast<CSettingPath>(m_pSetting), m_localizer));
+ else if (controlFormat == "date")
+ {
+ std::shared_ptr<CSettingDate> settingDate =
+ std::static_pointer_cast<CSettingDate>(m_pSetting);
+
+ KODI::TIME::SystemTime systemdate;
+ settingDate->GetDate().GetAsSystemTime(systemdate);
+ if (CGUIDialogNumeric::ShowAndGetDate(systemdate, Localize(buttonControl->GetHeading())))
+ SetValid(settingDate->SetDate(CDateTime(systemdate)));
+ }
+ else if (controlFormat == "time")
+ {
+ std::shared_ptr<CSettingTime> settingTime =
+ std::static_pointer_cast<CSettingTime>(m_pSetting);
+
+ KODI::TIME::SystemTime systemtime;
+ settingTime->GetTime().GetAsSystemTime(systemtime);
+
+ if (CGUIDialogNumeric::ShowAndGetTime(systemtime, Localize(buttonControl->GetHeading())))
+ SetValid(settingTime->SetTime(CDateTime(systemtime)));
+ }
+ else if (controlFormat == "action")
+ {
+ // simply call the OnSettingAction callback and whoever knows what to
+ // do can do so (based on the setting's identification)
+ m_pSetting->OnSettingAction(m_pSetting);
+ SetValid(true);
+ }
+ }
+ else if (controlType == "slider")
+ {
+ float value, min, step, max;
+ if (m_pSetting->GetType() == SettingType::Integer)
+ {
+ std::shared_ptr<CSettingInt> settingInt = std::static_pointer_cast<CSettingInt>(m_pSetting);
+ value = (float)settingInt->GetValue();
+ min = (float)settingInt->GetMinimum();
+ step = (float)settingInt->GetStep();
+ max = (float)settingInt->GetMaximum();
+ }
+ else if (m_pSetting->GetType() == SettingType::Number)
+ {
+ std::shared_ptr<CSettingNumber> settingNumber =
+ std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ value = (float)settingNumber->GetValue();
+ min = (float)settingNumber->GetMinimum();
+ step = (float)settingNumber->GetStep();
+ max = (float)settingNumber->GetMaximum();
+ }
+ else
+ return false;
+
+ std::shared_ptr<const CSettingControlSlider> sliderControl =
+ std::static_pointer_cast<const CSettingControlSlider>(control);
+ CGUIDialogSlider::ShowAndGetInput(Localize(sliderControl->GetHeading()), value, min, step, max,
+ this, NULL);
+ SetValid(true);
+ }
+
+ // update the displayed value
+ UpdateFromSetting(true);
+
+ return IsValid();
+}
+
+void CGUIControlButtonSetting::Update(bool fromControl, bool updateDisplayOnly)
+{
+ if (fromControl || m_pButton == NULL)
+ return;
+
+ CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
+
+ std::string strText;
+ std::shared_ptr<const ISettingControl> control = m_pSetting->GetControl();
+ const std::string& controlType = control->GetType();
+ const std::string& controlFormat = control->GetFormat();
+
+ if (controlType == "button")
+ {
+ if (!std::static_pointer_cast<const CSettingControlButton>(control)->HideValue())
+ {
+ auto setting = m_pSetting;
+ if (m_pSetting->GetType() == SettingType::List)
+ setting = std::static_pointer_cast<CSettingList>(m_pSetting)->GetDefinition();
+
+ switch (setting->GetType())
+ {
+ case SettingType::String:
+ {
+ if (controlFormat == "addon")
+ {
+ std::vector<std::string> addonIDs;
+ if (m_pSetting->GetType() == SettingType::List)
+ {
+ for (const auto& addonSetting :
+ std::static_pointer_cast<CSettingList>(m_pSetting)->GetValue())
+ addonIDs.push_back(
+ std::static_pointer_cast<CSettingAddon>(addonSetting)->GetValue());
+ }
+ else
+ addonIDs.push_back(std::static_pointer_cast<CSettingString>(setting)->GetValue());
+
+ std::vector<std::string> addonNames;
+ for (const auto& addonID : addonIDs)
+ {
+ ADDON::AddonPtr addon;
+ if (CServiceBroker::GetAddonMgr().GetAddon(addonID, addon,
+ ADDON::OnlyEnabled::CHOICE_YES))
+ addonNames.push_back(addon->Name());
+ }
+
+ if (addonNames.empty())
+ strText = g_localizeStrings.Get(231); // None
+ else
+ strText = StringUtils::Join(addonNames, ", ");
+ }
+ else
+ {
+ std::string strValue = std::static_pointer_cast<CSettingString>(setting)->GetValue();
+ if (controlFormat == "path" || controlFormat == "file" || controlFormat == "image")
+ {
+ std::string shortPath;
+ if (CUtil::MakeShortenPath(strValue, shortPath, 30))
+ strText = shortPath;
+ }
+ else if (controlFormat == "infolabel")
+ {
+ strText = strValue;
+ if (strText.empty())
+ strText = g_localizeStrings.Get(231); // None
+ }
+ else
+ strText = strValue;
+ }
+
+ break;
+ }
+
+ case SettingType::Action:
+ {
+ // CSettingAction.
+ // Note: This can be removed once all settings use a proper control & format combination.
+ // CSettingAction is strictly speaking not designed to have a label2, it does not even have a value.
+ strText = m_pButton->GetLabel2();
+
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+ }
+ else if (controlType == "slider")
+ {
+ switch (m_pSetting->GetType())
+ {
+ case SettingType::Integer:
+ {
+ std::shared_ptr<const CSettingInt> settingInt =
+ std::static_pointer_cast<CSettingInt>(m_pSetting);
+ strText = CGUIControlSliderSetting::GetText(m_pSetting, settingInt->GetValue(),
+ settingInt->GetMinimum(), settingInt->GetStep(),
+ settingInt->GetMaximum(), m_localizer);
+ break;
+ }
+
+ case SettingType::Number:
+ {
+ std::shared_ptr<const CSettingNumber> settingNumber =
+ std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ strText = CGUIControlSliderSetting::GetText(
+ m_pSetting, settingNumber->GetValue(), settingNumber->GetMinimum(),
+ settingNumber->GetStep(), settingNumber->GetMaximum(), m_localizer);
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+
+ m_pButton->SetLabel2(strText);
+}
+
+bool CGUIControlButtonSetting::GetPath(const std::shared_ptr<CSettingPath>& pathSetting,
+ ILocalizer* localizer)
+{
+ if (pathSetting == NULL)
+ return false;
+
+ std::string path = pathSetting->GetValue();
+
+ VECSOURCES shares;
+ bool localSharesOnly = false;
+ const std::vector<std::string>& sources = pathSetting->GetSources();
+ for (const auto& source : sources)
+ {
+ if (StringUtils::EqualsNoCase(source, "local"))
+ localSharesOnly = true;
+ else
+ {
+ VECSOURCES* sources = CMediaSourceSettings::GetInstance().GetSources(source);
+ if (sources != NULL)
+ shares.insert(shares.end(), sources->begin(), sources->end());
+ }
+ }
+
+ CServiceBroker::GetMediaManager().GetLocalDrives(shares);
+ if (!localSharesOnly)
+ CServiceBroker::GetMediaManager().GetNetworkLocations(shares);
+
+ bool result = false;
+ std::shared_ptr<const CSettingControlButton> control =
+ std::static_pointer_cast<const CSettingControlButton>(pathSetting->GetControl());
+ const auto heading = ::Localize(control->GetHeading(), localizer);
+ if (control->GetFormat() == "file")
+ result = CGUIDialogFileBrowser::ShowAndGetFile(
+ shares, pathSetting->GetMasking(CServiceBroker::GetFileExtensionProvider()), heading, path,
+ control->UseImageThumbs(), control->UseFileDirectories());
+ else if (control->GetFormat() == "image")
+ {
+ /* Check setting contains own masking, to filter required image type.
+ * e.g. png only needed
+ * <constraints>
+ * <masking>*.png</masking>
+ * </constraints>
+ * <control type="button" format="image">
+ * ...
+ */
+ std::string ext = pathSetting->GetMasking(CServiceBroker::GetFileExtensionProvider());
+ if (ext.empty())
+ ext = CServiceBroker::GetFileExtensionProvider().GetPictureExtensions();
+ result = CGUIDialogFileBrowser::ShowAndGetFile(shares, ext, heading, path, true);
+ }
+ else
+ result =
+ CGUIDialogFileBrowser::ShowAndGetDirectory(shares, heading, path, pathSetting->Writable());
+
+ if (!result)
+ return false;
+
+ return pathSetting->SetValue(path);
+}
+
+void CGUIControlButtonSetting::OnSliderChange(void* data, CGUISliderControl* slider)
+{
+ if (slider == NULL)
+ return;
+
+ std::string strText;
+ switch (m_pSetting->GetType())
+ {
+ case SettingType::Integer:
+ {
+ std::shared_ptr<CSettingInt> settingInt = std::static_pointer_cast<CSettingInt>(m_pSetting);
+ if (settingInt->SetValue(slider->GetIntValue()))
+ strText = CGUIControlSliderSetting::GetText(m_pSetting, settingInt->GetValue(),
+ settingInt->GetMinimum(), settingInt->GetStep(),
+ settingInt->GetMaximum(), m_localizer);
+ break;
+ }
+
+ case SettingType::Number:
+ {
+ std::shared_ptr<CSettingNumber> settingNumber =
+ std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ if (settingNumber->SetValue(static_cast<double>(slider->GetFloatValue())))
+ strText = CGUIControlSliderSetting::GetText(
+ m_pSetting, settingNumber->GetValue(), settingNumber->GetMinimum(),
+ settingNumber->GetStep(), settingNumber->GetMaximum(), m_localizer);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (!strText.empty())
+ slider->SetTextValue(strText);
+}
+
+CGUIControlEditSetting::CGUIControlEditSetting(CGUIEditControl* pEdit,
+ int id,
+ const std::shared_ptr<CSetting>& pSetting,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, pSetting, localizer)
+{
+ std::shared_ptr<const CSettingControlEdit> control =
+ std::static_pointer_cast<const CSettingControlEdit>(pSetting->GetControl());
+ m_pEdit = pEdit;
+ if (m_pEdit == NULL)
+ return;
+
+ m_pEdit->SetID(id);
+ int heading = m_pSetting->GetLabel();
+ if (control->GetHeading() > 0)
+ heading = control->GetHeading();
+ if (heading < 0)
+ heading = 0;
+
+ CGUIEditControl::INPUT_TYPE inputType = CGUIEditControl::INPUT_TYPE_TEXT;
+ const std::string& controlFormat = control->GetFormat();
+ if (controlFormat == "string")
+ {
+ if (control->IsHidden())
+ inputType = CGUIEditControl::INPUT_TYPE_PASSWORD;
+ }
+ else if (controlFormat == "integer" || controlFormat == "number")
+ {
+ if (control->VerifyNewValue())
+ inputType = CGUIEditControl::INPUT_TYPE_PASSWORD_NUMBER_VERIFY_NEW;
+ else
+ inputType = CGUIEditControl::INPUT_TYPE_NUMBER;
+ }
+ else if (controlFormat == "ip")
+ inputType = CGUIEditControl::INPUT_TYPE_IPADDRESS;
+ else if (controlFormat == "md5")
+ inputType = CGUIEditControl::INPUT_TYPE_PASSWORD_MD5;
+
+ m_pEdit->SetInputType(inputType, heading);
+
+ // this will automatically trigger validation so it must be executed after
+ // having set the value of the control based on the value of the setting
+ m_pEdit->SetInputValidation(InputValidation, this);
+}
+
+CGUIControlEditSetting::~CGUIControlEditSetting() = default;
+
+bool CGUIControlEditSetting::OnClick()
+{
+ if (m_pEdit == NULL)
+ return false;
+
+ // update our string
+ if (m_pSetting->GetControl()->GetFormat() == "urlencoded")
+ {
+ std::shared_ptr<CSettingUrlEncodedString> urlEncodedSetting =
+ std::static_pointer_cast<CSettingUrlEncodedString>(m_pSetting);
+ SetValid(urlEncodedSetting->SetDecodedValue(m_pEdit->GetLabel2()));
+ }
+ else
+ SetValid(m_pSetting->FromString(m_pEdit->GetLabel2()));
+
+ return IsValid();
+}
+
+void CGUIControlEditSetting::Update(bool fromControl, bool updateDisplayOnly)
+{
+ if (fromControl || m_pEdit == NULL)
+ return;
+
+ CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
+
+ std::shared_ptr<const CSettingControlEdit> control =
+ std::static_pointer_cast<const CSettingControlEdit>(m_pSetting->GetControl());
+
+ if (control->GetFormat() == "urlencoded")
+ {
+ std::shared_ptr<CSettingUrlEncodedString> urlEncodedSetting =
+ std::static_pointer_cast<CSettingUrlEncodedString>(m_pSetting);
+ m_pEdit->SetLabel2(urlEncodedSetting->GetDecodedValue());
+ }
+ else
+ m_pEdit->SetLabel2(m_pSetting->ToString());
+}
+
+bool CGUIControlEditSetting::InputValidation(const std::string& input, void* data)
+{
+ if (data == NULL)
+ return true;
+
+ CGUIControlEditSetting* editControl = reinterpret_cast<CGUIControlEditSetting*>(data);
+ if (editControl->GetSetting() == NULL)
+ return true;
+
+ editControl->SetValid(editControl->GetSetting()->CheckValidity(input));
+ return editControl->IsValid();
+}
+
+CGUIControlSliderSetting::CGUIControlSliderSetting(CGUISettingsSliderControl* pSlider,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
+{
+ m_pSlider = pSlider;
+ if (m_pSlider == NULL)
+ return;
+
+ m_pSlider->SetID(id);
+
+ switch (m_pSetting->GetType())
+ {
+ case SettingType::Integer:
+ {
+ std::shared_ptr<CSettingInt> settingInt = std::static_pointer_cast<CSettingInt>(m_pSetting);
+ if (m_pSetting->GetControl()->GetFormat() == "percentage")
+ m_pSlider->SetType(SLIDER_CONTROL_TYPE_PERCENTAGE);
+ else
+ {
+ m_pSlider->SetType(SLIDER_CONTROL_TYPE_INT);
+ m_pSlider->SetRange(settingInt->GetMinimum(), settingInt->GetMaximum());
+ }
+ m_pSlider->SetIntInterval(settingInt->GetStep());
+ break;
+ }
+
+ case SettingType::Number:
+ {
+ std::shared_ptr<CSettingNumber> settingNumber =
+ std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ m_pSlider->SetType(SLIDER_CONTROL_TYPE_FLOAT);
+ m_pSlider->SetFloatRange(static_cast<float>(settingNumber->GetMinimum()),
+ static_cast<float>(settingNumber->GetMaximum()));
+ m_pSlider->SetFloatInterval(static_cast<float>(settingNumber->GetStep()));
+ break;
+ }
+
+ default:
+ break;
+ }
+}
+
+CGUIControlSliderSetting::~CGUIControlSliderSetting() = default;
+
+bool CGUIControlSliderSetting::OnClick()
+{
+ if (m_pSlider == NULL)
+ return false;
+
+ switch (m_pSetting->GetType())
+ {
+ case SettingType::Integer:
+ SetValid(
+ std::static_pointer_cast<CSettingInt>(m_pSetting)->SetValue(m_pSlider->GetIntValue()));
+ break;
+
+ case SettingType::Number:
+ SetValid(std::static_pointer_cast<CSettingNumber>(m_pSetting)
+ ->SetValue(static_cast<double>(m_pSlider->GetFloatValue())));
+ break;
+
+ default:
+ return false;
+ }
+
+ return IsValid();
+}
+
+void CGUIControlSliderSetting::Update(bool fromControl, bool updateDisplayOnly)
+{
+ if (m_pSlider == NULL)
+ return;
+
+ CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
+
+ std::string strText;
+ switch (m_pSetting->GetType())
+ {
+ case SettingType::Integer:
+ {
+ std::shared_ptr<const CSettingInt> settingInt =
+ std::static_pointer_cast<CSettingInt>(m_pSetting);
+ int value;
+ if (fromControl)
+ value = m_pSlider->GetIntValue();
+ else
+ {
+ value = std::static_pointer_cast<CSettingInt>(m_pSetting)->GetValue();
+ m_pSlider->SetIntValue(value);
+ }
+
+ strText = CGUIControlSliderSetting::GetText(m_pSetting, value, settingInt->GetMinimum(),
+ settingInt->GetStep(), settingInt->GetMaximum(),
+ m_localizer);
+ break;
+ }
+
+ case SettingType::Number:
+ {
+ std::shared_ptr<const CSettingNumber> settingNumber =
+ std::static_pointer_cast<CSettingNumber>(m_pSetting);
+ double value;
+ if (fromControl)
+ value = static_cast<double>(m_pSlider->GetFloatValue());
+ else
+ {
+ value = std::static_pointer_cast<CSettingNumber>(m_pSetting)->GetValue();
+ m_pSlider->SetFloatValue((float)value);
+ }
+
+ strText = CGUIControlSliderSetting::GetText(m_pSetting, value, settingNumber->GetMinimum(),
+ settingNumber->GetStep(),
+ settingNumber->GetMaximum(), m_localizer);
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (!strText.empty())
+ m_pSlider->SetTextValue(strText);
+}
+
+std::string CGUIControlSliderSetting::GetText(const std::shared_ptr<CSetting>& setting,
+ const CVariant& value,
+ const CVariant& minimum,
+ const CVariant& step,
+ const CVariant& maximum,
+ ILocalizer* localizer)
+{
+ if (setting == NULL || !(value.isInteger() || value.isDouble()))
+ return "";
+
+ const auto control = std::static_pointer_cast<const CSettingControlSlider>(setting->GetControl());
+ if (control == NULL)
+ return "";
+
+ SettingControlSliderFormatter formatter = control->GetFormatter();
+ if (formatter != NULL)
+ return formatter(control, value, minimum, step, maximum);
+
+ std::string formatString = control->GetFormatString();
+ if (control->GetFormatLabel() > -1)
+ formatString = ::Localize(control->GetFormatLabel(), localizer);
+
+ std::string formattedString;
+ if (FormatText(formatString, value, setting->GetId(), formattedString))
+ return formattedString;
+
+ // fall back to default formatting
+ formatString = control->GetDefaultFormatString();
+ if (FormatText(formatString, value, setting->GetId(), formattedString))
+ return formattedString;
+
+ return "";
+}
+
+bool CGUIControlSliderSetting::FormatText(const std::string& formatString,
+ const CVariant& value,
+ const std::string& settingId,
+ std::string& formattedText)
+{
+ try
+ {
+ if (value.isDouble())
+ formattedText = StringUtils::Format(formatString, value.asDouble());
+ else
+ formattedText = StringUtils::Format(formatString, static_cast<int>(value.asInteger()));
+ }
+ catch (const std::runtime_error& err)
+ {
+ CLog::Log(LOGERROR, "Invalid formatting with string \"{}\" for setting \"{}\": {}",
+ formatString, settingId, err.what());
+ return false;
+ }
+
+ return true;
+}
+
+CGUIControlRangeSetting::CGUIControlRangeSetting(CGUISettingsSliderControl* pSlider,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
+{
+ m_pSlider = pSlider;
+ if (m_pSlider == NULL)
+ return;
+
+ m_pSlider->SetID(id);
+ m_pSlider->SetRangeSelection(true);
+
+ if (m_pSetting->GetType() == SettingType::List)
+ {
+ std::shared_ptr<CSettingList> settingList = std::static_pointer_cast<CSettingList>(m_pSetting);
+ SettingConstPtr listDefintion = settingList->GetDefinition();
+ switch (listDefintion->GetType())
+ {
+ case SettingType::Integer:
+ {
+ std::shared_ptr<const CSettingInt> listDefintionInt =
+ std::static_pointer_cast<const CSettingInt>(listDefintion);
+ if (m_pSetting->GetControl()->GetFormat() == "percentage")
+ m_pSlider->SetType(SLIDER_CONTROL_TYPE_PERCENTAGE);
+ else
+ {
+ m_pSlider->SetType(SLIDER_CONTROL_TYPE_INT);
+ m_pSlider->SetRange(listDefintionInt->GetMinimum(), listDefintionInt->GetMaximum());
+ }
+ m_pSlider->SetIntInterval(listDefintionInt->GetStep());
+ break;
+ }
+
+ case SettingType::Number:
+ {
+ std::shared_ptr<const CSettingNumber> listDefinitionNumber =
+ std::static_pointer_cast<const CSettingNumber>(listDefintion);
+ m_pSlider->SetType(SLIDER_CONTROL_TYPE_FLOAT);
+ m_pSlider->SetFloatRange(static_cast<float>(listDefinitionNumber->GetMinimum()),
+ static_cast<float>(listDefinitionNumber->GetMaximum()));
+ m_pSlider->SetFloatInterval(static_cast<float>(listDefinitionNumber->GetStep()));
+ break;
+ }
+
+ default:
+ break;
+ }
+ }
+}
+
+CGUIControlRangeSetting::~CGUIControlRangeSetting() = default;
+
+bool CGUIControlRangeSetting::OnClick()
+{
+ if (m_pSlider == NULL || m_pSetting->GetType() != SettingType::List)
+ return false;
+
+ std::shared_ptr<CSettingList> settingList = std::static_pointer_cast<CSettingList>(m_pSetting);
+ const SettingList& settingListValues = settingList->GetValue();
+ if (settingListValues.size() != 2)
+ return false;
+
+ std::vector<CVariant> values;
+ SettingConstPtr listDefintion = settingList->GetDefinition();
+ switch (listDefintion->GetType())
+ {
+ case SettingType::Integer:
+ values.emplace_back(m_pSlider->GetIntValue(CGUISliderControl::RangeSelectorLower));
+ values.emplace_back(m_pSlider->GetIntValue(CGUISliderControl::RangeSelectorUpper));
+ break;
+
+ case SettingType::Number:
+ values.emplace_back(m_pSlider->GetFloatValue(CGUISliderControl::RangeSelectorLower));
+ values.emplace_back(m_pSlider->GetFloatValue(CGUISliderControl::RangeSelectorUpper));
+ break;
+
+ default:
+ return false;
+ }
+
+ if (values.size() != 2)
+ return false;
+
+ SetValid(CSettingUtils::SetList(settingList, values));
+ return IsValid();
+}
+
+void CGUIControlRangeSetting::Update(bool fromControl, bool updateDisplayOnly)
+{
+ if (m_pSlider == NULL || m_pSetting->GetType() != SettingType::List)
+ return;
+
+ CGUIControlBaseSetting::Update(fromControl, updateDisplayOnly);
+
+ std::shared_ptr<CSettingList> settingList = std::static_pointer_cast<CSettingList>(m_pSetting);
+ const SettingList& settingListValues = settingList->GetValue();
+ if (settingListValues.size() != 2)
+ return;
+
+ SettingConstPtr listDefintion = settingList->GetDefinition();
+ std::shared_ptr<const CSettingControlRange> controlRange =
+ std::static_pointer_cast<const CSettingControlRange>(m_pSetting->GetControl());
+ const std::string& controlFormat = controlRange->GetFormat();
+
+ std::string strText;
+ std::string strTextLower, strTextUpper;
+ std::string formatString =
+ Localize(controlRange->GetFormatLabel() > -1 ? controlRange->GetFormatLabel() : 21469);
+ std::string valueFormat = controlRange->GetValueFormat();
+ if (controlRange->GetValueFormatLabel() > -1)
+ valueFormat = Localize(controlRange->GetValueFormatLabel());
+
+ switch (listDefintion->GetType())
+ {
+ case SettingType::Integer:
+ {
+ int valueLower, valueUpper;
+ if (fromControl)
+ {
+ valueLower = m_pSlider->GetIntValue(CGUISliderControl::RangeSelectorLower);
+ valueUpper = m_pSlider->GetIntValue(CGUISliderControl::RangeSelectorUpper);
+ }
+ else
+ {
+ valueLower = std::static_pointer_cast<CSettingInt>(settingListValues[0])->GetValue();
+ valueUpper = std::static_pointer_cast<CSettingInt>(settingListValues[1])->GetValue();
+ m_pSlider->SetIntValue(valueLower, CGUISliderControl::RangeSelectorLower);
+ m_pSlider->SetIntValue(valueUpper, CGUISliderControl::RangeSelectorUpper);
+ }
+
+ if (controlFormat == "date" || controlFormat == "time")
+ {
+ CDateTime dateLower((time_t)valueLower);
+ CDateTime dateUpper((time_t)valueUpper);
+
+ if (controlFormat == "date")
+ {
+ if (valueFormat.empty())
+ {
+ strTextLower = dateLower.GetAsLocalizedDate();
+ strTextUpper = dateUpper.GetAsLocalizedDate();
+ }
+ else
+ {
+ strTextLower = dateLower.GetAsLocalizedDate(valueFormat);
+ strTextUpper = dateUpper.GetAsLocalizedDate(valueFormat);
+ }
+ }
+ else
+ {
+ if (valueFormat.empty())
+ valueFormat = "mm:ss";
+
+ strTextLower = dateLower.GetAsLocalizedTime(valueFormat);
+ strTextUpper = dateUpper.GetAsLocalizedTime(valueFormat);
+ }
+ }
+ else
+ {
+ strTextLower = StringUtils::Format(valueFormat, valueLower);
+ strTextUpper = StringUtils::Format(valueFormat, valueUpper);
+ }
+
+ if (valueLower != valueUpper)
+ strText = StringUtils::Format(formatString, strTextLower, strTextUpper);
+ else
+ strText = strTextLower;
+ break;
+ }
+
+ case SettingType::Number:
+ {
+ double valueLower, valueUpper;
+ if (fromControl)
+ {
+ valueLower =
+ static_cast<double>(m_pSlider->GetFloatValue(CGUISliderControl::RangeSelectorLower));
+ valueUpper =
+ static_cast<double>(m_pSlider->GetFloatValue(CGUISliderControl::RangeSelectorUpper));
+ }
+ else
+ {
+ valueLower = std::static_pointer_cast<CSettingNumber>(settingListValues[0])->GetValue();
+ valueUpper = std::static_pointer_cast<CSettingNumber>(settingListValues[1])->GetValue();
+ m_pSlider->SetFloatValue((float)valueLower, CGUISliderControl::RangeSelectorLower);
+ m_pSlider->SetFloatValue((float)valueUpper, CGUISliderControl::RangeSelectorUpper);
+ }
+
+ strTextLower = StringUtils::Format(valueFormat, valueLower);
+ if (valueLower != valueUpper)
+ {
+ strTextUpper = StringUtils::Format(valueFormat, valueUpper);
+ strText = StringUtils::Format(formatString, strTextLower, strTextUpper);
+ }
+ else
+ strText = strTextLower;
+ break;
+ }
+
+ default:
+ strText.clear();
+ break;
+ }
+
+ if (!strText.empty())
+ m_pSlider->SetTextValue(strText);
+}
+
+CGUIControlSeparatorSetting::CGUIControlSeparatorSetting(CGUIImage* pImage,
+ int id,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, NULL, localizer)
+{
+ m_pImage = pImage;
+ if (m_pImage == NULL)
+ return;
+
+ m_pImage->SetID(id);
+}
+
+CGUIControlSeparatorSetting::~CGUIControlSeparatorSetting() = default;
+
+CGUIControlGroupTitleSetting::CGUIControlGroupTitleSetting(CGUILabelControl* pLabel,
+ int id,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, NULL, localizer)
+{
+ m_pLabel = pLabel;
+ if (m_pLabel == NULL)
+ return;
+
+ m_pLabel->SetID(id);
+}
+
+CGUIControlGroupTitleSetting::~CGUIControlGroupTitleSetting() = default;
+
+CGUIControlLabelSetting::CGUIControlLabelSetting(CGUIButtonControl* pButton,
+ int id,
+ std::shared_ptr<CSetting> pSetting,
+ ILocalizer* localizer)
+ : CGUIControlBaseSetting(id, std::move(pSetting), localizer)
+{
+ m_pButton = pButton;
+ if (m_pButton == NULL)
+ return;
+
+ m_pButton->SetID(id);
+ UpdateFromSetting();
+}